From 9d7e5dc1aef4d84c204c13088507d63ae5005507 Mon Sep 17 00:00:00 2001 From: liaochuntao Date: Tue, 23 Jan 2024 16:45:36 +0800 Subject: [PATCH] feat:support inject java agent to user pod (#163) --- .../app/polaris-controller-manager.go | 17 +- .../controller-configmap-javaagent.yaml | 107 ++++ .../controller-configmap-sidecar.yaml | 45 ++ .../kubernetes_v1.21/kubernetes/injector.yaml | 43 ++ .../kubernetes/javaagent-configmap.yaml | 107 ++++ .../controller-configmap-javaagent.yaml | 107 ++++ .../controller-configmap-sidecar.yaml | 45 ++ .../kubernetes_v1.22/kubernetes/injector.yaml | 44 ++ .../kubernetes/javaagent-configmap.yaml | 107 ++++ deploy/variables.txt | 3 +- .../pkg/kube/inject/apply/base/patch.go | 209 ++++++++ .../pkg/kube/inject/apply/javaagent/patch.go | 210 ++++++++ .../pkg/kube/inject/apply/mesh/patch.go | 212 ++++++++ .../inject/{ => apply/mesh}/sidecar_env.go | 2 +- pkg/inject/pkg/kube/inject/inject.go | 27 +- pkg/inject/pkg/kube/inject/pod_patch.go | 94 ++++ pkg/inject/pkg/kube/inject/webhook.go | 500 ++++++++---------- pkg/util/types.go | 16 +- .../Dockerfile | 2 +- 19 files changed, 1603 insertions(+), 294 deletions(-) create mode 100644 deploy/kubernetes_v1.21/helm/templates/controller-configmap-javaagent.yaml create mode 100644 deploy/kubernetes_v1.21/kubernetes/javaagent-configmap.yaml create mode 100644 deploy/kubernetes_v1.22/helm/templates/controller-configmap-javaagent.yaml create mode 100644 deploy/kubernetes_v1.22/kubernetes/javaagent-configmap.yaml create mode 100644 pkg/inject/pkg/kube/inject/apply/base/patch.go create mode 100644 pkg/inject/pkg/kube/inject/apply/javaagent/patch.go create mode 100644 pkg/inject/pkg/kube/inject/apply/mesh/patch.go rename pkg/inject/pkg/kube/inject/{ => apply/mesh}/sidecar_env.go (99%) create mode 100644 pkg/inject/pkg/kube/inject/pod_patch.go diff --git a/cmd/polaris-controller/app/polaris-controller-manager.go b/cmd/polaris-controller/app/polaris-controller-manager.go index 7e2d0432..55b2002a 100644 --- a/cmd/polaris-controller/app/polaris-controller-manager.go +++ b/cmd/polaris-controller/app/polaris-controller-manager.go @@ -53,6 +53,9 @@ import ( "github.com/polarismesh/polaris-controller/pkg/util" utilflag "github.com/polarismesh/polaris-controller/pkg/util/flag" "github.com/polarismesh/polaris-controller/pkg/version" + + _ "github.com/polarismesh/polaris-controller/pkg/inject/pkg/kube/inject/apply/javaagent" + _ "github.com/polarismesh/polaris-controller/pkg/inject/pkg/kube/inject/apply/mesh" ) const ( @@ -62,12 +65,13 @@ const ( DefaultLockObjectName = "polaris-controller" DefaultLeaderElectionName = "polaris-controller" - MeshConfigFile = "/etc/polaris-inject/inject/mesh-config" - DnsConfigFile = "/etc/polaris-inject/inject/dns-config" - ValuesFile = "/etc/polaris-inject/inject/values" - MeshFile = "/etc/polaris-inject/config/mesh" - CertFile = "/etc/polaris-inject/certs/cert.pem" - KeyFile = "/etc/polaris-inject/certs/key.pem" + MeshConfigFile = "/etc/polaris-inject/inject/mesh-config" + DnsConfigFile = "/etc/polaris-inject/inject/dns-config" + JavaAgentConfigFile = "/etc/polaris-inject/inject/java-agent-config" + ValuesFile = "/etc/polaris-inject/inject/values" + MeshFile = "/etc/polaris-inject/config/mesh" + CertFile = "/etc/polaris-inject/certs/cert.pem" + KeyFile = "/etc/polaris-inject/certs/key.pem" ) var ( @@ -248,6 +252,7 @@ func initPolarisSidecarInjector(c *options.CompletedConfig) error { DefaultSidecarMode: util.ParseSidecarMode(c.ComponentConfig.PolarisController.SidecarMode), MeshConfigFile: MeshConfigFile, DnsConfigFile: DnsConfigFile, + JavaAgentConfigFile: JavaAgentConfigFile, ValuesFile: ValuesFile, MeshFile: MeshFile, CertFile: CertFile, diff --git a/deploy/kubernetes_v1.21/helm/templates/controller-configmap-javaagent.yaml b/deploy/kubernetes_v1.21/helm/templates/controller-configmap-javaagent.yaml new file mode 100644 index 00000000..558adf22 --- /dev/null +++ b/deploy/kubernetes_v1.21/helm/templates/controller-configmap-javaagent.yaml @@ -0,0 +1,107 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: plugin-default.properties + namespace: polaris-system + labels: + app: sidecar-injector +data: + springcloud2020-default-properties: |+ + # 应用名称 + spring.application.name={{ .MicroserviceName }} + # 配置北极星服务端地址 + spring.cloud.polaris.address=grpc\://{{ .PolarisServerIP }}\:{{ .PolarisDiscoverPort }} + spring.cloud.polaris.enabled=true + + # 启用 Java Agent 的 Spring Cloud Tencent 注册发现能力 + spring.cloud.discovery.enabled=true + # 配置服务注册发现的命名空间信息 + spring.cloud.polaris.discovery.namespace=default + # 启用从北极星 + spring.cloud.polaris.discovery.enabled=true + spring.cloud.polaris.discovery.register=true + + ## 是否启用北极星的 LoadBalancer + spring.cloud.polaris.loadbalancer.enabled=true + #spring.cloud.polaris.loadbalancer.discoveryType=POLARIS + #spring.cloud.polaris.loadbalancer.strategy=random + + ## 是否启用北极星服务路由能力 + spring.cloud.polaris.router.enabled=false + # 是否启用北极星的自定义路由能力 + spring.cloud.polaris.router.rule-router.enabled=false + # 是否启用北极星的元数据路由能力 + spring.cloud.polaris.router.metadata-router.enabled=false + # 是否启用北极星的就近路由能力 + spring.cloud.polaris.router.nearby-router.enabled=false + + ## 是否启用北极星的服务限流能力 + spring.cloud.polaris.ratelimit.enabled=false + # 设置触发限流时的提示信息 + # spring.cloud.polaris.ratelimit.rejectRequestTips="" + # 设置触发限流时的响应码 + spring.cloud.polaris.ratelimit.rejectHttpCode=429 + # 设置限流匀速排队最大排队时间 + spring.cloud.polaris.ratelimit.maxQueuingTime=1000 + + ## RPC 调用增强 + spring.cloud.tencent.rpc-enhancement.enabled=false + # 开启 RPC 调用结果上报 + spring.cloud.tencent.rpc-enhancement.reporter.enabled=false + + # 配置北极星监控指标上报 + spring.cloud.polaris.stat.enabled=false + # 指标上报监听端口 + spring.cloud.polaris.stat.port=0 + # 指标上报暴露的 http path + spring.cloud.polaris.stat.path=/metrics + + springcloud2021-default-properties: |+ + # 应用名称 + spring.application.name={{ .MicroserviceName }} + # 配置北极星服务端地址 + spring.cloud.polaris.address=grpc\://{{ .PolarisServerIP }}\:{{ .PolarisDiscoverPort }} + spring.cloud.polaris.enabled=true + + # 启用 Java Agent 的 Spring Cloud Tencent 注册发现能力 + spring.cloud.discovery.enabled=true + # 配置服务注册发现的命名空间信息 + spring.cloud.polaris.discovery.namespace=default + # 启用从北极星 + spring.cloud.polaris.discovery.enabled=true + spring.cloud.polaris.discovery.register=true + + ## 是否启用北极星的 LoadBalancer + spring.cloud.polaris.loadbalancer.enabled=true + #spring.cloud.polaris.loadbalancer.discoveryType=POLARIS + #spring.cloud.polaris.loadbalancer.strategy=random + + ## 是否启用北极星服务路由能力 + spring.cloud.polaris.router.enabled=false + # 是否启用北极星的自定义路由能力 + spring.cloud.polaris.router.rule-router.enabled=false + # 是否启用北极星的元数据路由能力 + spring.cloud.polaris.router.metadata-router.enabled=false + # 是否启用北极星的就近路由能力 + spring.cloud.polaris.router.nearby-router.enabled=false + + ## 是否启用北极星的服务限流能力 + spring.cloud.polaris.ratelimit.enabled=false + # 设置触发限流时的提示信息 + # spring.cloud.polaris.ratelimit.rejectRequestTips="" + # 设置触发限流时的响应码 + spring.cloud.polaris.ratelimit.rejectHttpCode=429 + # 设置限流匀速排队最大排队时间 + spring.cloud.polaris.ratelimit.maxQueuingTime=1000 + + ## RPC 调用增强 + spring.cloud.tencent.rpc-enhancement.enabled=false + # 开启 RPC 调用结果上报 + spring.cloud.tencent.rpc-enhancement.reporter.enabled=false + + # 配置北极星监控指标上报 + spring.cloud.polaris.stat.enabled=false + # 指标上报监听端口 + spring.cloud.polaris.stat.port=0 + # 指标上报暴露的 http path + spring.cloud.polaris.stat.path=/metrics \ No newline at end of file diff --git a/deploy/kubernetes_v1.21/helm/templates/controller-configmap-sidecar.yaml b/deploy/kubernetes_v1.21/helm/templates/controller-configmap-sidecar.yaml index 814d94e7..f49281d5 100644 --- a/deploy/kubernetes_v1.21/helm/templates/controller-configmap-sidecar.yaml +++ b/deploy/kubernetes_v1.21/helm/templates/controller-configmap-sidecar.yaml @@ -87,6 +87,51 @@ data: - name: polaris-log emptyDir: {} + java-agent-config: |- + policy: enabled + alwaysInjectSelector: + [] + + neverInjectSelector: + [] + + template: | + initContainers: + - name: polaris-javaagent-init + image: polarismesh/polaris-javaagent-init:#JAVA_AGENT_INIT# + imagePullPolicy: Always + env: + - name: JAVA_AGENT_DIR + value: /app/lib/.polaris/java_agent + resources: + limits: + cpu: 100m + memory: 50Mi + requests: + cpu: 10m + memory: 10Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + add: + - NET_ADMIN + - NET_RAW + drop: + - ALL + privileged: false + readOnlyRootFilesystem: false + runAsGroup: 0 + runAsNonRoot: false + runAsUser: 0 + volumeMounts: + - mountPath: /app/lib/.polaris/java_agent + name: java-agent-dir + volumes: + - name: java-agent-dir + emptyDir: {} + + + mesh-config: |- policy: enabled alwaysInjectSelector: diff --git a/deploy/kubernetes_v1.21/kubernetes/injector.yaml b/deploy/kubernetes_v1.21/kubernetes/injector.yaml index 2eb4837b..a4ec5277 100644 --- a/deploy/kubernetes_v1.21/kubernetes/injector.yaml +++ b/deploy/kubernetes_v1.21/kubernetes/injector.yaml @@ -107,6 +107,49 @@ data: - name: polaris-log emptyDir: {} + java-agent-config: |- + policy: enabled + alwaysInjectSelector: + [] + + neverInjectSelector: + [] + + template: | + initContainers: + - name: polaris-javaagent-init + image: polarismesh/polaris-javaagent-init:#JAVA_AGENT_INIT# + imagePullPolicy: Always + env: + - name: JAVA_AGENT_DIR + value: /app/lib/.polaris/java_agent + resources: + limits: + cpu: 100m + memory: 50Mi + requests: + cpu: 10m + memory: 10Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + add: + - NET_ADMIN + - NET_RAW + drop: + - ALL + privileged: false + readOnlyRootFilesystem: false + runAsGroup: 0 + runAsNonRoot: false + runAsUser: 0 + volumeMounts: + - mountPath: /app/lib/.polaris/java_agent + name: java-agent-dir + volumes: + - name: java-agent-dir + emptyDir: {} + mesh-config: |+ policy: enabled alwaysInjectSelector: diff --git a/deploy/kubernetes_v1.21/kubernetes/javaagent-configmap.yaml b/deploy/kubernetes_v1.21/kubernetes/javaagent-configmap.yaml new file mode 100644 index 00000000..558adf22 --- /dev/null +++ b/deploy/kubernetes_v1.21/kubernetes/javaagent-configmap.yaml @@ -0,0 +1,107 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: plugin-default.properties + namespace: polaris-system + labels: + app: sidecar-injector +data: + springcloud2020-default-properties: |+ + # 应用名称 + spring.application.name={{ .MicroserviceName }} + # 配置北极星服务端地址 + spring.cloud.polaris.address=grpc\://{{ .PolarisServerIP }}\:{{ .PolarisDiscoverPort }} + spring.cloud.polaris.enabled=true + + # 启用 Java Agent 的 Spring Cloud Tencent 注册发现能力 + spring.cloud.discovery.enabled=true + # 配置服务注册发现的命名空间信息 + spring.cloud.polaris.discovery.namespace=default + # 启用从北极星 + spring.cloud.polaris.discovery.enabled=true + spring.cloud.polaris.discovery.register=true + + ## 是否启用北极星的 LoadBalancer + spring.cloud.polaris.loadbalancer.enabled=true + #spring.cloud.polaris.loadbalancer.discoveryType=POLARIS + #spring.cloud.polaris.loadbalancer.strategy=random + + ## 是否启用北极星服务路由能力 + spring.cloud.polaris.router.enabled=false + # 是否启用北极星的自定义路由能力 + spring.cloud.polaris.router.rule-router.enabled=false + # 是否启用北极星的元数据路由能力 + spring.cloud.polaris.router.metadata-router.enabled=false + # 是否启用北极星的就近路由能力 + spring.cloud.polaris.router.nearby-router.enabled=false + + ## 是否启用北极星的服务限流能力 + spring.cloud.polaris.ratelimit.enabled=false + # 设置触发限流时的提示信息 + # spring.cloud.polaris.ratelimit.rejectRequestTips="" + # 设置触发限流时的响应码 + spring.cloud.polaris.ratelimit.rejectHttpCode=429 + # 设置限流匀速排队最大排队时间 + spring.cloud.polaris.ratelimit.maxQueuingTime=1000 + + ## RPC 调用增强 + spring.cloud.tencent.rpc-enhancement.enabled=false + # 开启 RPC 调用结果上报 + spring.cloud.tencent.rpc-enhancement.reporter.enabled=false + + # 配置北极星监控指标上报 + spring.cloud.polaris.stat.enabled=false + # 指标上报监听端口 + spring.cloud.polaris.stat.port=0 + # 指标上报暴露的 http path + spring.cloud.polaris.stat.path=/metrics + + springcloud2021-default-properties: |+ + # 应用名称 + spring.application.name={{ .MicroserviceName }} + # 配置北极星服务端地址 + spring.cloud.polaris.address=grpc\://{{ .PolarisServerIP }}\:{{ .PolarisDiscoverPort }} + spring.cloud.polaris.enabled=true + + # 启用 Java Agent 的 Spring Cloud Tencent 注册发现能力 + spring.cloud.discovery.enabled=true + # 配置服务注册发现的命名空间信息 + spring.cloud.polaris.discovery.namespace=default + # 启用从北极星 + spring.cloud.polaris.discovery.enabled=true + spring.cloud.polaris.discovery.register=true + + ## 是否启用北极星的 LoadBalancer + spring.cloud.polaris.loadbalancer.enabled=true + #spring.cloud.polaris.loadbalancer.discoveryType=POLARIS + #spring.cloud.polaris.loadbalancer.strategy=random + + ## 是否启用北极星服务路由能力 + spring.cloud.polaris.router.enabled=false + # 是否启用北极星的自定义路由能力 + spring.cloud.polaris.router.rule-router.enabled=false + # 是否启用北极星的元数据路由能力 + spring.cloud.polaris.router.metadata-router.enabled=false + # 是否启用北极星的就近路由能力 + spring.cloud.polaris.router.nearby-router.enabled=false + + ## 是否启用北极星的服务限流能力 + spring.cloud.polaris.ratelimit.enabled=false + # 设置触发限流时的提示信息 + # spring.cloud.polaris.ratelimit.rejectRequestTips="" + # 设置触发限流时的响应码 + spring.cloud.polaris.ratelimit.rejectHttpCode=429 + # 设置限流匀速排队最大排队时间 + spring.cloud.polaris.ratelimit.maxQueuingTime=1000 + + ## RPC 调用增强 + spring.cloud.tencent.rpc-enhancement.enabled=false + # 开启 RPC 调用结果上报 + spring.cloud.tencent.rpc-enhancement.reporter.enabled=false + + # 配置北极星监控指标上报 + spring.cloud.polaris.stat.enabled=false + # 指标上报监听端口 + spring.cloud.polaris.stat.port=0 + # 指标上报暴露的 http path + spring.cloud.polaris.stat.path=/metrics \ No newline at end of file diff --git a/deploy/kubernetes_v1.22/helm/templates/controller-configmap-javaagent.yaml b/deploy/kubernetes_v1.22/helm/templates/controller-configmap-javaagent.yaml new file mode 100644 index 00000000..558adf22 --- /dev/null +++ b/deploy/kubernetes_v1.22/helm/templates/controller-configmap-javaagent.yaml @@ -0,0 +1,107 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: plugin-default.properties + namespace: polaris-system + labels: + app: sidecar-injector +data: + springcloud2020-default-properties: |+ + # 应用名称 + spring.application.name={{ .MicroserviceName }} + # 配置北极星服务端地址 + spring.cloud.polaris.address=grpc\://{{ .PolarisServerIP }}\:{{ .PolarisDiscoverPort }} + spring.cloud.polaris.enabled=true + + # 启用 Java Agent 的 Spring Cloud Tencent 注册发现能力 + spring.cloud.discovery.enabled=true + # 配置服务注册发现的命名空间信息 + spring.cloud.polaris.discovery.namespace=default + # 启用从北极星 + spring.cloud.polaris.discovery.enabled=true + spring.cloud.polaris.discovery.register=true + + ## 是否启用北极星的 LoadBalancer + spring.cloud.polaris.loadbalancer.enabled=true + #spring.cloud.polaris.loadbalancer.discoveryType=POLARIS + #spring.cloud.polaris.loadbalancer.strategy=random + + ## 是否启用北极星服务路由能力 + spring.cloud.polaris.router.enabled=false + # 是否启用北极星的自定义路由能力 + spring.cloud.polaris.router.rule-router.enabled=false + # 是否启用北极星的元数据路由能力 + spring.cloud.polaris.router.metadata-router.enabled=false + # 是否启用北极星的就近路由能力 + spring.cloud.polaris.router.nearby-router.enabled=false + + ## 是否启用北极星的服务限流能力 + spring.cloud.polaris.ratelimit.enabled=false + # 设置触发限流时的提示信息 + # spring.cloud.polaris.ratelimit.rejectRequestTips="" + # 设置触发限流时的响应码 + spring.cloud.polaris.ratelimit.rejectHttpCode=429 + # 设置限流匀速排队最大排队时间 + spring.cloud.polaris.ratelimit.maxQueuingTime=1000 + + ## RPC 调用增强 + spring.cloud.tencent.rpc-enhancement.enabled=false + # 开启 RPC 调用结果上报 + spring.cloud.tencent.rpc-enhancement.reporter.enabled=false + + # 配置北极星监控指标上报 + spring.cloud.polaris.stat.enabled=false + # 指标上报监听端口 + spring.cloud.polaris.stat.port=0 + # 指标上报暴露的 http path + spring.cloud.polaris.stat.path=/metrics + + springcloud2021-default-properties: |+ + # 应用名称 + spring.application.name={{ .MicroserviceName }} + # 配置北极星服务端地址 + spring.cloud.polaris.address=grpc\://{{ .PolarisServerIP }}\:{{ .PolarisDiscoverPort }} + spring.cloud.polaris.enabled=true + + # 启用 Java Agent 的 Spring Cloud Tencent 注册发现能力 + spring.cloud.discovery.enabled=true + # 配置服务注册发现的命名空间信息 + spring.cloud.polaris.discovery.namespace=default + # 启用从北极星 + spring.cloud.polaris.discovery.enabled=true + spring.cloud.polaris.discovery.register=true + + ## 是否启用北极星的 LoadBalancer + spring.cloud.polaris.loadbalancer.enabled=true + #spring.cloud.polaris.loadbalancer.discoveryType=POLARIS + #spring.cloud.polaris.loadbalancer.strategy=random + + ## 是否启用北极星服务路由能力 + spring.cloud.polaris.router.enabled=false + # 是否启用北极星的自定义路由能力 + spring.cloud.polaris.router.rule-router.enabled=false + # 是否启用北极星的元数据路由能力 + spring.cloud.polaris.router.metadata-router.enabled=false + # 是否启用北极星的就近路由能力 + spring.cloud.polaris.router.nearby-router.enabled=false + + ## 是否启用北极星的服务限流能力 + spring.cloud.polaris.ratelimit.enabled=false + # 设置触发限流时的提示信息 + # spring.cloud.polaris.ratelimit.rejectRequestTips="" + # 设置触发限流时的响应码 + spring.cloud.polaris.ratelimit.rejectHttpCode=429 + # 设置限流匀速排队最大排队时间 + spring.cloud.polaris.ratelimit.maxQueuingTime=1000 + + ## RPC 调用增强 + spring.cloud.tencent.rpc-enhancement.enabled=false + # 开启 RPC 调用结果上报 + spring.cloud.tencent.rpc-enhancement.reporter.enabled=false + + # 配置北极星监控指标上报 + spring.cloud.polaris.stat.enabled=false + # 指标上报监听端口 + spring.cloud.polaris.stat.port=0 + # 指标上报暴露的 http path + spring.cloud.polaris.stat.path=/metrics \ No newline at end of file diff --git a/deploy/kubernetes_v1.22/helm/templates/controller-configmap-sidecar.yaml b/deploy/kubernetes_v1.22/helm/templates/controller-configmap-sidecar.yaml index 814d94e7..f49281d5 100644 --- a/deploy/kubernetes_v1.22/helm/templates/controller-configmap-sidecar.yaml +++ b/deploy/kubernetes_v1.22/helm/templates/controller-configmap-sidecar.yaml @@ -87,6 +87,51 @@ data: - name: polaris-log emptyDir: {} + java-agent-config: |- + policy: enabled + alwaysInjectSelector: + [] + + neverInjectSelector: + [] + + template: | + initContainers: + - name: polaris-javaagent-init + image: polarismesh/polaris-javaagent-init:#JAVA_AGENT_INIT# + imagePullPolicy: Always + env: + - name: JAVA_AGENT_DIR + value: /app/lib/.polaris/java_agent + resources: + limits: + cpu: 100m + memory: 50Mi + requests: + cpu: 10m + memory: 10Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + add: + - NET_ADMIN + - NET_RAW + drop: + - ALL + privileged: false + readOnlyRootFilesystem: false + runAsGroup: 0 + runAsNonRoot: false + runAsUser: 0 + volumeMounts: + - mountPath: /app/lib/.polaris/java_agent + name: java-agent-dir + volumes: + - name: java-agent-dir + emptyDir: {} + + + mesh-config: |- policy: enabled alwaysInjectSelector: diff --git a/deploy/kubernetes_v1.22/kubernetes/injector.yaml b/deploy/kubernetes_v1.22/kubernetes/injector.yaml index 1a5a8ae2..5849f99a 100644 --- a/deploy/kubernetes_v1.22/kubernetes/injector.yaml +++ b/deploy/kubernetes_v1.22/kubernetes/injector.yaml @@ -92,6 +92,50 @@ data: - name: polaris-log emptyDir: {} + java-agent-config: |- + policy: enabled + alwaysInjectSelector: + [] + + neverInjectSelector: + [] + + template: | + initContainers: + - name: polaris-javaagent-init + image: polarismesh/polaris-javaagent-init:#JAVA_AGENT_INIT# + imagePullPolicy: Always + env: + - name: JAVA_AGENT_DIR + value: /app/lib/.polaris/java_agent + resources: + limits: + cpu: 100m + memory: 50Mi + requests: + cpu: 10m + memory: 10Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + add: + - NET_ADMIN + - NET_RAW + drop: + - ALL + privileged: false + readOnlyRootFilesystem: false + runAsGroup: 0 + runAsNonRoot: false + runAsUser: 0 + volumeMounts: + - mountPath: /app/lib/.polaris/java_agent + name: java-agent-dir + volumes: + - name: java-agent-dir + emptyDir: {} + + mesh-config: |- policy: enabled alwaysInjectSelector: diff --git a/deploy/kubernetes_v1.22/kubernetes/javaagent-configmap.yaml b/deploy/kubernetes_v1.22/kubernetes/javaagent-configmap.yaml new file mode 100644 index 00000000..558adf22 --- /dev/null +++ b/deploy/kubernetes_v1.22/kubernetes/javaagent-configmap.yaml @@ -0,0 +1,107 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: plugin-default.properties + namespace: polaris-system + labels: + app: sidecar-injector +data: + springcloud2020-default-properties: |+ + # 应用名称 + spring.application.name={{ .MicroserviceName }} + # 配置北极星服务端地址 + spring.cloud.polaris.address=grpc\://{{ .PolarisServerIP }}\:{{ .PolarisDiscoverPort }} + spring.cloud.polaris.enabled=true + + # 启用 Java Agent 的 Spring Cloud Tencent 注册发现能力 + spring.cloud.discovery.enabled=true + # 配置服务注册发现的命名空间信息 + spring.cloud.polaris.discovery.namespace=default + # 启用从北极星 + spring.cloud.polaris.discovery.enabled=true + spring.cloud.polaris.discovery.register=true + + ## 是否启用北极星的 LoadBalancer + spring.cloud.polaris.loadbalancer.enabled=true + #spring.cloud.polaris.loadbalancer.discoveryType=POLARIS + #spring.cloud.polaris.loadbalancer.strategy=random + + ## 是否启用北极星服务路由能力 + spring.cloud.polaris.router.enabled=false + # 是否启用北极星的自定义路由能力 + spring.cloud.polaris.router.rule-router.enabled=false + # 是否启用北极星的元数据路由能力 + spring.cloud.polaris.router.metadata-router.enabled=false + # 是否启用北极星的就近路由能力 + spring.cloud.polaris.router.nearby-router.enabled=false + + ## 是否启用北极星的服务限流能力 + spring.cloud.polaris.ratelimit.enabled=false + # 设置触发限流时的提示信息 + # spring.cloud.polaris.ratelimit.rejectRequestTips="" + # 设置触发限流时的响应码 + spring.cloud.polaris.ratelimit.rejectHttpCode=429 + # 设置限流匀速排队最大排队时间 + spring.cloud.polaris.ratelimit.maxQueuingTime=1000 + + ## RPC 调用增强 + spring.cloud.tencent.rpc-enhancement.enabled=false + # 开启 RPC 调用结果上报 + spring.cloud.tencent.rpc-enhancement.reporter.enabled=false + + # 配置北极星监控指标上报 + spring.cloud.polaris.stat.enabled=false + # 指标上报监听端口 + spring.cloud.polaris.stat.port=0 + # 指标上报暴露的 http path + spring.cloud.polaris.stat.path=/metrics + + springcloud2021-default-properties: |+ + # 应用名称 + spring.application.name={{ .MicroserviceName }} + # 配置北极星服务端地址 + spring.cloud.polaris.address=grpc\://{{ .PolarisServerIP }}\:{{ .PolarisDiscoverPort }} + spring.cloud.polaris.enabled=true + + # 启用 Java Agent 的 Spring Cloud Tencent 注册发现能力 + spring.cloud.discovery.enabled=true + # 配置服务注册发现的命名空间信息 + spring.cloud.polaris.discovery.namespace=default + # 启用从北极星 + spring.cloud.polaris.discovery.enabled=true + spring.cloud.polaris.discovery.register=true + + ## 是否启用北极星的 LoadBalancer + spring.cloud.polaris.loadbalancer.enabled=true + #spring.cloud.polaris.loadbalancer.discoveryType=POLARIS + #spring.cloud.polaris.loadbalancer.strategy=random + + ## 是否启用北极星服务路由能力 + spring.cloud.polaris.router.enabled=false + # 是否启用北极星的自定义路由能力 + spring.cloud.polaris.router.rule-router.enabled=false + # 是否启用北极星的元数据路由能力 + spring.cloud.polaris.router.metadata-router.enabled=false + # 是否启用北极星的就近路由能力 + spring.cloud.polaris.router.nearby-router.enabled=false + + ## 是否启用北极星的服务限流能力 + spring.cloud.polaris.ratelimit.enabled=false + # 设置触发限流时的提示信息 + # spring.cloud.polaris.ratelimit.rejectRequestTips="" + # 设置触发限流时的响应码 + spring.cloud.polaris.ratelimit.rejectHttpCode=429 + # 设置限流匀速排队最大排队时间 + spring.cloud.polaris.ratelimit.maxQueuingTime=1000 + + ## RPC 调用增强 + spring.cloud.tencent.rpc-enhancement.enabled=false + # 开启 RPC 调用结果上报 + spring.cloud.tencent.rpc-enhancement.reporter.enabled=false + + # 配置北极星监控指标上报 + spring.cloud.polaris.stat.enabled=false + # 指标上报监听端口 + spring.cloud.polaris.stat.port=0 + # 指标上报暴露的 http path + spring.cloud.polaris.stat.path=/metrics \ No newline at end of file diff --git a/deploy/variables.txt b/deploy/variables.txt index 17fa4cea..e50d8a43 100644 --- a/deploy/variables.txt +++ b/deploy/variables.txt @@ -3,4 +3,5 @@ CONTROLLER_VERSION:##VERSION## SIDECAR_VERSION:v1.5.1 POLARIS_TOKEN:nu/0WRA4EqSR1FagrjRj0fZwPXuGlMpX+zCuWu4uMqy8xr1vRjisSbA25aAC3mtU8MeeRsKhQiDAynUR09I= ENVOY_VERSION:v1.26.2 -CLUSTER_NAME:default \ No newline at end of file +CLUSTER_NAME:default +JAVA_AGENT_INIT:v0.0.1 \ No newline at end of file diff --git a/pkg/inject/pkg/kube/inject/apply/base/patch.go b/pkg/inject/pkg/kube/inject/apply/base/patch.go new file mode 100644 index 00000000..66c18364 --- /dev/null +++ b/pkg/inject/pkg/kube/inject/apply/base/patch.go @@ -0,0 +1,209 @@ +// Copyright 2018 Istio Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package base + +import ( + "fmt" + + utils "github.com/polarismesh/polaris-controller/pkg/util" + + "github.com/polarismesh/polaris-controller/pkg/inject/pkg/kube/inject" + corev1 "k8s.io/api/core/v1" +) + +// PodPatchBuilder +type PodPatchBuilder struct { +} + +func (pb *PodPatchBuilder) PatchContainer(req *inject.OperateContainerRequest) ([]inject.Rfc6902PatchOperation, error) { + switch req.Type { + case inject.PatchType_Add: + patchs := pb.addContainers(req.Option.SidecarMode, req.Option.Pod, req.Source, req.External, req.BasePath) + return patchs, nil + case inject.PatchType_Remove: + patchs := pb.removeContainers(req.Source, req.External, req.BasePath) + return patchs, nil + } + return nil, nil +} + +// JSONPatch `remove` is applied sequentially. Remove items in reverse +// order to avoid renumbering indices. +func (pb *PodPatchBuilder) removeContainers(containers, removed []corev1.Container, path string) []inject.Rfc6902PatchOperation { + patch := make([]inject.Rfc6902PatchOperation, 0, 4) + + names := map[string]bool{} + for _, container := range removed { + names[container.Name] = true + } + for i := len(containers) - 1; i >= 0; i-- { + if _, ok := names[containers[i].Name]; ok { + patch = append(patch, inject.Rfc6902PatchOperation{ + Op: "remove", + Path: fmt.Sprintf("%v/%v", path, i), + }) + } + } + return patch +} + +func (pb *PodPatchBuilder) addContainers(sidecarMode utils.SidecarMode, pod *corev1.Pod, target, added []corev1.Container, basePath string) []inject.Rfc6902PatchOperation { + + patch := make([]inject.Rfc6902PatchOperation, 0, 4) + + saJwtSecretMountName := "" + var saJwtSecretMount corev1.VolumeMount + // find service account secret volume mount(/var/run/secrets/kubernetes.io/serviceaccount, + // https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/#service-account-automation) from app container + for _, add := range target { + for _, vmount := range add.VolumeMounts { + if vmount.MountPath == "/var/run/secrets/kubernetes.io/serviceaccount" { + saJwtSecretMountName = vmount.Name + saJwtSecretMount = vmount + } + } + } + first := len(target) == 0 + var value interface{} + for _, add := range added { + if add.Name == inject.ProxyContainerName && saJwtSecretMountName != "" { + // add service account secret volume mount(/var/run/secrets/kubernetes.io/serviceaccount, + // https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/#service-account-automation) to istio-proxy container, + // so that envoy could fetch/pass k8s sa jwt and pass to sds server, which will be used to request workload identity for the pod. + add.VolumeMounts = append(add.VolumeMounts, saJwtSecretMount) + } + + value = add + path := basePath + if first { + first = false + value = []corev1.Container{add} + } else { + path += "/-" + } + patch = append(patch, inject.Rfc6902PatchOperation{ + Op: "add", + Path: path, + Value: value, + }) + } + return patch +} + +func (pb *PodPatchBuilder) PatchVolumes(req *inject.OperateVolumesRequest) ([]inject.Rfc6902PatchOperation, error) { + switch req.Type { + case inject.PatchType_Add: + patchs := addVolume(req.Source, req.External, req.BasePath) + return patchs, nil + case inject.PatchType_Remove: + patchs := removeVolumes(req.Source, req.External, req.BasePath) + return patchs, nil + } + return nil, nil +} + +func addVolume(target, added []corev1.Volume, basePath string) (patch []inject.Rfc6902PatchOperation) { + first := len(target) == 0 + var value interface{} + for _, add := range added { + value = add + path := basePath + if first { + first = false + value = []corev1.Volume{add} + } else { + path += "/-" + } + patch = append(patch, inject.Rfc6902PatchOperation{ + Op: "add", + Path: path, + Value: value, + }) + } + return patch +} + +func removeVolumes(volumes, removed []corev1.Volume, path string) (patch []inject.Rfc6902PatchOperation) { + names := map[string]bool{} + for _, volume := range removed { + names[volume.Name] = true + } + for i := len(volumes) - 1; i >= 0; i-- { + if _, ok := names[volumes[i].Name]; ok { + patch = append(patch, inject.Rfc6902PatchOperation{ + Op: "remove", + Path: fmt.Sprintf("%v/%v", path, i), + }) + } + } + return patch +} + +func (pb *PodPatchBuilder) PatchImagePullSecrets(req *inject.OperateImagePullSecretsRequest) ([]inject.Rfc6902PatchOperation, error) { + switch req.Type { + case inject.PatchType_Add: + patchs := addImagePullSecrets(req.Source, req.External, req.BasePath) + return patchs, nil + case inject.PatchType_Remove: + patchs := removeImagePullSecrets(req.Source, req.External, req.BasePath) + return patchs, nil + } + return nil, nil +} + +func addImagePullSecrets(target, added []corev1.LocalObjectReference, basePath string) (patch []inject.Rfc6902PatchOperation) { + first := len(target) == 0 + var value interface{} + for _, add := range added { + value = add + path := basePath + if first { + first = false + value = []corev1.LocalObjectReference{add} + } else { + path += "/-" + } + patch = append(patch, inject.Rfc6902PatchOperation{ + Op: "add", + Path: path, + Value: value, + }) + } + return patch +} + +func removeImagePullSecrets(imagePullSecrets, removed []corev1.LocalObjectReference, path string) (patch []inject.Rfc6902PatchOperation) { + names := map[string]bool{} + for _, ref := range removed { + names[ref.Name] = true + } + for i := len(imagePullSecrets) - 1; i >= 0; i-- { + if _, ok := names[imagePullSecrets[i].Name]; ok { + patch = append(patch, inject.Rfc6902PatchOperation{ + Op: "remove", + Path: fmt.Sprintf("%v/%v", path, i), + }) + } + } + return patch +} + +func (pb *PodPatchBuilder) PatchSecurityContext() ([]inject.Rfc6902PatchOperation, error) { + return nil, nil +} + +func (pb *PodPatchBuilder) PatchDnsConfig() ([]inject.Rfc6902PatchOperation, error) { + return nil, nil +} diff --git a/pkg/inject/pkg/kube/inject/apply/javaagent/patch.go b/pkg/inject/pkg/kube/inject/apply/javaagent/patch.go new file mode 100644 index 00000000..aaad6444 --- /dev/null +++ b/pkg/inject/pkg/kube/inject/apply/javaagent/patch.go @@ -0,0 +1,210 @@ +// Copyright 2018 Istio Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package javaagent + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "strings" + "text/template" + + "github.com/polarismesh/polaris-controller/common/log" + "github.com/polarismesh/polaris-controller/pkg/polarisapi" + "github.com/polarismesh/polaris-controller/pkg/util" + utils "github.com/polarismesh/polaris-controller/pkg/util" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/polarismesh/polaris-controller/pkg/inject/pkg/kube/inject" + "github.com/polarismesh/polaris-controller/pkg/inject/pkg/kube/inject/apply/base" + corev1 "k8s.io/api/core/v1" +) + +// Java Agent 场景下的特殊 annonations 信息 +const ( + customJavaAgentVersion = "sidecar.polarismesh.cn/java-agent-version" + customJavaAgentPluginType = "sidecar.polarismesh.cn/java-agent-plugin-type" + customJavaAgentPluginConfig = "sidecar.polarismesh.cn/java-agent-plugin-config" +) + +const ( + ActiveJavaAgentCmd = "-javaagent:/app/lib/.polaris/java_agent/polaris-agent-core-bootstrap.jar" +) + +func init() { + inject.RegisterPatchBuilder(utils.SidecarJavaAgentModeName, &PodPatchBuilder{}) +} + +// PodPatchBuilder +type PodPatchBuilder struct { + *base.PodPatchBuilder +} + +func (pb *PodPatchBuilder) PatchContainer(req *inject.OperateContainerRequest) ([]inject.Rfc6902PatchOperation, error) { + switch req.Type { + case inject.PatchType_Remove: + return pb.PodPatchBuilder.PatchContainer(req) + case inject.PatchType_Add: + pod := req.Option.Pod + added := req.External + for _, add := range added { + if add.Name == "polaris-javaagent-init" { + log.InjectScope().Infof("begin deal polaris-javaagent-init inject for pod=[%s, %s]", pod.Namespace, pod.Name) + if err := pb.handleJavaAgentInit(req.Option, pod, &add); err != nil { + log.InjectScope().Errorf("handle polaris-javaagent-init inject for pod=[%s, %s] failed: %v", pod.Namespace, pod.Name, err) + } + } + } + // 重新更新请求参数中的 req.External + req.External = added + case inject.PatchType_Update: + return pb.updateContainer(req.Option.SidecarMode, req.Option.Pod, req.Option.Pod.Spec.Containers, req.BasePath), nil + } + return nil, nil +} + +func (pb *PodPatchBuilder) handleJavaAgentInit(opt *inject.PatchOptions, pod *corev1.Pod, add *corev1.Container) error { + annonations := opt.Annotations + // 判断用户是否自定义了 javaagent 的版本 + if val, ok := annonations[customJavaAgentVersion]; ok { + oldImageInfo := strings.Split(add.Image, ":") + add.Image = fmt.Sprintf("%s:%s", oldImageInfo[0], val) + } + + // 需要将用户的框架信息注入到 javaagent-init 中,用于初始化相关的配置文件信息 + if pluginType, ok := annonations[customJavaAgentPluginType]; ok { + add.Env = append(add.Env, corev1.EnvVar{ + Name: "JAVA_AGENT_PLUGIN_TYPE", + Value: pluginType, + }) + kubeClient := opt.KubeClient + pluginCm, err := kubeClient.CoreV1().ConfigMaps(util.RootNamespace).Get(context.Background(), + "plugin-default.properties", metav1.GetOptions{}) + if err != nil { + return err + } + pluginConfTemp := pluginCm.Data[nameOfPluginDefault(pluginType)] + defaultParam := map[string]string{ + "MicroserviceName": annonations[util.SidecarServiceName], + "PolarisServerIP": strings.Split(polarisapi.PolarisGrpc, ":")[0], + "PolarisDiscoverPort": strings.Split(polarisapi.PolarisGrpc, ":")[1], + } + tpl, err := template.New(pluginType).Parse(pluginConfTemp) + if err != nil { + return err + } + buf := new(bytes.Buffer) + if err := tpl.Execute(buf, defaultParam); err != nil { + return err + } + defaultProperties := map[string]string{} + if err := json.Unmarshal(buf.Bytes(), &defaultProperties); err != nil { + return err + } + + // 查看用户是否自定义了相关配置信息 + // 需要根据用户的自定义参数信息,将 agent 的特定 application.properties 文件注入到 javaagent-init 中 + if properties, ok := annonations[customJavaAgentPluginConfig]; ok { + customProperties := map[string]string{} + if err := json.Unmarshal([]byte(properties), &customProperties); err != nil { + return err + } + // 先从 configmap 中获取 java-agent 不同 plugin-type 的默认配置信息 + for k, v := range customProperties { + defaultProperties[k] = v + } + } + exportAgentPluginConf := "" + for key, value := range defaultProperties { + exportAgentPluginConf += fmt.Sprintf("\n%s=%s\n", key, value) + } + + add.Env = append(add.Env, corev1.EnvVar{ + Name: "JAVA_AGENT_PLUGIN_CONF", + Value: exportAgentPluginConf, + }) + } + + return nil +} + +func nameOfPluginDefault(v string) string { + return v + "-default-properties" +} + +func (pb *PodPatchBuilder) updateContainer(sidecarMode utils.SidecarMode, pod *corev1.Pod, + target []corev1.Container, basePath string) []inject.Rfc6902PatchOperation { + + patchs := make([]inject.Rfc6902PatchOperation, 0, len(target)) + + for _, container := range target { + envs := container.Env + javaEnvIndex := -1 + if len(envs) != 0 { + for i := range envs { + if envs[i].Name == "JAVA_TOOL_OPTIONS" { + javaEnvIndex = i + break + } + } + if javaEnvIndex != -1 { + oldVal := envs[javaEnvIndex].Value + envs[javaEnvIndex] = corev1.EnvVar{ + Name: "JAVA_TOOL_OPTIONS", + Value: oldVal + " " + ActiveJavaAgentCmd, + } + } + } + if javaEnvIndex == -1 { + // 注入 java agent 需要用到的参数信息 + container.Env = append(container.Env, corev1.EnvVar{ + Name: "JAVA_TOOL_OPTIONS", + Value: ActiveJavaAgentCmd, + }) + } + + // container 需要新挂载磁盘 + container.VolumeMounts = append(container.VolumeMounts, + corev1.VolumeMount{ + Name: "java-agent-dir", + MountPath: "/app/lib/.polaris/java_agent", + }) + + path := basePath + "/-" + patchs = append(patchs, inject.Rfc6902PatchOperation{ + Op: "replace", + Path: path, + Value: container, + }) + } + return patchs +} + +func (pb *PodPatchBuilder) PatchVolumes(req *inject.OperateVolumesRequest) ([]inject.Rfc6902PatchOperation, error) { + return pb.PodPatchBuilder.PatchVolumes(req) +} + +func (pb *PodPatchBuilder) PatchImagePullSecrets(req *inject.OperateImagePullSecretsRequest) ([]inject.Rfc6902PatchOperation, error) { + return pb.PodPatchBuilder.PatchImagePullSecrets(req) +} + +func (pb *PodPatchBuilder) PatchSecurityContext() ([]inject.Rfc6902PatchOperation, error) { + return nil, nil +} + +func (pb *PodPatchBuilder) PatchDnsConfig() ([]inject.Rfc6902PatchOperation, error) { + return nil, nil +} diff --git a/pkg/inject/pkg/kube/inject/apply/mesh/patch.go b/pkg/inject/pkg/kube/inject/apply/mesh/patch.go new file mode 100644 index 00000000..e63849ea --- /dev/null +++ b/pkg/inject/pkg/kube/inject/apply/mesh/patch.go @@ -0,0 +1,212 @@ +// Copyright 2018 Istio Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package mesh + +import ( + "bytes" + "context" + "fmt" + "html/template" + "strconv" + "strings" + + "github.com/polarismesh/polaris-controller/common" + "github.com/polarismesh/polaris-controller/common/log" + "github.com/polarismesh/polaris-controller/pkg/util" + utils "github.com/polarismesh/polaris-controller/pkg/util" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + + "github.com/polarismesh/polaris-controller/pkg/inject/pkg/kube/inject" + "github.com/polarismesh/polaris-controller/pkg/inject/pkg/kube/inject/apply/base" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/yaml" +) + +func init() { + inject.RegisterPatchBuilder(utils.SidecarDnsModeName, &PodPatchBuilder{}) + inject.RegisterPatchBuilder(utils.SidecarMeshModeName, &PodPatchBuilder{}) +} + +// PodPatchBuilder +type PodPatchBuilder struct { + *base.PodPatchBuilder +} + +func (pb *PodPatchBuilder) PatchContainer(req *inject.OperateContainerRequest) ([]inject.Rfc6902PatchOperation, error) { + switch req.Type { + case inject.PatchType_Add: + pod := req.Option.Pod + added := req.External + for _, add := range added { + // mesh condition || dns condition + if add.Name == "polaris-bootstrap-writer" || add.Name == "polaris-sidecar-init" { + log.InjectScope().Infof("begin to add polaris-sidecar config to int container for pod[%s, %s]", + pod.Namespace, pod.Name) + if err := pb.addPolarisConfigToInitContainerEnv(req.Option, &add); err != nil { + log.InjectScope().Errorf("begin to add polaris-sidecar config to init container for pod[%s, %s] failed: %v", + pod.Namespace, pod.Name, err) + } + } + if add.Name == "polaris-sidecar" { + log.InjectScope().Infof("begin deal polaris-sidecar inject for pod=[%s, %s]", pod.Namespace, pod.Name) + if _, err := pb.handlePolarisSidecarEnvInject(req.Option, pod, &add); err != nil { + log.InjectScope().Errorf("handle polaris-sidecar inject for pod=[%s, %s] failed: %v", pod.Namespace, pod.Name, err) + } + // 将刚刚创建好的配置文件挂载到 pod 的 container 中去 + add.VolumeMounts = append(add.VolumeMounts, corev1.VolumeMount{ + Name: utils.PolarisGoConfigFile, + SubPath: "polaris.yaml", + MountPath: "/data/polaris.yaml", + }) + } + } + // 重新更新请求参数中的 req.External + req.External = added + } + return pb.PodPatchBuilder.PatchContainer(req) +} + +func (pb *PodPatchBuilder) handlePolarisSidecarEnvInject(opt *inject.PatchOptions, pod *corev1.Pod, add *corev1.Container) (bool, error) { + + err := pb.ensureRootCertExist(opt.KubeClient, pod) + if err != nil { + return false, err + } + envMap := make(map[string]string) + envMap[EnvSidecarPort] = strconv.Itoa(ValueListenPort) + envMap[EnvSidecarRecurseEnable] = strconv.FormatBool(true) + if opt.SidecarMode == utils.SidecarForDns { + envMap[EnvSidecarDnsEnable] = strconv.FormatBool(true) + envMap[EnvSidecarMeshEnable] = strconv.FormatBool(false) + envMap[EnvSidecarMetricEnable] = strconv.FormatBool(false) + envMap[EnvSidecarMetricListenPort] = strconv.Itoa(ValueMetricListenPort) + } else { + envMap[EnvSidecarDnsEnable] = strconv.FormatBool(false) + envMap[EnvSidecarMeshEnable] = strconv.FormatBool(true) + envMap[EnvSidecarRLSEnable] = strconv.FormatBool(true) + envMap[EnvSidecarMetricEnable] = strconv.FormatBool(true) + envMap[EnvSidecarMetricListenPort] = strconv.Itoa(ValueMetricListenPort) + } + envMap[EnvSidecarLogLevel] = "info" + envMap[EnvSidecarNamespace] = pod.GetNamespace() + envMap[EnvPolarisAddress] = common.PolarisServerGrpcAddress + envMap[EnvSidecarDnsRouteLabels] = buildLabelsStr(pod.Labels) + if inject.EnableMtls(pod) { + envMap[EnvSidecarMtlsEnable] = strconv.FormatBool(true) + } + log.InjectScope().Infof("pod=[%s, %s] inject polaris-sidecar mode %s, env map %v", + pod.Namespace, pod.Name, utils.ParseSidecarModeName(opt.SidecarMode), envMap) + for k, v := range envMap { + add.Env = append(add.Env, corev1.EnvVar{Name: k, Value: v}) + } + return true, nil +} + +// ensureRootCertExist ensure that we have rootca pem secret in current namespace +func (pb *PodPatchBuilder) ensureRootCertExist(k8sClient *kubernetes.Clientset, pod *corev1.Pod) error { + if !inject.EnableMtls(pod) { + return nil + } + ns := pod.Namespace + _, err := k8sClient.CoreV1().Secrets(ns).Get(context.TODO(), utils.PolarisSidecarRootCert, metav1.GetOptions{}) + if err == nil { + return nil + } + if !errors.IsNotFound(err) { + return err + } + secret, err := k8sClient.CoreV1().Secrets(util.RootNamespace).Get(context.TODO(), utils.PolarisSidecarRootCert, metav1.GetOptions{}) + if err != nil { + return err + } + + // copy all data from root namespace rootca secret. + s := &corev1.Secret{} + s.Data = secret.Data + s.StringData = secret.StringData + s.Name = utils.PolarisSidecarRootCert + _, err = k8sClient.CoreV1().Secrets(ns).Create(context.TODO(), s, metav1.CreateOptions{}) + if errors.IsAlreadyExists(err) || errors.IsConflict(err) { + return nil + } + return err +} + +func buildLabelsStr(labels map[string]string) string { + tags := make([]string, 0, len(labels)) + + for k, v := range labels { + tags = append(tags, fmt.Sprintf("%s:%s", k, v)) + } + + return strings.Join(tags, ",") +} + +// addPolarisConfigToInitContainerEnv 将polaris-sidecar 的配置注入到init container中 +func (pb *PodPatchBuilder) addPolarisConfigToInitContainerEnv(opt *inject.PatchOptions, add *corev1.Container) error { + k8sClient := opt.KubeClient + cfgTpl, err := k8sClient.CoreV1().ConfigMaps(common.PolarisControllerNamespace). + Get(context.TODO(), utils.PolarisGoConfigFileTpl, metav1.GetOptions{}) + if err != nil { + log.InjectScope().Errorf("[Webhook][Inject] parse polaris-sidecar failed: %v", err) + return err + } + + tmp, err := (&template.Template{}).Parse(cfgTpl.Data["polaris.yaml"]) + if err != nil { + log.InjectScope().Errorf("[Webhook][Inject] parse polaris-sidecar failed: %v", err) + return err + } + buf := new(bytes.Buffer) + if err := tmp.Execute(buf, inject.PolarisGoConfig{ + Name: utils.PolarisGoConfigFile, + PolarisServer: common.PolarisServerGrpcAddress, + }); err != nil { + log.InjectScope().Errorf("[Webhook][Inject] parse polaris-sidecar failed: %v", err) + return err + } + + // 获取 polaris-sidecar 配置 + configMap := corev1.ConfigMap{} + str := buf.String() + if err := yaml.NewYAMLOrJSONDecoder(strings.NewReader(str), len(str)).Decode(&configMap); err != nil { + log.InjectScope().Errorf("[Webhook][Inject] parse polaris-sidecar failed: %v", err) + return err + } + + add.Env = append(add.Env, corev1.EnvVar{ + Name: utils.PolarisGoConfigFile, + Value: configMap.Data["polaris.yaml"], + }) + return nil +} + +func (pb *PodPatchBuilder) PatchVolumes(req *inject.OperateVolumesRequest) ([]inject.Rfc6902PatchOperation, error) { + return pb.PodPatchBuilder.PatchVolumes(req) +} + +func (pb *PodPatchBuilder) PatchImagePullSecrets(req *inject.OperateImagePullSecretsRequest) ([]inject.Rfc6902PatchOperation, error) { + return pb.PodPatchBuilder.PatchImagePullSecrets(req) +} + +func (pb *PodPatchBuilder) PatchSecurityContext() ([]inject.Rfc6902PatchOperation, error) { + return nil, nil +} + +func (pb *PodPatchBuilder) PatchDnsConfig() ([]inject.Rfc6902PatchOperation, error) { + return nil, nil +} diff --git a/pkg/inject/pkg/kube/inject/sidecar_env.go b/pkg/inject/pkg/kube/inject/apply/mesh/sidecar_env.go similarity index 99% rename from pkg/inject/pkg/kube/inject/sidecar_env.go rename to pkg/inject/pkg/kube/inject/apply/mesh/sidecar_env.go index f925924f..4b6f379c 100644 --- a/pkg/inject/pkg/kube/inject/sidecar_env.go +++ b/pkg/inject/pkg/kube/inject/apply/mesh/sidecar_env.go @@ -15,7 +15,7 @@ * specific language governing permissions and limitations under the License. */ -package inject +package mesh const ( EnvSidecarBind = "SIDECAR_BIND" diff --git a/pkg/inject/pkg/kube/inject/inject.go b/pkg/inject/pkg/kube/inject/inject.go index 2dce861e..220c68b0 100644 --- a/pkg/inject/pkg/kube/inject/inject.go +++ b/pkg/inject/pkg/kube/inject/inject.go @@ -267,6 +267,7 @@ func validateBool(value string) error { return err } +// getSidecarMode 获取 sidecar 注入模式 func (wh *Webhook) getSidecarMode(namespace string, pod *corev1.Pod) utils.SidecarMode { // 这里主要是处理北极星 sidecar, 优先级: pod.annotations > namespace.labels > configmap sidecarMode := "" @@ -577,16 +578,24 @@ func InjectionData(sidecarTemplate, valuesConfig, version string, typeMetadata * status := &SidecarInjectionStatus{Version: version} for _, c := range sic.InitContainers { - status.InitContainers = append(status.InitContainers, c.Name) + status.InitContainers = append(status.InitContainers, corev1.Container{ + Name: c.Name, + }) } for _, c := range sic.Containers { - status.Containers = append(status.Containers, c.Name) + status.Containers = append(status.Containers, corev1.Container{ + Name: c.Name, + }) } for _, c := range sic.Volumes { - status.Volumes = append(status.Volumes, c.Name) + status.Volumes = append(status.Volumes, corev1.Volume{ + Name: c.Name, + }) } for _, c := range sic.ImagePullSecrets { - status.ImagePullSecrets = append(status.ImagePullSecrets, c.Name) + status.ImagePullSecrets = append(status.ImagePullSecrets, corev1.LocalObjectReference{ + Name: c.Name, + }) } statusAnnotationValue, err := json.Marshal(status) if err != nil { @@ -788,11 +797,11 @@ func valueOrDefault(value interface{}, defaultValue interface{}) interface{} { // injected sidecar. This includes the names of added containers and // volumes. type SidecarInjectionStatus struct { - Version string `json:"version"` - InitContainers []string `json:"initContainers"` - Containers []string `json:"containers"` - Volumes []string `json:"volumes"` - ImagePullSecrets []string `json:"imagePullSecrets"` + Version string `json:"version"` + InitContainers []corev1.Container `json:"initContainers"` + Containers []corev1.Container `json:"containers"` + Volumes []corev1.Volume `json:"volumes"` + ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets"` } // helper function to generate a template version identifier from a diff --git a/pkg/inject/pkg/kube/inject/pod_patch.go b/pkg/inject/pkg/kube/inject/pod_patch.go new file mode 100644 index 00000000..d9677f99 --- /dev/null +++ b/pkg/inject/pkg/kube/inject/pod_patch.go @@ -0,0 +1,94 @@ +// Copyright 2018 Istio Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package inject + +import ( + utils "github.com/polarismesh/polaris-controller/pkg/util" + corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes" +) + +var ( + // 定义一个变量,用于记录当前正在使用的 patch builder + _PatchBuilders map[string]PodPatchBuilder +) + +func RegisterPatchBuilder(name string, pb PodPatchBuilder) { + _PatchBuilders[name] = pb +} + +type PatchType int32 + +const ( + _ PatchType = iota + // PatchType_Remove 删除操作 + PatchType_Remove + // PatchType_Add 增加操作 + PatchType_Add + // PatchType_Update 更新操作 + PatchType_Update +) + +type PatchOptions struct { + KubeClient *kubernetes.Clientset + // Sidecar 的运行模式 + SidecarMode utils.SidecarMode + // 目标操作的 POD + Pod *corev1.Pod + // 如果 POD 之前已经注入过 Sidecar,那么这里会记录之前的状态信息 + PrevStatus *SidecarInjectionStatus + // 需要增加的注解信息 + Annotations map[string]string + // 准备注入 POD 的 Sidecar 的新信息 + Sic *SidecarInjectionSpec + // Workload 的名称 + WorkloadName string +} + +type OperateContainerRequest struct { + // 操作类型 + Type PatchType + Option *PatchOptions + Source []corev1.Container + External []corev1.Container + BasePath string +} + +type OperateVolumesRequest struct { + // 操作类型 + Type PatchType + Option *PatchOptions + Source []corev1.Volume + External []corev1.Volume + BasePath string +} + +type OperateImagePullSecretsRequest struct { + // 操作类型 + Type PatchType + Option *PatchOptions + Source []corev1.LocalObjectReference + External []corev1.LocalObjectReference + BasePath string +} + +// PodPatchBuilder +type PodPatchBuilder interface { + PatchContainer(*OperateContainerRequest) ([]Rfc6902PatchOperation, error) + PatchVolumes(*OperateVolumesRequest) ([]Rfc6902PatchOperation, error) + PatchImagePullSecrets(*OperateImagePullSecretsRequest) ([]Rfc6902PatchOperation, error) + PatchSecurityContext() ([]Rfc6902PatchOperation, error) + PatchDnsConfig() ([]Rfc6902PatchOperation, error) +} diff --git a/pkg/inject/pkg/kube/inject/webhook.go b/pkg/inject/pkg/kube/inject/webhook.go index 8897a477..ba4457c2 100644 --- a/pkg/inject/pkg/kube/inject/webhook.go +++ b/pkg/inject/pkg/kube/inject/webhook.go @@ -25,7 +25,6 @@ import ( "net/http" "path/filepath" "sort" - "strconv" "strings" "sync" "text/template" @@ -38,6 +37,7 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + apismetav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/util/yaml" @@ -68,65 +68,91 @@ const ( // Webhook implements a mutating webhook for automatic proxy injection. type Webhook struct { - defaultSidecarMode utils.SidecarMode - mu sync.RWMutex + defaultSidecarMode utils.SidecarMode + mu sync.RWMutex + + // envoy mesh 场景下的 sidecar 注入配置 sidecarMeshConfig *Config sidecarMeshTemplateVersion string - sidecarDnsConfig *Config - sidecarDnsTemplateVersion string - meshConfig *mesh.MeshConfig - valuesConfig string + // dns 场景下的 sidecar 注入配置 + sidecarDnsConfig *Config + sidecarDnsTemplateVersion string + // java agent 场景下的注入配置 + sidecarJavaAgentConfig *Config + sidecarJavaAgentTemplateVersion string + meshConfig *mesh.MeshConfig + valuesConfig string healthCheckInterval time.Duration healthCheckFile string - server *http.Server - meshFile string - meshConfigFile string - dnsConfigFile string - valuesFile string - watcher *fsnotify.Watcher - certFile string - keyFile string - cert *tls.Certificate + server *http.Server + meshFile string + meshConfigFile string + dnsConfigFile string + javaAgentConfigFile string + valuesFile string + watcher *fsnotify.Watcher + certFile string + keyFile string + cert *tls.Certificate k8sClient kubernetes.Interface } +type InjectConfigInfo struct { + MeshInjectConf *Config + DnsInjectConf *Config + JavaAgentInjectConf *Config + MeshConf *mesh.MeshConfig + ValuesConf string +} + // env will be used for other things besides meshConfig - when webhook is running in Istiod it can take advantage // of the config and endpoint cache. // nolint directives: interfacer -func loadConfig(injectMeshFile, injectDnsFile, meshFile, valuesFile string) (*Config, *Config, *mesh.MeshConfig, string, error) { +func loadConfig(injectMeshFile, injectDnsFile, injectJavaFile, meshFile, valuesFile string) (*InjectConfigInfo, error) { // 处理 polaris-sidecar mesh 模式的注入 meshData, err := ioutil.ReadFile(injectMeshFile) if err != nil { - return nil, nil, nil, "", err + return nil, err } var meshConf Config if err := gyaml.Unmarshal(meshData, &meshConf); err != nil { - log.InjectScope().Warnf("Failed to parse injectFile %s", string(meshData)) - return nil, nil, nil, "", err + log.InjectScope().Warnf("Failed to parse inject mesh config file %s", string(meshData)) + return nil, err } // 处理 polaris-sidecar dns 模式的注入 dnsData, err := ioutil.ReadFile(injectDnsFile) if err != nil { - return nil, nil, nil, "", err + return nil, err } var dnsConf Config if err := gyaml.Unmarshal(dnsData, &dnsConf); err != nil { - log.InjectScope().Warnf("Failed to parse injectFile %s", string(dnsData)) - return nil, nil, nil, "", err + log.InjectScope().Warnf("Failed to parse inject dns config file %s", string(dnsData)) + return nil, err + } + + // 处理 java-agent 模式的注入 + javaAgentData, err := ioutil.ReadFile(injectJavaFile) + if err != nil { + return nil, err + } + var javaAgentConf Config + if err := gyaml.Unmarshal(javaAgentData, &javaAgentData); err != nil { + log.InjectScope().Warnf("Failed to parse inject java-agent config file %s", string(dnsData)) + return nil, err } valuesConfig, err := ioutil.ReadFile(valuesFile) if err != nil { - return nil, nil, nil, "", err + return nil, err } meshConfig, err := mesh.ReadMeshConfig(meshFile) if err != nil { - return nil, nil, nil, "", err + return nil, err } log.InjectScope().Infof("[MESH] New inject configuration: sha256sum %x", sha256.Sum256(meshData)) @@ -141,7 +167,18 @@ func loadConfig(injectMeshFile, injectDnsFile, meshFile, valuesFile string) (*Co log.InjectScope().Infof("[DNS] NeverInjectSelector: %v", dnsConf.NeverInjectSelector) log.InjectScope().Infof("[DNS] Template: |\n %v", strings.Replace(dnsConf.Template, "\n", "\n ", -1)) - return &meshConf, &dnsConf, meshConfig, string(valuesConfig), nil + log.InjectScope().Infof("[JavaAgent] New inject configuration: sha256sum %x", sha256.Sum256(javaAgentData)) + log.InjectScope().Infof("[JavaAgent] AlwaysInjectSelector: %v", javaAgentConf.AlwaysInjectSelector) + log.InjectScope().Infof("[JavaAgent] NeverInjectSelector: %v", javaAgentConf.NeverInjectSelector) + log.InjectScope().Infof("[JavaAgent] Template: |\n %v", strings.Replace(javaAgentConf.Template, "\n", "\n ", -1)) + + return &InjectConfigInfo{ + MeshInjectConf: &meshConf, + DnsInjectConf: &dnsConf, + JavaAgentInjectConf: &javaAgentConf, + MeshConf: meshConfig, + ValuesConf: string(valuesConfig), + }, nil } // WebhookParameters configures parameters for the sidecar injection @@ -156,6 +193,9 @@ type WebhookParameters struct { // DnsConfigFile 处理 polaris-sidecar 运行模式为 dns 的配置文件 DnsConfigFile string + // JavaAgentConfigFile 处理运行模式为 javaagent 的配置文件 + JavaAgentConfigFile string + ValuesFile string // MeshFile is the path to the mesh configuration file. @@ -190,7 +230,9 @@ type WebhookParameters struct { func NewWebhook(p WebhookParameters) (*Webhook, error) { // TODO: pass a pointer to mesh config from Pilot bootstrap, no need to watch and load 2 times // This is needed before we implement advanced merging / patching of mesh config - sidecarMeshConfig, sidecarDnsConfig, meshConfig, valuesConfig, err := loadConfig(p.MeshConfigFile, p.DnsConfigFile, p.MeshFile, p.ValuesFile) + injectConf, err := loadConfig(p.MeshConfigFile, p.DnsConfigFile, + p.JavaAgentConfigFile, p.MeshFile, p.ValuesFile) + if err != nil { return nil, err } @@ -203,6 +245,8 @@ func NewWebhook(p WebhookParameters) (*Webhook, error) { if err != nil { return nil, err } + + // TODO 直接监听 configmap // watch the parent directory of the target files so we can catch // symlink updates of k8s ConfigMaps volumes. for _, file := range []string{p.MeshConfigFile, p.DnsConfigFile, p.MeshFile, p.CertFile, p.KeyFile} { @@ -216,22 +260,25 @@ func NewWebhook(p WebhookParameters) (*Webhook, error) { } wh := &Webhook{ - sidecarMeshConfig: sidecarMeshConfig, - sidecarMeshTemplateVersion: sidecarTemplateVersionHash(sidecarMeshConfig.Template), - sidecarDnsConfig: sidecarDnsConfig, - sidecarDnsTemplateVersion: sidecarTemplateVersionHash(sidecarDnsConfig.Template), - meshConfig: meshConfig, - meshConfigFile: p.MeshConfigFile, - dnsConfigFile: p.DnsConfigFile, - valuesFile: p.ValuesFile, - valuesConfig: valuesConfig, - meshFile: p.MeshFile, - watcher: watcher, - healthCheckInterval: p.HealthCheckInterval, - healthCheckFile: p.HealthCheckFile, - certFile: p.CertFile, - keyFile: p.KeyFile, - cert: &pair, + sidecarMeshConfig: injectConf.MeshInjectConf, + sidecarMeshTemplateVersion: sidecarTemplateVersionHash(injectConf.MeshInjectConf.Template), + sidecarDnsConfig: injectConf.DnsInjectConf, + sidecarDnsTemplateVersion: sidecarTemplateVersionHash(injectConf.DnsInjectConf.Template), + sidecarJavaAgentConfig: injectConf.JavaAgentInjectConf, + sidecarJavaAgentTemplateVersion: sidecarTemplateVersionHash(injectConf.JavaAgentInjectConf.Template), + meshConfig: injectConf.MeshConf, + meshConfigFile: p.MeshConfigFile, + dnsConfigFile: p.DnsConfigFile, + javaAgentConfigFile: p.JavaAgentConfigFile, + valuesFile: p.ValuesFile, + valuesConfig: injectConf.ValuesConf, + meshFile: p.MeshFile, + watcher: watcher, + healthCheckInterval: p.HealthCheckInterval, + healthCheckFile: p.HealthCheckFile, + certFile: p.CertFile, + keyFile: p.KeyFile, + cert: &pair, // 新增查询 k8s 资源的操作者 k8sClient: p.Client, @@ -279,7 +326,7 @@ func (wh *Webhook) Run(stop <-chan struct{}) { select { case <-timerC: timerC = nil - sidecarMeshConfig, sidecarDnsConfig, meshConfig, valuesConfig, err := loadConfig(wh.meshConfigFile, wh.dnsConfigFile, wh.meshFile, wh.valuesFile) + injectConf, err := loadConfig(wh.meshConfigFile, wh.dnsConfigFile, wh.javaAgentConfigFile, wh.meshFile, wh.valuesFile) if err != nil { log.InjectScope().Errorf("update error: %v", err) break @@ -290,11 +337,19 @@ func (wh *Webhook) Run(stop <-chan struct{}) { log.InjectScope().Errorf("reload cert error: %v", err) break } + + sidecarMeshConfig := injectConf.MeshInjectConf + sidecarDnsConfig := injectConf.DnsInjectConf + meshConfig := injectConf.MeshConf + valuesConfig := injectConf.ValuesConf + wh.mu.Lock() wh.sidecarMeshConfig = sidecarMeshConfig wh.sidecarMeshTemplateVersion = sidecarTemplateVersionHash(sidecarMeshConfig.Template) wh.sidecarDnsConfig = sidecarDnsConfig wh.sidecarDnsTemplateVersion = sidecarTemplateVersionHash(sidecarDnsConfig.Template) + wh.sidecarJavaAgentConfig = sidecarDnsConfig + wh.sidecarJavaAgentTemplateVersion = sidecarTemplateVersionHash(sidecarDnsConfig.Template) wh.valuesConfig = valuesConfig wh.meshConfig = meshConfig @@ -329,135 +384,22 @@ func (wh *Webhook) getCert(*tls.ClientHelloInfo) (*tls.Certificate, error) { // generate RFC6902 JSON patches. Unfortunately, it doesn't produce // correct patches for object removal. Fortunately, our patching needs // are fairly simple so generating them manually isn't horrible (yet). -type rfc6902PatchOperation struct { +type Rfc6902PatchOperation struct { Op string `json:"op"` Path string `json:"path"` Value interface{} `json:"value,omitempty"` } -// JSONPatch `remove` is applied sequentially. Remove items in reverse -// order to avoid renumbering indices. -func removeContainers(containers []corev1.Container, removed []string, path string) (patch []rfc6902PatchOperation) { - names := map[string]bool{} - for _, name := range removed { - names[name] = true - } - for i := len(containers) - 1; i >= 0; i-- { - if _, ok := names[containers[i].Name]; ok { - patch = append(patch, rfc6902PatchOperation{ - Op: "remove", - Path: fmt.Sprintf("%v/%v", path, i), - }) - } - } - return patch -} - -func removeVolumes(volumes []corev1.Volume, removed []string, path string) (patch []rfc6902PatchOperation) { - names := map[string]bool{} - for _, name := range removed { - names[name] = true - } - for i := len(volumes) - 1; i >= 0; i-- { - if _, ok := names[volumes[i].Name]; ok { - patch = append(patch, rfc6902PatchOperation{ - Op: "remove", - Path: fmt.Sprintf("%v/%v", path, i), - }) - } - } - return patch -} - -func removeImagePullSecrets(imagePullSecrets []corev1.LocalObjectReference, removed []string, path string) (patch []rfc6902PatchOperation) { - names := map[string]bool{} - for _, name := range removed { - names[name] = true - } - for i := len(imagePullSecrets) - 1; i >= 0; i-- { - if _, ok := names[imagePullSecrets[i].Name]; ok { - patch = append(patch, rfc6902PatchOperation{ - Op: "remove", - Path: fmt.Sprintf("%v/%v", path, i), - }) - } - } - return patch -} - -func (wh *Webhook) addContainer(sidecarMode utils.SidecarMode, pod *corev1.Pod, target, added []corev1.Container, basePath string) (patch []rfc6902PatchOperation) { - saJwtSecretMountName := "" - var saJwtSecretMount corev1.VolumeMount - // find service account secret volume mount(/var/run/secrets/kubernetes.io/serviceaccount, - // https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/#service-account-automation) from app container - for _, add := range target { - for _, vmount := range add.VolumeMounts { - if vmount.MountPath == "/var/run/secrets/kubernetes.io/serviceaccount" { - saJwtSecretMountName = vmount.Name - saJwtSecretMount = vmount - } - } - } - first := len(target) == 0 - var value interface{} - for _, add := range added { - if add.Name == ProxyContainerName && saJwtSecretMountName != "" { - // add service account secret volume mount(/var/run/secrets/kubernetes.io/serviceaccount, - // https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/#service-account-automation) to istio-proxy container, - // so that envoy could fetch/pass k8s sa jwt and pass to sds server, which will be used to request workload identity for the pod. - add.VolumeMounts = append(add.VolumeMounts, saJwtSecretMount) - } - - // mesh condition || dns condition - if add.Name == "polaris-bootstrap-writer" || add.Name == "polaris-sidecar-init" { - log.InjectScope().Infof("begin to add polaris-sidecar config to int container for pod[%s, %s]", - pod.Namespace, pod.Name) - if err := wh.addPolarisConfigToInitContainerEnv(&add); err != nil { - log.InjectScope().Errorf("begin to add polaris-sidecar config to init container for pod[%s, %s] failed: %v", - pod.Namespace, pod.Name, err) - } - } - - if add.Name == "polaris-sidecar" { - log.InjectScope().Infof("begin deal polaris-sidecar inject for pod=[%s, %s]", pod.Namespace, pod.Name) - if _, err := wh.handlePolarisSidecarEnvInject(sidecarMode, pod, &add); err != nil { - log.InjectScope().Errorf("handle polaris-sidecar inject for pod=[%s, %s] failed: %v", pod.Namespace, pod.Name, err) - } - - //if _, err := wh.handlePolarisSideInject(sidecarMode, pod, &add); err != nil { - // log.InjectScope().Errorf("handle polaris-sidecar inject for pod=[%s, %s] failed: %v", pod.Namespace, pod.Name, err) - //} - // 将刚刚创建好的配置文件挂载到 pod 的 container 中去 - add.VolumeMounts = append(add.VolumeMounts, corev1.VolumeMount{ - Name: utils.PolarisGoConfigFile, - SubPath: "polaris.yaml", - MountPath: "/data/polaris.yaml", - }) - } - - value = add - path := basePath - if first { - first = false - value = []corev1.Container{add} - } else { - path += "/-" - } - patch = append(patch, rfc6902PatchOperation{ - Op: "add", - Path: path, - Value: value, - }) - } - return patch -} - type PolarisGoConfig struct { Name string Namespace string PolarisServer string } +func EnableMtls(pod *corev1.Pod) bool { + return enableMtls(pod) +} + func enableMtls(pod *corev1.Pod) bool { value, ok := pod.Annotations[utils.PolarisTLSMode] if ok && value != utils.MTLSModeNone { @@ -504,52 +446,6 @@ func (wh *Webhook) addPolarisConfigToInitContainerEnv(add *corev1.Container) err return nil } -func (wh *Webhook) handlePolarisSidecarEnvInject( - sidecarMode utils.SidecarMode, pod *corev1.Pod, add *corev1.Container) (bool, error) { - err := wh.ensureRootCertExist(pod) - if err != nil { - return false, err - } - envMap := make(map[string]string) - envMap[EnvSidecarPort] = strconv.Itoa(ValueListenPort) - envMap[EnvSidecarRecurseEnable] = strconv.FormatBool(true) - if sidecarMode == utils.SidecarForDns { - envMap[EnvSidecarDnsEnable] = strconv.FormatBool(true) - envMap[EnvSidecarMeshEnable] = strconv.FormatBool(false) - envMap[EnvSidecarMetricEnable] = strconv.FormatBool(false) - envMap[EnvSidecarMetricListenPort] = strconv.Itoa(ValueMetricListenPort) - } else { - envMap[EnvSidecarDnsEnable] = strconv.FormatBool(false) - envMap[EnvSidecarMeshEnable] = strconv.FormatBool(true) - envMap[EnvSidecarRLSEnable] = strconv.FormatBool(true) - envMap[EnvSidecarMetricEnable] = strconv.FormatBool(true) - envMap[EnvSidecarMetricListenPort] = strconv.Itoa(ValueMetricListenPort) - } - envMap[EnvSidecarLogLevel] = "info" - envMap[EnvSidecarNamespace] = pod.GetNamespace() - envMap[EnvPolarisAddress] = common.PolarisServerGrpcAddress - envMap[EnvSidecarDnsRouteLabels] = buildLabelsStr(pod.Labels) - if enableMtls(pod) { - envMap[EnvSidecarMtlsEnable] = strconv.FormatBool(true) - } - log.InjectScope().Infof("pod=[%s, %s] inject polaris-sidecar mode %s, env map %v", - pod.Namespace, pod.Name, utils.ParseSidecarModeName(sidecarMode), envMap) - for k, v := range envMap { - add.Env = append(add.Env, corev1.EnvVar{Name: k, Value: v}) - } - return true, nil -} - -func buildLabelsStr(labels map[string]string) string { - tags := make([]string, 0, len(labels)) - - for k, v := range labels { - tags = append(tags, fmt.Sprintf("%s:%s", k, v)) - } - - return strings.Join(tags, ",") -} - // currently we assume that polaris-security deploy into polaris-system namespace. const rootNamespace = "polaris-system" @@ -583,8 +479,8 @@ func (wh *Webhook) ensureRootCertExist(pod *corev1.Pod) error { return err } -func addSecurityContext(target *corev1.PodSecurityContext, basePath string) (patch []rfc6902PatchOperation) { - patch = append(patch, rfc6902PatchOperation{ +func addSecurityContext(target *corev1.PodSecurityContext, basePath string) (patch []Rfc6902PatchOperation) { + patch = append(patch, Rfc6902PatchOperation{ Op: "add", Path: basePath, Value: target, @@ -592,50 +488,8 @@ func addSecurityContext(target *corev1.PodSecurityContext, basePath string) (pat return patch } -func addVolume(target, added []corev1.Volume, basePath string) (patch []rfc6902PatchOperation) { - first := len(target) == 0 - var value interface{} - for _, add := range added { - value = add - path := basePath - if first { - first = false - value = []corev1.Volume{add} - } else { - path += "/-" - } - patch = append(patch, rfc6902PatchOperation{ - Op: "add", - Path: path, - Value: value, - }) - } - return patch -} - -func addImagePullSecrets(target, added []corev1.LocalObjectReference, basePath string) (patch []rfc6902PatchOperation) { - first := len(target) == 0 - var value interface{} - for _, add := range added { - value = add - path := basePath - if first { - first = false - value = []corev1.LocalObjectReference{add} - } else { - path += "/-" - } - patch = append(patch, rfc6902PatchOperation{ - Op: "add", - Path: path, - Value: value, - }) - } - return patch -} - -func addPodDNSConfig(target *corev1.PodDNSConfig, basePath string) (patch []rfc6902PatchOperation) { - patch = append(patch, rfc6902PatchOperation{ +func addPodDNSConfig(target *corev1.PodDNSConfig, basePath string) (patch []Rfc6902PatchOperation) { + patch = append(patch, Rfc6902PatchOperation{ Op: "add", Path: basePath, Value: target, @@ -650,8 +504,8 @@ func escapeJSONPointerValue(in string) string { } // adds labels to the target spec, will not overwrite label's value if it already exists -func addLabels(target map[string]string, added map[string]string) []rfc6902PatchOperation { - patches := []rfc6902PatchOperation{} +func addLabels(target map[string]string, added map[string]string) []Rfc6902PatchOperation { + patches := []Rfc6902PatchOperation{} addedKeys := make([]string, 0, len(added)) for key := range added { @@ -665,7 +519,7 @@ func addLabels(target map[string]string, added map[string]string) []rfc6902Patch continue } - patch := rfc6902PatchOperation{ + patch := Rfc6902PatchOperation{ Op: "add", Path: "/metadata/labels/" + escapeJSONPointerValue(key), Value: value, @@ -687,7 +541,7 @@ func addLabels(target map[string]string, added map[string]string) []rfc6902Patch return patches } -func updateAnnotation(target map[string]string, added map[string]string) (patch []rfc6902PatchOperation) { +func updateAnnotation(target map[string]string, added map[string]string) (patch []Rfc6902PatchOperation) { // To ensure deterministic patches, we sort the keys var keys []string for k := range added { @@ -702,7 +556,7 @@ func updateAnnotation(target map[string]string, added map[string]string) (patch } if target == nil { target = map[string]string{} - patch = append(patch, rfc6902PatchOperation{ + patch = append(patch, Rfc6902PatchOperation{ Op: "add", Path: "/metadata/annotations", Value: map[string]string{ @@ -714,7 +568,7 @@ func updateAnnotation(target map[string]string, added map[string]string) (patch if target[key] != "" { op = "replace" } - patch = append(patch, rfc6902PatchOperation{ + patch = append(patch, Rfc6902PatchOperation{ Op: op, Path: "/metadata/annotations/" + escapeJSONPointerValue(key), Value: value, @@ -727,19 +581,113 @@ func updateAnnotation(target map[string]string, added map[string]string) (patch func (wh *Webhook) createPatch(sidecarMode utils.SidecarMode, pod *corev1.Pod, prevStatus *SidecarInjectionStatus, annotations map[string]string, sic *SidecarInjectionSpec, workloadName string, ) ([]byte, error) { - var patch []rfc6902PatchOperation + + var patch []Rfc6902PatchOperation + + patchBuilder, ok := _PatchBuilders[utils.ParseSidecarModeName(sidecarMode)] + if !ok { + return nil, errors.NewInternalError(fmt.Errorf("sidecar-mode %s not found target patch builder", sidecarMode)) + } // Remove any containers previously injected by kube-inject using // container and volume name as unique key for removal. - patch = append(patch, removeContainers(pod.Spec.InitContainers, prevStatus.InitContainers, "/spec/initContainers")...) - patch = append(patch, removeContainers(pod.Spec.Containers, prevStatus.Containers, "/spec/containers")...) - patch = append(patch, removeVolumes(pod.Spec.Volumes, prevStatus.Volumes, "/spec/volumes")...) - patch = append(patch, removeImagePullSecrets(pod.Spec.ImagePullSecrets, prevStatus.ImagePullSecrets, "/spec/imagePullSecrets")...) + removeInitContainerPatch, err := patchBuilder.PatchContainer(&OperateContainerRequest{ + Type: PatchType_Remove, + BasePath: "/spec/initContainers", + Source: pod.Spec.InitContainers, + External: prevStatus.InitContainers, + }) + if err != nil { + return nil, err + } + removeContainerPatch, err := patchBuilder.PatchContainer(&OperateContainerRequest{ + Type: PatchType_Remove, + BasePath: "/spec/containers", + Source: pod.Spec.Containers, + External: prevStatus.Containers, + }) + if err != nil { + return nil, err + } + removeVolumesPatch, err := patchBuilder.PatchVolumes(&OperateVolumesRequest{ + Type: PatchType_Remove, + BasePath: "/spec/volumes", + Source: pod.Spec.Volumes, + External: prevStatus.Volumes, + }) + if err != nil { + return nil, err + } + removeImagePullSecretsPatch, err := patchBuilder.PatchImagePullSecrets(&OperateImagePullSecretsRequest{ + Type: PatchType_Remove, + BasePath: "/spec/imagePullSecrets", + Source: pod.Spec.ImagePullSecrets, + External: prevStatus.ImagePullSecrets, + }) + if err != nil { + return nil, err + } - patch = append(patch, wh.addContainer(sidecarMode, pod, pod.Spec.InitContainers, sic.InitContainers, "/spec/initContainers")...) - patch = append(patch, wh.addContainer(sidecarMode, pod, pod.Spec.Containers, sic.Containers, "/spec/containers")...) - patch = append(patch, addVolume(pod.Spec.Volumes, sic.Volumes, "/spec/volumes")...) - patch = append(patch, addImagePullSecrets(pod.Spec.ImagePullSecrets, sic.ImagePullSecrets, "/spec/imagePullSecrets")...) + // + addInitContainerPatch, err := patchBuilder.PatchContainer(&OperateContainerRequest{ + Type: PatchType_Add, + BasePath: "/spec/initContainers", + Source: pod.Spec.InitContainers, + External: sic.InitContainers, + }) + if err != nil { + return nil, err + } + addContainerPatch, err := patchBuilder.PatchContainer(&OperateContainerRequest{ + Type: PatchType_Add, + BasePath: "/spec/containers", + Source: pod.Spec.Containers, + External: sic.Containers, + }) + if err != nil { + return nil, err + } + updateContainerPatch, err := patchBuilder.PatchContainer(&OperateContainerRequest{ + Type: PatchType_Update, + BasePath: "/spec/containers", + Source: pod.Spec.Containers, + External: sic.Containers, + }) + if err != nil { + return nil, err + } + + addVolumePatch, err := patchBuilder.PatchVolumes(&OperateVolumesRequest{ + Type: PatchType_Add, + BasePath: "/spec/volumes", + Source: pod.Spec.Volumes, + External: sic.Volumes, + }) + if err != nil { + return nil, err + } + addImagePullSecretsPatch, err := patchBuilder.PatchImagePullSecrets(&OperateImagePullSecretsRequest{ + Type: PatchType_Add, + BasePath: "/spec/imagePullSecrets", + Source: pod.Spec.ImagePullSecrets, + External: sic.ImagePullSecrets, + }) + if err != nil { + return nil, err + } + + // + patch = append(patch, removeInitContainerPatch...) + patch = append(patch, removeContainerPatch...) + patch = append(patch, removeVolumesPatch...) + patch = append(patch, removeImagePullSecretsPatch...) + + // + patch = append(patch, addInitContainerPatch...) + patch = append(patch, addContainerPatch...) + patch = append(patch, updateContainerPatch...) + patch = append(patch, addVolumePatch...) + patch = append(patch, addImagePullSecretsPatch...) if sic.DNSConfig != nil { patch = append(patch, addPodDNSConfig(sic.DNSConfig, "/spec/dnsConfig")...) @@ -757,9 +705,9 @@ func (wh *Webhook) createPatch(sidecarMode utils.SidecarMode, pod *corev1.Pod, p // Retain deprecated hardcoded container and volumes names to aid in // backwards compatible migration to the new SidecarInjectionStatus. var ( - legacyInitContainerNames = []string{"istio-init", "enable-core-dump"} - legacyContainerNames = []string{ProxyContainerName} - legacyVolumeNames = []string{"polaris-certs", "polaris-envoy"} + legacyInitContainerNames = []corev1.Container{corev1.Container{Name: "istio-init"}, corev1.Container{Name: "enable-core-dump"}} + legacyContainerNames = []corev1.Container{corev1.Container{Name: ProxyContainerName}} + legacyVolumeNames = []corev1.Volume{corev1.Volume{Name: "polaris-certs"}, corev1.Volume{Name: "polaris-envoy"}} ) func injectionStatus(pod *corev1.Pod) *SidecarInjectionStatus { @@ -773,7 +721,7 @@ func injectionStatus(pod *corev1.Pod) *SidecarInjectionStatus { // default case when injected pod has explicit status var iStatus SidecarInjectionStatus if err := json.Unmarshal(statusBytes, &iStatus); err == nil { - // heuristic assumes status is valid if any of the resource + // heuristic assumes status is valid if any o // lists is non-empty. if len(iStatus.InitContainers) != 0 || len(iStatus.Containers) != 0 || @@ -798,7 +746,7 @@ func toV1AdmissionResponse(err error) *v1.AdmissionResponse { } func toV1beta1AdmissionResponse(err error) *v1beta1.AdmissionResponse { - return &v1beta1.AdmissionResponse{Result: &metav1.Status{Message: err.Error()}} + return &v1beta1.AdmissionResponse{Result: &apismetav1.Status{Message: err.Error()}} } func (wh *Webhook) injectV1beta1(ar *v1beta1.AdmissionReview) *v1beta1.AdmissionResponse { @@ -829,6 +777,10 @@ func (wh *Webhook) injectV1beta1(ar *v1beta1.AdmissionReview) *v1beta1.Admission config = wh.sidecarDnsConfig tempVersion = wh.sidecarDnsTemplateVersion } + if sidecarMode == utils.SidecarForJavaAgent { + config = wh.sidecarJavaAgentConfig + tempVersion = wh.sidecarJavaAgentTemplateVersion + } if !wh.injectRequired(ignoredNamespaces, config, &pod.Spec, &pod.ObjectMeta) { log.InjectScope().Infof("Skipping %s/%s due to policy check", pod.ObjectMeta.Namespace, podName) diff --git a/pkg/util/types.go b/pkg/util/types.go index 58966a54..cd6c8f65 100644 --- a/pkg/util/types.go +++ b/pkg/util/types.go @@ -15,6 +15,10 @@ package util +const ( + RootNamespace = "polaris-system" +) + const ( PolarisSync = "polarismesh.cn/sync" PolarisEnableRegister = "polarismesh.cn/enableRegister" @@ -135,9 +139,11 @@ const ( SidecarForUnknown SidecarMode = iota SidecarForMesh SidecarForDns + SidecarForJavaAgent - SidecarMeshModeName string = "mesh" - SidecarDnsModeName string = "dns" + SidecarMeshModeName string = "mesh" + SidecarDnsModeName string = "dns" + SidecarJavaAgentModeName string = "java-agent" ) func ParseSidecarMode(val string) SidecarMode { @@ -147,6 +153,9 @@ func ParseSidecarMode(val string) SidecarMode { if val == SidecarDnsModeName { return SidecarForDns } + if val == SidecarJavaAgentModeName { + return SidecarForJavaAgent + } return SidecarForMesh } @@ -157,6 +166,9 @@ func ParseSidecarModeName(mode SidecarMode) string { if mode == SidecarForDns { return SidecarDnsModeName } + if mode == SidecarForJavaAgent { + return SidecarJavaAgentModeName + } return SidecarMeshModeName } diff --git a/sidecar/envoy-bootstrap-config-generator/Dockerfile b/sidecar/envoy-bootstrap-config-generator/Dockerfile index c847181f..f61a0370 100644 --- a/sidecar/envoy-bootstrap-config-generator/Dockerfile +++ b/sidecar/envoy-bootstrap-config-generator/Dockerfile @@ -24,4 +24,4 @@ COPY start.sh /start.sh RUN mkdir -p /logs && chmod 777 /logs RUN ["chmod", "+x", "/start.sh"] -ENTRYPOINT ["/bin/bash", "-c", "/start.sh"] \ No newline at end of file +ENTRYPOINT ["/bin/bash", "-c", "/start.sh"]