You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by GitBox <gi...@apache.org> on 2021/08/15 14:47:57 UTC

[GitHub] [skywalking-swck] hanahmily commented on a change in pull request #30: add feature: java agent injector

hanahmily commented on a change in pull request #30:
URL: https://github.com/apache/skywalking-swck/pull/30#discussion_r689086039



##########
File path: apis/operator/v1alpha1/oapserver_webhook.go
##########
@@ -37,6 +37,11 @@ func (r *OAPServer) SetupWebhookWithManager(mgr ctrl.Manager) error {
 		Complete()
 }
 
+// nolint: lll

Review comment:
       Please move this marker to a separate webhook go file, it doesn't belong to oapserver. 
   The file name might be `javaagent_webhook.go`

##########
File path: pkg/operator/injector/injector.go
##########
@@ -0,0 +1,487 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 injector
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strings"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+)
+
+const (
+	labelKeyagentInjector       = "swck-java-agent-injected"
+	defaultConfigmapNamespace   = "skywalking-swck-system"
+	agentConfigAnnotationPrefix = "agent.skywalking.apache.org/"
+	// If user want to use other Plugins' config ,the annotation must have the following form
+	// plugins.skywalking.apache.org/${config.name} = ${config.value}
+	// for example , if user want to enable plugin.mongodb.trace_param
+	// the annotation is plugins.skywalking.apache.org/plugin.mongodb.trace_param: "true"
+	otherPluginsAnnotationPrefix = "plugins.skywalking.apache.org/"
+)
+
+// log is for logging in this package.
+var log = logf.Log.WithName("injector")
+
+// SidecarInjectField contains all info that will be injected
+type SidecarInjectField struct {
+	// determine whether to inject , default is not to inject
+	needInject bool
+	// determine whether to use annotation to overlay agent config ,
+	// default is not to overlay,which means only use configmap to set agent config
+	// Otherwise, the way to overlay is set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar={config1}={value1},{config2}={value2}
+	agentOverlay bool
+	// initcontainer is a container that has the agent folder
+	initcontainer corev1.Container
+	// sidecarVolume is a shared directory between App's container and initcontainer
+	sidecarVolume corev1.Volume
+	// sidecarVolumeMount is a path that specifies a shared directory
+	sidecarVolumeMount corev1.VolumeMount
+	// configmapVolume is volume that provide user with configmap
+	configmapVolume corev1.Volume
+	// configmapVolumeMount is the configmap's mountpath for user
+	// Notice : the mount path will overwrite the original agent/config/agent.config
+	// So the mount path must match the path of agent.config in the image
+	configmapVolumeMount corev1.VolumeMount
+	// env is used to set java agent’s parameters
+	env corev1.EnvVar
+	// the string is used to set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar=jvmAgentConfigStr
+	jvmAgentConfigStr string
+	// determine which container to inject , default is to inject all containers
+	injectContainer string
+}
+
+// NewSidecarInjectField will create a new SidecarInjectField
+func NewSidecarInjectField() *SidecarInjectField {
+	return new(SidecarInjectField)
+}
+
+// Inject will do real injection
+func (s *SidecarInjectField) Inject(pod *corev1.Pod) {
+	// add initcontrainers to spec
+	if pod.Spec.InitContainers != nil {
+		pod.Spec.InitContainers = append(pod.Spec.InitContainers, s.initcontainer)
+	} else {
+		pod.Spec.InitContainers = []corev1.Container{s.initcontainer}
+	}
+
+	// add volume to spec
+	if pod.Spec.Volumes != nil {
+		pod.Spec.Volumes = append(pod.Spec.Volumes, s.sidecarVolume, s.configmapVolume)
+	} else {
+		pod.Spec.Volumes = []corev1.Volume{s.sidecarVolume, s.configmapVolume}
+	}
+
+	// add volumemount and env to container
+	for i, c := range pod.Spec.Containers {
+		// choose a specific container to inject
+		if s.injectContainer != "" && c.Name == s.injectContainer {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+			break
+		} else {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+		}
+	}
+}
+
+// GetInjectStrategy gets user's injection strategy
+func (s *SidecarInjectField) GetInjectStrategy(containers []corev1.Container, labels,
+	annotation *map[string]string) {
+	// set default value
+	s.needInject = false
+	s.injectContainer = ""
+	s.agentOverlay = false
+
+	// set needInject's value , if the pod has the label "swck-java-agent-injected=true",means need inject
+	if *labels == nil {
+		labels = new(map[string]string)
+		s.needInject = false
+	}
+
+	switch strings.ToLower((*labels)[labelKeyagentInjector]) {
+	case "true":
+		s.needInject = true
+	}
+
+	// set injectContainer's value
+	if *annotation == nil {
+		annotation = new(map[string]string)
+		s.injectContainer = ""
+		s.agentOverlay = false
+	}
+
+	// validate the container is or not exist
+	if v, ok := (*annotation)[AnnoInjectContainerName.Name]; ok {
+		for _, c := range containers {
+			if c.Name == v {
+				s.injectContainer = v
+				break
+			}
+		}
+	}
+
+	// set agentOverlay's value
+	switch strings.ToLower((*annotation)[AnnoAgentConfigOverlay.Name]) {
+	case "true":
+		s.agentOverlay = true
+	}
+
+}
+
+func (s *SidecarInjectField) injectErrorAnnotation(annotation *map[string]string, errorInfo string) {
+	(*annotation)[AnnoInjectErrorInfo.Name] = errorInfo
+}
+
+// SidecarOverlayandGetValue get final value of sidecar
+func (s *SidecarInjectField) SidecarOverlayandGetValue(as *Annotations, annotation *map[string]string,
+	a Annotation) (string, bool) {
+	if _, ok := (*annotation)[a.Name]; ok {
+		err := as.SetOverlay(annotation, a)
+		if err != nil {
+			s.injectErrorAnnotation(annotation, err.Error())
+			return "", false
+		}
+	}
+	return as.GetFinalValue(a), true
+}
+
+func (s *SidecarInjectField) setValue(config *string, as *Annotations, annotation *map[string]string,
+	a Annotation) bool {
+	if v, ok := s.SidecarOverlayandGetValue(as, annotation, a); ok {
+		*config = v
+		return true
+	}
+	return false
+}
+
+// OverlaySidecar overlays default config
+func (s *SidecarInjectField) OverlaySidecar(as *Annotations, annotation *map[string]string) bool {
+	var (
+		command       string
+		argOption     string
+		argCommand    string
+		configmapName string
+	)
+
+	switch {
+	case !s.setValue(&s.initcontainer.Name, as, annotation, AnnoInitcontainerName):
+		return false
+	case !s.setValue(&s.initcontainer.Image, as, annotation, AnnoInitcontainerImage):
+		return false
+	case !s.setValue(&command, as, annotation, AnnoInitcontainerCommand):
+		return false
+	case !s.setValue(&argOption, as, annotation, AnnoInitcontainerArgsOption):
+		return false
+	case !s.setValue(&argCommand, as, annotation, AnnoInitcontainerArgsCommand):
+		return false
+	case !s.setValue(&s.sidecarVolume.Name, as, annotation, AnnoSidecarVolumeName):
+		return false
+	case !s.setValue(&s.sidecarVolumeMount.Name, as, annotation, AnnoSidecarVolumeName):
+		return false
+	case !s.setValue(&s.sidecarVolumeMount.MountPath, as, annotation, AnnoSidecarVolumemountMountpath):
+		return false
+	case !s.setValue(&configmapName, as, annotation, AnnoConfigmapName):
+		return false
+	case !s.setValue(&s.configmapVolume.Name, as, annotation, AnnoConfigmapVolumeName):
+		return false
+	case !s.setValue(&s.configmapVolumeMount.Name, as, annotation, AnnoConfigmapVolumeName):
+		return false
+	case !s.setValue(&s.configmapVolumeMount.MountPath, as, annotation, AnnoConfigmapVolumemountMountpath):
+		return false
+	case !s.setValue(&s.env.Name, as, annotation, AnnoEnvVarName):
+		return false
+	case !s.setValue(&s.env.Value, as, annotation, AnnoEnvVarValue):
+		return false
+	}
+
+	s.initcontainer.Command = []string{command}
+	s.initcontainer.Args = []string{argOption, argCommand}
+	s.initcontainer.VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount}
+
+	// the sidecar volume's type is determined
+	s.sidecarVolume.VolumeSource.EmptyDir = nil
+
+	s.configmapVolume.VolumeSource = corev1.VolumeSource{
+		ConfigMap: &corev1.ConfigMapVolumeSource{
+			LocalObjectReference: corev1.LocalObjectReference{
+				Name: configmapName,
+			},
+		},
+	}
+
+	return true
+}
+
+// AgentOverlayandGetValue will do real annotation overlay
+func (s *SidecarInjectField) AgentOverlayandGetValue(as *Annotations, annotation *map[string]string,
+	a Annotation) (string, bool) {
+	if _, ok := (*annotation)[a.Name]; ok {
+		err := as.SetOverlay(annotation, a)
+		if err != nil {
+			s.injectErrorAnnotation(annotation, err.Error())
+			return "", false
+		}
+	}
+	return as.GetOverlayValue(a), true
+}
+
+func (s *SidecarInjectField) setJvmAgentStr(as *Annotations, annotation *map[string]string, a Annotation,
+	specialStr bool) bool {
+	v, ok := s.AgentOverlayandGetValue(as, annotation, a)
+	if v != "" && ok {
+		// get {config1}={value1}
+		configName := strings.TrimPrefix(a.Name, agentConfigAnnotationPrefix)
+		if specialStr {
+			v = strings.Join([]string{"'", "'"}, v)
+		}
+		config := strings.Join([]string{configName, v}, "=")
+
+		// add to jvmAgentConfigStr
+		if s.jvmAgentConfigStr != "" {
+			s.jvmAgentConfigStr = strings.Join([]string{s.jvmAgentConfigStr, config}, ",")
+		} else {
+			s.jvmAgentConfigStr = config
+		}
+	}
+	return ok
+}
+
+// OverlayAgentConfig overlays agent config
+func (s *SidecarInjectField) OverlayAgentConfig(as *Annotations, annotation *map[string]string) bool {
+	// jvmAgentConfigStr init
+	s.jvmAgentConfigStr = ""
+
+	switch {
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentNamespace, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentServiceName, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentSampleNumber, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentAuthentication, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentSpanLimit, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentIgnoreSuffix, true):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentIsOpenDebugging, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentIsCacheEnhaned, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentClassCache, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentOperationName, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentForceTLS, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileActive, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileMaxParallel, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileMaxDuration, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileDump, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileSnapshot, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentCollectorService, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingName, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingLevel, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingDir, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingMaxSize, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingMaxFiles, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentStatuscheckExceptions, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentStatuscheckDepth, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginMount, true):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentExcludePlugins, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginJdbc, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginKafkaServers, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginKafkaNamespace, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginSpringannotation, false):
+		return false
+	}
+
+	return true
+}
+
+// OverlayPluginsConfig will add Plugins' config to JvmAgentStr without verification
+// Notice, if a config is not in agent.config, it will be seen as a plugin config
+// user must ensure the accuracy of configuration.
+// Otherwides,If a separator(, or =) in the option or value, it should be wrapped in quotes.
+func (s *SidecarInjectField) OverlayPluginsConfig(annotation *map[string]string) bool {
+	for k, v := range *annotation {
+		if strings.HasPrefix(k, otherPluginsAnnotationPrefix) {
+			configName := strings.TrimPrefix(k, otherPluginsAnnotationPrefix)
+			config := strings.Join([]string{configName, v}, "=")
+			// add to jvmAgentConfigStr
+			if s.jvmAgentConfigStr != "" {
+				s.jvmAgentConfigStr = strings.Join([]string{s.jvmAgentConfigStr, config}, ",")
+			} else {
+				s.jvmAgentConfigStr = config
+			}
+		}
+	}
+	return true
+}
+
+// CreateConfigmap will find a configmap to set agent config
+// if not exist , then create a configmap
+func (s *SidecarInjectField) CreateConfigmap(ctx context.Context, kubeclient client.Client, namespace string,
+	annotation *map[string]string) bool {
+	configmap := &corev1.ConfigMap{}
+	configmapName := s.configmapVolume.VolumeSource.ConfigMap.LocalObjectReference.Name
+	// check whether the configmap is existed
+	err := kubeclient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: configmapName}, configmap)
+	// if configmap not exist , get configmap from defaultConfigmapNamespace
+	if err != nil {
+		defaultConfigmap := &corev1.ConfigMap{}
+		if err := kubeclient.Get(ctx, client.ObjectKey{Namespace: defaultConfigmapNamespace,
+			Name: AnnoConfigmapName.DefaultValue}, defaultConfigmap); err != nil {
+			log.Info(err.Error())
+			s.injectErrorAnnotation(annotation, fmt.Sprintf("get configmap %s from namespace %s error[%s]",
+				AnnoConfigmapName.DefaultValue, defaultConfigmapNamespace, err.Error()))
+			return false
+		}
+		// create new configmap and update namespace
+		injectConfigmap := corev1.ConfigMap{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      configmapName,
+				Namespace: namespace,
+			},
+			Data: defaultConfigmap.Data,
+		}
+
+		if err := kubeclient.Create(ctx, &injectConfigmap); err != nil {
+			log.Info(err.Error())
+			s.injectErrorAnnotation(annotation, fmt.Sprintf("create configmap %s in namespace %s error[%s]",
+				configmapName, namespace, err.Error()))
+			return false
+		}
+	}
+	return true
+}
+
+// PodInjector injects agent into Pods
+type PodInjector struct {
+	Client  client.Client
+	decoder *admission.Decoder
+}
+
+// Handle will process every coming pod under the
+// specified namespace which labeled "swck-injection=enabled"
+func (r *PodInjector) Handle(ctx context.Context, req admission.Request) admission.Response {
+	pod := &corev1.Pod{}
+
+	err := r.decoder.Decode(req, pod)
+	if err != nil {
+		return admission.Errored(http.StatusBadRequest, err)
+	}
+
+	// init Annotations to store the overlaied value
+	as := NewAnnotations()
+
+	// init SidecarInjectField and get injected strategy from annotations
+	s := NewSidecarInjectField()
+	s.GetInjectStrategy(pod.Spec.Containers, &pod.ObjectMeta.Labels, &pod.ObjectMeta.Annotations)
+
+	if s.needInject {
+		log.Info("will inject agent,please wait for a moment!")
+		// the overlation function is always on
+		s.OverlaySidecar(as, &pod.ObjectMeta.Annotations)
+		switch {
+		// only if user choose to overlay agent config in annotations, then start agent and plugins' overlay
+		case s.agentOverlay && (!s.OverlayAgentConfig(as, &pod.ObjectMeta.Annotations) ||
+			!s.OverlayPluginsConfig(&pod.ObjectMeta.Annotations)):
+			log.Info("overlay agent config error!please look the error annotation!")
+		// configmap will always overwrite the original agent.config in image
+		case !s.CreateConfigmap(ctx, r.Client, req.Namespace, &pod.ObjectMeta.Annotations):
+			log.Info("create configmap error!please look the error annotation!")
+		// add jvmStr to EnvVar and do real injection
+		default:
+			if s.jvmAgentConfigStr != "" {
+				s.env.Value = strings.Join([]string{s.env.Value, s.jvmAgentConfigStr}, "=")
+			}
+			s.Inject(pod)
+			log.Info("inject successfully!")
+		}

Review comment:
       This piece is hard to understand, I have to read your comment to know the logic, that means ppl can't maintain it.
   
   I suggest using `if-else` or https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern to refactor them.

##########
File path: cmd/manager/manager.go
##########
@@ -116,6 +118,12 @@ func main() {
 			setupLog.Error(err, "unable to create webhook", "webhook", "Fetcher")
 			os.Exit(1)
 		}
+		// register a webhook to enable the agent injector,
+		setupLog.Info("registering /mutate-v1-pod webhook")
+		mgr.GetWebhookServer().Register("/mutate-v1-pod",

Review comment:
       Pls refer to other webhooks to register injection webhoook

##########
File path: main.go
##########
@@ -26,10 +26,12 @@ import (
 	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"

Review comment:
       Please leave this file alone. I should remove it which is used as template to write `cmd/manager/manager.go`
   
   As I mentioned above, you should use `kubebuilder create webhook` to create the injection webhoook. This command will generate main.go, then you should copy necessary pieces from it to `cmd/manager/manager.go`

##########
File path: config/operator/webhook/kustomization.yaml
##########
@@ -19,6 +19,9 @@ resources:
 - service.yaml
 - manifests.yaml
 
+patchesStrategicMerge:
+- add_nsSelector.yaml

Review comment:
       It's nice to use patches to active the name space selector since kubebuilder marker doesn't include it.
   
   How about rename it to a snake style name? `ns_selector_patch.yaml` makes more sense to me.

##########
File path: pkg/operator/injector/annotation.go
##########
@@ -0,0 +1,134 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 injector
+
+//Annotation is used to set default value
+type Annotation struct {
+	Name         string
+	DefaultValue string
+}
+
+//Annotations is used to set overlaied value
+type Annotations map[Annotation]string
+
+func setDefaultAnnotation(name, value string) Annotation {
+	return Annotation{
+		Name:         name,
+		DefaultValue: value,
+	}
+}
+
+const (
+	namePrefix = "skywalking-swck-"
+)
+
+// nolint
+var (
+	//set sidecar's basic information
+	AnnoInitcontainerName  = setDefaultAnnotation(SidecarInitcontainerName, "inject-sky-agent")

Review comment:
       To pick up `sw` or `skywalking` instead of `sky`.

##########
File path: pkg/operator/injector/annotation.go
##########
@@ -0,0 +1,134 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 injector
+
+//Annotation is used to set default value
+type Annotation struct {
+	Name         string
+	DefaultValue string
+}
+
+//Annotations is used to set overlaied value
+type Annotations map[Annotation]string
+
+func setDefaultAnnotation(name, value string) Annotation {
+	return Annotation{
+		Name:         name,
+		DefaultValue: value,
+	}
+}
+
+const (
+	namePrefix = "skywalking-swck-"
+)
+
+// nolint
+var (
+	//set sidecar's basic information
+	AnnoInitcontainerName  = setDefaultAnnotation(SidecarInitcontainerName, "inject-sky-agent")
+	AnnoInitcontainerImage = setDefaultAnnotation(SidecarInitcontainerImage,
+		"apache/skywalking-java-agent:8.6.0-jdk8")

Review comment:
       Update the version to `8.7.0`

##########
File path: pkg/operator/injector/injector.go
##########
@@ -0,0 +1,487 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 injector
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strings"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+)
+
+const (
+	labelKeyagentInjector       = "swck-java-agent-injected"
+	defaultConfigmapNamespace   = "skywalking-swck-system"
+	agentConfigAnnotationPrefix = "agent.skywalking.apache.org/"
+	// If user want to use other Plugins' config ,the annotation must have the following form
+	// plugins.skywalking.apache.org/${config.name} = ${config.value}
+	// for example , if user want to enable plugin.mongodb.trace_param
+	// the annotation is plugins.skywalking.apache.org/plugin.mongodb.trace_param: "true"
+	otherPluginsAnnotationPrefix = "plugins.skywalking.apache.org/"
+)
+
+// log is for logging in this package.
+var log = logf.Log.WithName("injector")
+
+// SidecarInjectField contains all info that will be injected
+type SidecarInjectField struct {
+	// determine whether to inject , default is not to inject
+	needInject bool
+	// determine whether to use annotation to overlay agent config ,
+	// default is not to overlay,which means only use configmap to set agent config
+	// Otherwise, the way to overlay is set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar={config1}={value1},{config2}={value2}
+	agentOverlay bool
+	// initcontainer is a container that has the agent folder
+	initcontainer corev1.Container
+	// sidecarVolume is a shared directory between App's container and initcontainer
+	sidecarVolume corev1.Volume
+	// sidecarVolumeMount is a path that specifies a shared directory
+	sidecarVolumeMount corev1.VolumeMount
+	// configmapVolume is volume that provide user with configmap
+	configmapVolume corev1.Volume
+	// configmapVolumeMount is the configmap's mountpath for user
+	// Notice : the mount path will overwrite the original agent/config/agent.config
+	// So the mount path must match the path of agent.config in the image
+	configmapVolumeMount corev1.VolumeMount
+	// env is used to set java agent’s parameters
+	env corev1.EnvVar
+	// the string is used to set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar=jvmAgentConfigStr
+	jvmAgentConfigStr string
+	// determine which container to inject , default is to inject all containers
+	injectContainer string
+}
+
+// NewSidecarInjectField will create a new SidecarInjectField
+func NewSidecarInjectField() *SidecarInjectField {
+	return new(SidecarInjectField)
+}
+
+// Inject will do real injection
+func (s *SidecarInjectField) Inject(pod *corev1.Pod) {
+	// add initcontrainers to spec
+	if pod.Spec.InitContainers != nil {
+		pod.Spec.InitContainers = append(pod.Spec.InitContainers, s.initcontainer)
+	} else {
+		pod.Spec.InitContainers = []corev1.Container{s.initcontainer}
+	}
+
+	// add volume to spec
+	if pod.Spec.Volumes != nil {
+		pod.Spec.Volumes = append(pod.Spec.Volumes, s.sidecarVolume, s.configmapVolume)
+	} else {
+		pod.Spec.Volumes = []corev1.Volume{s.sidecarVolume, s.configmapVolume}
+	}
+
+	// add volumemount and env to container
+	for i, c := range pod.Spec.Containers {
+		// choose a specific container to inject
+		if s.injectContainer != "" && c.Name == s.injectContainer {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+			break
+		} else {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+		}
+	}
+}
+
+// GetInjectStrategy gets user's injection strategy
+func (s *SidecarInjectField) GetInjectStrategy(containers []corev1.Container, labels,
+	annotation *map[string]string) {
+	// set default value
+	s.needInject = false
+	s.injectContainer = ""
+	s.agentOverlay = false
+
+	// set needInject's value , if the pod has the label "swck-java-agent-injected=true",means need inject
+	if *labels == nil {
+		labels = new(map[string]string)
+		s.needInject = false

Review comment:
       Can we return here? looks like we don't need go through the below piece.

##########
File path: pkg/operator/injector/injector.go
##########
@@ -0,0 +1,487 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 injector
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strings"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+)
+
+const (
+	labelKeyagentInjector       = "swck-java-agent-injected"
+	defaultConfigmapNamespace   = "skywalking-swck-system"
+	agentConfigAnnotationPrefix = "agent.skywalking.apache.org/"
+	// If user want to use other Plugins' config ,the annotation must have the following form
+	// plugins.skywalking.apache.org/${config.name} = ${config.value}
+	// for example , if user want to enable plugin.mongodb.trace_param
+	// the annotation is plugins.skywalking.apache.org/plugin.mongodb.trace_param: "true"
+	otherPluginsAnnotationPrefix = "plugins.skywalking.apache.org/"
+)
+
+// log is for logging in this package.
+var log = logf.Log.WithName("injector")
+
+// SidecarInjectField contains all info that will be injected
+type SidecarInjectField struct {
+	// determine whether to inject , default is not to inject
+	needInject bool
+	// determine whether to use annotation to overlay agent config ,
+	// default is not to overlay,which means only use configmap to set agent config
+	// Otherwise, the way to overlay is set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar={config1}={value1},{config2}={value2}
+	agentOverlay bool
+	// initcontainer is a container that has the agent folder
+	initcontainer corev1.Container
+	// sidecarVolume is a shared directory between App's container and initcontainer
+	sidecarVolume corev1.Volume
+	// sidecarVolumeMount is a path that specifies a shared directory
+	sidecarVolumeMount corev1.VolumeMount
+	// configmapVolume is volume that provide user with configmap
+	configmapVolume corev1.Volume
+	// configmapVolumeMount is the configmap's mountpath for user
+	// Notice : the mount path will overwrite the original agent/config/agent.config
+	// So the mount path must match the path of agent.config in the image
+	configmapVolumeMount corev1.VolumeMount
+	// env is used to set java agent’s parameters
+	env corev1.EnvVar
+	// the string is used to set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar=jvmAgentConfigStr
+	jvmAgentConfigStr string
+	// determine which container to inject , default is to inject all containers
+	injectContainer string
+}
+
+// NewSidecarInjectField will create a new SidecarInjectField
+func NewSidecarInjectField() *SidecarInjectField {
+	return new(SidecarInjectField)
+}
+
+// Inject will do real injection
+func (s *SidecarInjectField) Inject(pod *corev1.Pod) {
+	// add initcontrainers to spec
+	if pod.Spec.InitContainers != nil {
+		pod.Spec.InitContainers = append(pod.Spec.InitContainers, s.initcontainer)
+	} else {
+		pod.Spec.InitContainers = []corev1.Container{s.initcontainer}
+	}
+
+	// add volume to spec
+	if pod.Spec.Volumes != nil {
+		pod.Spec.Volumes = append(pod.Spec.Volumes, s.sidecarVolume, s.configmapVolume)
+	} else {
+		pod.Spec.Volumes = []corev1.Volume{s.sidecarVolume, s.configmapVolume}
+	}
+
+	// add volumemount and env to container
+	for i, c := range pod.Spec.Containers {
+		// choose a specific container to inject
+		if s.injectContainer != "" && c.Name == s.injectContainer {

Review comment:
       Supposing the first container is not the `injectContainer`, does it get injected? If true, it should be a bug.

##########
File path: pkg/operator/injector/injector.go
##########
@@ -0,0 +1,487 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 injector
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strings"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+)
+
+const (
+	labelKeyagentInjector       = "swck-java-agent-injected"
+	defaultConfigmapNamespace   = "skywalking-swck-system"
+	agentConfigAnnotationPrefix = "agent.skywalking.apache.org/"
+	// If user want to use other Plugins' config ,the annotation must have the following form
+	// plugins.skywalking.apache.org/${config.name} = ${config.value}
+	// for example , if user want to enable plugin.mongodb.trace_param
+	// the annotation is plugins.skywalking.apache.org/plugin.mongodb.trace_param: "true"
+	otherPluginsAnnotationPrefix = "plugins.skywalking.apache.org/"
+)
+
+// log is for logging in this package.
+var log = logf.Log.WithName("injector")
+
+// SidecarInjectField contains all info that will be injected
+type SidecarInjectField struct {
+	// determine whether to inject , default is not to inject
+	needInject bool
+	// determine whether to use annotation to overlay agent config ,
+	// default is not to overlay,which means only use configmap to set agent config
+	// Otherwise, the way to overlay is set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar={config1}={value1},{config2}={value2}
+	agentOverlay bool
+	// initcontainer is a container that has the agent folder
+	initcontainer corev1.Container
+	// sidecarVolume is a shared directory between App's container and initcontainer
+	sidecarVolume corev1.Volume
+	// sidecarVolumeMount is a path that specifies a shared directory
+	sidecarVolumeMount corev1.VolumeMount
+	// configmapVolume is volume that provide user with configmap
+	configmapVolume corev1.Volume
+	// configmapVolumeMount is the configmap's mountpath for user
+	// Notice : the mount path will overwrite the original agent/config/agent.config
+	// So the mount path must match the path of agent.config in the image
+	configmapVolumeMount corev1.VolumeMount
+	// env is used to set java agent’s parameters
+	env corev1.EnvVar
+	// the string is used to set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar=jvmAgentConfigStr
+	jvmAgentConfigStr string
+	// determine which container to inject , default is to inject all containers
+	injectContainer string
+}
+
+// NewSidecarInjectField will create a new SidecarInjectField
+func NewSidecarInjectField() *SidecarInjectField {
+	return new(SidecarInjectField)
+}
+
+// Inject will do real injection
+func (s *SidecarInjectField) Inject(pod *corev1.Pod) {
+	// add initcontrainers to spec
+	if pod.Spec.InitContainers != nil {
+		pod.Spec.InitContainers = append(pod.Spec.InitContainers, s.initcontainer)
+	} else {
+		pod.Spec.InitContainers = []corev1.Container{s.initcontainer}
+	}
+
+	// add volume to spec
+	if pod.Spec.Volumes != nil {
+		pod.Spec.Volumes = append(pod.Spec.Volumes, s.sidecarVolume, s.configmapVolume)
+	} else {
+		pod.Spec.Volumes = []corev1.Volume{s.sidecarVolume, s.configmapVolume}
+	}
+
+	// add volumemount and env to container
+	for i, c := range pod.Spec.Containers {
+		// choose a specific container to inject
+		if s.injectContainer != "" && c.Name == s.injectContainer {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+			break
+		} else {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+		}
+	}
+}
+
+// GetInjectStrategy gets user's injection strategy
+func (s *SidecarInjectField) GetInjectStrategy(containers []corev1.Container, labels,
+	annotation *map[string]string) {
+	// set default value
+	s.needInject = false
+	s.injectContainer = ""
+	s.agentOverlay = false
+
+	// set needInject's value , if the pod has the label "swck-java-agent-injected=true",means need inject
+	if *labels == nil {
+		labels = new(map[string]string)
+		s.needInject = false
+	}
+
+	switch strings.ToLower((*labels)[labelKeyagentInjector]) {
+	case "true":
+		s.needInject = true
+	}
+
+	// set injectContainer's value
+	if *annotation == nil {
+		annotation = new(map[string]string)
+		s.injectContainer = ""
+		s.agentOverlay = false
+	}
+
+	// validate the container is or not exist
+	if v, ok := (*annotation)[AnnoInjectContainerName.Name]; ok {
+		for _, c := range containers {
+			if c.Name == v {
+				s.injectContainer = v
+				break
+			}
+		}
+	}
+
+	// set agentOverlay's value
+	switch strings.ToLower((*annotation)[AnnoAgentConfigOverlay.Name]) {
+	case "true":
+		s.agentOverlay = true
+	}
+
+}
+
+func (s *SidecarInjectField) injectErrorAnnotation(annotation *map[string]string, errorInfo string) {
+	(*annotation)[AnnoInjectErrorInfo.Name] = errorInfo
+}
+
+// SidecarOverlayandGetValue get final value of sidecar
+func (s *SidecarInjectField) SidecarOverlayandGetValue(as *Annotations, annotation *map[string]string,
+	a Annotation) (string, bool) {
+	if _, ok := (*annotation)[a.Name]; ok {
+		err := as.SetOverlay(annotation, a)
+		if err != nil {
+			s.injectErrorAnnotation(annotation, err.Error())
+			return "", false
+		}
+	}
+	return as.GetFinalValue(a), true
+}
+
+func (s *SidecarInjectField) setValue(config *string, as *Annotations, annotation *map[string]string,
+	a Annotation) bool {
+	if v, ok := s.SidecarOverlayandGetValue(as, annotation, a); ok {
+		*config = v
+		return true
+	}
+	return false
+}
+
+// OverlaySidecar overlays default config
+func (s *SidecarInjectField) OverlaySidecar(as *Annotations, annotation *map[string]string) bool {
+	var (
+		command       string
+		argOption     string
+		argCommand    string
+		configmapName string
+	)
+
+	switch {
+	case !s.setValue(&s.initcontainer.Name, as, annotation, AnnoInitcontainerName):

Review comment:
       Too many branches here. A common way is to store annotations into a `slice`, loop which to validate them. 

##########
File path: pkg/operator/injector/injector.go
##########
@@ -0,0 +1,487 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 injector
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strings"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+)
+
+const (
+	labelKeyagentInjector       = "swck-java-agent-injected"
+	defaultConfigmapNamespace   = "skywalking-swck-system"
+	agentConfigAnnotationPrefix = "agent.skywalking.apache.org/"
+	// If user want to use other Plugins' config ,the annotation must have the following form
+	// plugins.skywalking.apache.org/${config.name} = ${config.value}
+	// for example , if user want to enable plugin.mongodb.trace_param
+	// the annotation is plugins.skywalking.apache.org/plugin.mongodb.trace_param: "true"
+	otherPluginsAnnotationPrefix = "plugins.skywalking.apache.org/"
+)
+
+// log is for logging in this package.
+var log = logf.Log.WithName("injector")
+
+// SidecarInjectField contains all info that will be injected
+type SidecarInjectField struct {
+	// determine whether to inject , default is not to inject
+	needInject bool
+	// determine whether to use annotation to overlay agent config ,
+	// default is not to overlay,which means only use configmap to set agent config
+	// Otherwise, the way to overlay is set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar={config1}={value1},{config2}={value2}
+	agentOverlay bool
+	// initcontainer is a container that has the agent folder
+	initcontainer corev1.Container
+	// sidecarVolume is a shared directory between App's container and initcontainer
+	sidecarVolume corev1.Volume
+	// sidecarVolumeMount is a path that specifies a shared directory
+	sidecarVolumeMount corev1.VolumeMount
+	// configmapVolume is volume that provide user with configmap
+	configmapVolume corev1.Volume
+	// configmapVolumeMount is the configmap's mountpath for user
+	// Notice : the mount path will overwrite the original agent/config/agent.config
+	// So the mount path must match the path of agent.config in the image
+	configmapVolumeMount corev1.VolumeMount
+	// env is used to set java agent’s parameters
+	env corev1.EnvVar
+	// the string is used to set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar=jvmAgentConfigStr
+	jvmAgentConfigStr string
+	// determine which container to inject , default is to inject all containers
+	injectContainer string
+}
+
+// NewSidecarInjectField will create a new SidecarInjectField
+func NewSidecarInjectField() *SidecarInjectField {
+	return new(SidecarInjectField)
+}
+
+// Inject will do real injection
+func (s *SidecarInjectField) Inject(pod *corev1.Pod) {
+	// add initcontrainers to spec
+	if pod.Spec.InitContainers != nil {
+		pod.Spec.InitContainers = append(pod.Spec.InitContainers, s.initcontainer)
+	} else {
+		pod.Spec.InitContainers = []corev1.Container{s.initcontainer}
+	}
+
+	// add volume to spec
+	if pod.Spec.Volumes != nil {
+		pod.Spec.Volumes = append(pod.Spec.Volumes, s.sidecarVolume, s.configmapVolume)
+	} else {
+		pod.Spec.Volumes = []corev1.Volume{s.sidecarVolume, s.configmapVolume}
+	}
+
+	// add volumemount and env to container
+	for i, c := range pod.Spec.Containers {
+		// choose a specific container to inject
+		if s.injectContainer != "" && c.Name == s.injectContainer {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+			break
+		} else {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+		}

Review comment:
       duplicated lines with the lines in above `if` block

##########
File path: pkg/operator/injector/annotation.go
##########
@@ -0,0 +1,134 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 injector
+
+//Annotation is used to set default value
+type Annotation struct {
+	Name         string
+	DefaultValue string
+}
+
+//Annotations is used to set overlaied value
+type Annotations map[Annotation]string
+
+func setDefaultAnnotation(name, value string) Annotation {
+	return Annotation{
+		Name:         name,
+		DefaultValue: value,
+	}
+}
+
+const (
+	namePrefix = "skywalking-swck-"
+)
+
+// nolint
+var (
+	//set sidecar's basic information
+	AnnoInitcontainerName  = setDefaultAnnotation(SidecarInitcontainerName, "inject-sky-agent")
+	AnnoInitcontainerImage = setDefaultAnnotation(SidecarInitcontainerImage,
+		"apache/skywalking-java-agent:8.6.0-jdk8")
+	AnnoInitcontainerCommand     = setDefaultAnnotation(SidecarInitcontainerCommand, "sh")
+	AnnoInitcontainerArgsOption  = setDefaultAnnotation(SidecarInitcontainerArgsOption, "-c")
+	AnnoInitcontainerArgsCommand = setDefaultAnnotation(SidecarInitcontainerArgsCommand,
+		"mkdir -p /sky/agent && cp -r /skywalking/agent/* /sky/agent")
+	AnnoSidecarVolumeName             = setDefaultAnnotation(SidecarVolumeName, "sky-agent")
+	AnnoSidecarVolumemountMountpath   = setDefaultAnnotation(SidecarVolumemountMountpath, "/sky/agent")
+	AnnoConfigmapName                 = setDefaultAnnotation(ConfigmapName, namePrefix+"java-agent-configmap")
+	AnnoConfigmapVolumeName           = setDefaultAnnotation(ConfigmapVolumeName, "java-agent-configmap-volume")
+	AnnoConfigmapVolumemountMountpath = setDefaultAnnotation(ConfigmapVolumemountMountpath, "/sky/agent/config")
+	AnnoEnvVarName                    = setDefaultAnnotation(SidecarEnvVarName, "AGENT_OPTS")
+	AnnoEnvVarValue                   = setDefaultAnnotation(SidecarEnvValue,
+		"-javaagent:/sky/agent/skywalking-agent.jar")
+	AnnoInjectContainerName = setDefaultAnnotation(SidecarInjectContainerName, "")
+	//set nil
+	AnnoInjectErrorInfo    = setDefaultAnnotation(SidecarInjectErrorInfo, "")
+	AnnoAgentConfigOverlay = setDefaultAnnotation(SidecarAgentConfigOverlay, "false")
+	//set agent config

Review comment:
       You define the default values here and in `configmap`, which is too hard to maintain these values.
   
   How about creating a yaml file contains those values, generating the `configmap` and default annotations from it. 

##########
File path: pkg/operator/injector/injector.go
##########
@@ -0,0 +1,487 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 injector
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strings"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+)
+
+const (
+	labelKeyagentInjector       = "swck-java-agent-injected"
+	defaultConfigmapNamespace   = "skywalking-swck-system"
+	agentConfigAnnotationPrefix = "agent.skywalking.apache.org/"
+	// If user want to use other Plugins' config ,the annotation must have the following form
+	// plugins.skywalking.apache.org/${config.name} = ${config.value}
+	// for example , if user want to enable plugin.mongodb.trace_param
+	// the annotation is plugins.skywalking.apache.org/plugin.mongodb.trace_param: "true"
+	otherPluginsAnnotationPrefix = "plugins.skywalking.apache.org/"
+)
+
+// log is for logging in this package.
+var log = logf.Log.WithName("injector")
+
+// SidecarInjectField contains all info that will be injected
+type SidecarInjectField struct {
+	// determine whether to inject , default is not to inject
+	needInject bool
+	// determine whether to use annotation to overlay agent config ,
+	// default is not to overlay,which means only use configmap to set agent config
+	// Otherwise, the way to overlay is set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar={config1}={value1},{config2}={value2}
+	agentOverlay bool
+	// initcontainer is a container that has the agent folder
+	initcontainer corev1.Container
+	// sidecarVolume is a shared directory between App's container and initcontainer
+	sidecarVolume corev1.Volume
+	// sidecarVolumeMount is a path that specifies a shared directory
+	sidecarVolumeMount corev1.VolumeMount
+	// configmapVolume is volume that provide user with configmap
+	configmapVolume corev1.Volume
+	// configmapVolumeMount is the configmap's mountpath for user
+	// Notice : the mount path will overwrite the original agent/config/agent.config
+	// So the mount path must match the path of agent.config in the image
+	configmapVolumeMount corev1.VolumeMount
+	// env is used to set java agent’s parameters
+	env corev1.EnvVar
+	// the string is used to set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar=jvmAgentConfigStr
+	jvmAgentConfigStr string
+	// determine which container to inject , default is to inject all containers
+	injectContainer string
+}
+
+// NewSidecarInjectField will create a new SidecarInjectField
+func NewSidecarInjectField() *SidecarInjectField {
+	return new(SidecarInjectField)
+}
+
+// Inject will do real injection
+func (s *SidecarInjectField) Inject(pod *corev1.Pod) {
+	// add initcontrainers to spec
+	if pod.Spec.InitContainers != nil {
+		pod.Spec.InitContainers = append(pod.Spec.InitContainers, s.initcontainer)
+	} else {
+		pod.Spec.InitContainers = []corev1.Container{s.initcontainer}
+	}
+
+	// add volume to spec
+	if pod.Spec.Volumes != nil {
+		pod.Spec.Volumes = append(pod.Spec.Volumes, s.sidecarVolume, s.configmapVolume)
+	} else {
+		pod.Spec.Volumes = []corev1.Volume{s.sidecarVolume, s.configmapVolume}
+	}
+
+	// add volumemount and env to container
+	for i, c := range pod.Spec.Containers {
+		// choose a specific container to inject
+		if s.injectContainer != "" && c.Name == s.injectContainer {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+			break
+		} else {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+		}
+	}
+}
+
+// GetInjectStrategy gets user's injection strategy
+func (s *SidecarInjectField) GetInjectStrategy(containers []corev1.Container, labels,
+	annotation *map[string]string) {
+	// set default value
+	s.needInject = false
+	s.injectContainer = ""
+	s.agentOverlay = false
+
+	// set needInject's value , if the pod has the label "swck-java-agent-injected=true",means need inject
+	if *labels == nil {
+		labels = new(map[string]string)
+		s.needInject = false
+	}
+
+	switch strings.ToLower((*labels)[labelKeyagentInjector]) {
+	case "true":

Review comment:
       Why not a simple `if`? a single branch swich-case is overkill.

##########
File path: pkg/operator/injector/injector.go
##########
@@ -0,0 +1,487 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 injector
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strings"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+)
+
+const (
+	labelKeyagentInjector       = "swck-java-agent-injected"
+	defaultConfigmapNamespace   = "skywalking-swck-system"
+	agentConfigAnnotationPrefix = "agent.skywalking.apache.org/"
+	// If user want to use other Plugins' config ,the annotation must have the following form
+	// plugins.skywalking.apache.org/${config.name} = ${config.value}
+	// for example , if user want to enable plugin.mongodb.trace_param
+	// the annotation is plugins.skywalking.apache.org/plugin.mongodb.trace_param: "true"
+	otherPluginsAnnotationPrefix = "plugins.skywalking.apache.org/"
+)
+
+// log is for logging in this package.
+var log = logf.Log.WithName("injector")
+
+// SidecarInjectField contains all info that will be injected
+type SidecarInjectField struct {
+	// determine whether to inject , default is not to inject
+	needInject bool
+	// determine whether to use annotation to overlay agent config ,
+	// default is not to overlay,which means only use configmap to set agent config
+	// Otherwise, the way to overlay is set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar={config1}={value1},{config2}={value2}
+	agentOverlay bool
+	// initcontainer is a container that has the agent folder
+	initcontainer corev1.Container
+	// sidecarVolume is a shared directory between App's container and initcontainer
+	sidecarVolume corev1.Volume
+	// sidecarVolumeMount is a path that specifies a shared directory
+	sidecarVolumeMount corev1.VolumeMount
+	// configmapVolume is volume that provide user with configmap
+	configmapVolume corev1.Volume
+	// configmapVolumeMount is the configmap's mountpath for user
+	// Notice : the mount path will overwrite the original agent/config/agent.config
+	// So the mount path must match the path of agent.config in the image
+	configmapVolumeMount corev1.VolumeMount
+	// env is used to set java agent’s parameters
+	env corev1.EnvVar
+	// the string is used to set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar=jvmAgentConfigStr
+	jvmAgentConfigStr string
+	// determine which container to inject , default is to inject all containers
+	injectContainer string
+}
+
+// NewSidecarInjectField will create a new SidecarInjectField
+func NewSidecarInjectField() *SidecarInjectField {
+	return new(SidecarInjectField)
+}
+
+// Inject will do real injection
+func (s *SidecarInjectField) Inject(pod *corev1.Pod) {
+	// add initcontrainers to spec
+	if pod.Spec.InitContainers != nil {
+		pod.Spec.InitContainers = append(pod.Spec.InitContainers, s.initcontainer)
+	} else {
+		pod.Spec.InitContainers = []corev1.Container{s.initcontainer}
+	}
+
+	// add volume to spec
+	if pod.Spec.Volumes != nil {
+		pod.Spec.Volumes = append(pod.Spec.Volumes, s.sidecarVolume, s.configmapVolume)
+	} else {
+		pod.Spec.Volumes = []corev1.Volume{s.sidecarVolume, s.configmapVolume}
+	}
+
+	// add volumemount and env to container
+	for i, c := range pod.Spec.Containers {
+		// choose a specific container to inject
+		if s.injectContainer != "" && c.Name == s.injectContainer {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+			break
+		} else {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+		}
+	}
+}
+
+// GetInjectStrategy gets user's injection strategy
+func (s *SidecarInjectField) GetInjectStrategy(containers []corev1.Container, labels,
+	annotation *map[string]string) {
+	// set default value
+	s.needInject = false
+	s.injectContainer = ""
+	s.agentOverlay = false
+
+	// set needInject's value , if the pod has the label "swck-java-agent-injected=true",means need inject
+	if *labels == nil {
+		labels = new(map[string]string)
+		s.needInject = false
+	}
+
+	switch strings.ToLower((*labels)[labelKeyagentInjector]) {
+	case "true":
+		s.needInject = true
+	}
+
+	// set injectContainer's value
+	if *annotation == nil {
+		annotation = new(map[string]string)
+		s.injectContainer = ""
+		s.agentOverlay = false
+	}
+
+	// validate the container is or not exist
+	if v, ok := (*annotation)[AnnoInjectContainerName.Name]; ok {
+		for _, c := range containers {
+			if c.Name == v {
+				s.injectContainer = v
+				break
+			}
+		}
+	}
+
+	// set agentOverlay's value
+	switch strings.ToLower((*annotation)[AnnoAgentConfigOverlay.Name]) {

Review comment:
       Same as above, swich-case is overkill.

##########
File path: pkg/operator/injector/injector.go
##########
@@ -0,0 +1,487 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 injector
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strings"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+)
+
+const (
+	labelKeyagentInjector       = "swck-java-agent-injected"
+	defaultConfigmapNamespace   = "skywalking-swck-system"
+	agentConfigAnnotationPrefix = "agent.skywalking.apache.org/"
+	// If user want to use other Plugins' config ,the annotation must have the following form
+	// plugins.skywalking.apache.org/${config.name} = ${config.value}
+	// for example , if user want to enable plugin.mongodb.trace_param
+	// the annotation is plugins.skywalking.apache.org/plugin.mongodb.trace_param: "true"
+	otherPluginsAnnotationPrefix = "plugins.skywalking.apache.org/"
+)
+
+// log is for logging in this package.
+var log = logf.Log.WithName("injector")
+
+// SidecarInjectField contains all info that will be injected
+type SidecarInjectField struct {
+	// determine whether to inject , default is not to inject
+	needInject bool
+	// determine whether to use annotation to overlay agent config ,
+	// default is not to overlay,which means only use configmap to set agent config
+	// Otherwise, the way to overlay is set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar={config1}={value1},{config2}={value2}
+	agentOverlay bool
+	// initcontainer is a container that has the agent folder
+	initcontainer corev1.Container
+	// sidecarVolume is a shared directory between App's container and initcontainer
+	sidecarVolume corev1.Volume
+	// sidecarVolumeMount is a path that specifies a shared directory
+	sidecarVolumeMount corev1.VolumeMount
+	// configmapVolume is volume that provide user with configmap
+	configmapVolume corev1.Volume
+	// configmapVolumeMount is the configmap's mountpath for user
+	// Notice : the mount path will overwrite the original agent/config/agent.config
+	// So the mount path must match the path of agent.config in the image
+	configmapVolumeMount corev1.VolumeMount
+	// env is used to set java agent’s parameters
+	env corev1.EnvVar
+	// the string is used to set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar=jvmAgentConfigStr
+	jvmAgentConfigStr string
+	// determine which container to inject , default is to inject all containers
+	injectContainer string
+}
+
+// NewSidecarInjectField will create a new SidecarInjectField
+func NewSidecarInjectField() *SidecarInjectField {
+	return new(SidecarInjectField)
+}
+
+// Inject will do real injection
+func (s *SidecarInjectField) Inject(pod *corev1.Pod) {
+	// add initcontrainers to spec
+	if pod.Spec.InitContainers != nil {
+		pod.Spec.InitContainers = append(pod.Spec.InitContainers, s.initcontainer)
+	} else {
+		pod.Spec.InitContainers = []corev1.Container{s.initcontainer}
+	}
+
+	// add volume to spec
+	if pod.Spec.Volumes != nil {
+		pod.Spec.Volumes = append(pod.Spec.Volumes, s.sidecarVolume, s.configmapVolume)
+	} else {
+		pod.Spec.Volumes = []corev1.Volume{s.sidecarVolume, s.configmapVolume}
+	}
+
+	// add volumemount and env to container
+	for i, c := range pod.Spec.Containers {
+		// choose a specific container to inject
+		if s.injectContainer != "" && c.Name == s.injectContainer {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+			break
+		} else {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+		}
+	}
+}
+
+// GetInjectStrategy gets user's injection strategy
+func (s *SidecarInjectField) GetInjectStrategy(containers []corev1.Container, labels,
+	annotation *map[string]string) {
+	// set default value
+	s.needInject = false
+	s.injectContainer = ""
+	s.agentOverlay = false
+
+	// set needInject's value , if the pod has the label "swck-java-agent-injected=true",means need inject
+	if *labels == nil {
+		labels = new(map[string]string)
+		s.needInject = false
+	}
+
+	switch strings.ToLower((*labels)[labelKeyagentInjector]) {
+	case "true":
+		s.needInject = true
+	}
+
+	// set injectContainer's value
+	if *annotation == nil {
+		annotation = new(map[string]string)
+		s.injectContainer = ""
+		s.agentOverlay = false
+	}
+
+	// validate the container is or not exist
+	if v, ok := (*annotation)[AnnoInjectContainerName.Name]; ok {
+		for _, c := range containers {
+			if c.Name == v {
+				s.injectContainer = v
+				break
+			}
+		}
+	}
+
+	// set agentOverlay's value
+	switch strings.ToLower((*annotation)[AnnoAgentConfigOverlay.Name]) {
+	case "true":
+		s.agentOverlay = true
+	}
+
+}
+
+func (s *SidecarInjectField) injectErrorAnnotation(annotation *map[string]string, errorInfo string) {
+	(*annotation)[AnnoInjectErrorInfo.Name] = errorInfo
+}
+
+// SidecarOverlayandGetValue get final value of sidecar
+func (s *SidecarInjectField) SidecarOverlayandGetValue(as *Annotations, annotation *map[string]string,
+	a Annotation) (string, bool) {
+	if _, ok := (*annotation)[a.Name]; ok {
+		err := as.SetOverlay(annotation, a)
+		if err != nil {
+			s.injectErrorAnnotation(annotation, err.Error())
+			return "", false
+		}
+	}
+	return as.GetFinalValue(a), true
+}
+
+func (s *SidecarInjectField) setValue(config *string, as *Annotations, annotation *map[string]string,
+	a Annotation) bool {
+	if v, ok := s.SidecarOverlayandGetValue(as, annotation, a); ok {
+		*config = v
+		return true
+	}
+	return false
+}
+
+// OverlaySidecar overlays default config
+func (s *SidecarInjectField) OverlaySidecar(as *Annotations, annotation *map[string]string) bool {
+	var (
+		command       string
+		argOption     string
+		argCommand    string
+		configmapName string
+	)
+
+	switch {
+	case !s.setValue(&s.initcontainer.Name, as, annotation, AnnoInitcontainerName):
+		return false
+	case !s.setValue(&s.initcontainer.Image, as, annotation, AnnoInitcontainerImage):
+		return false
+	case !s.setValue(&command, as, annotation, AnnoInitcontainerCommand):
+		return false
+	case !s.setValue(&argOption, as, annotation, AnnoInitcontainerArgsOption):
+		return false
+	case !s.setValue(&argCommand, as, annotation, AnnoInitcontainerArgsCommand):
+		return false
+	case !s.setValue(&s.sidecarVolume.Name, as, annotation, AnnoSidecarVolumeName):
+		return false
+	case !s.setValue(&s.sidecarVolumeMount.Name, as, annotation, AnnoSidecarVolumeName):
+		return false
+	case !s.setValue(&s.sidecarVolumeMount.MountPath, as, annotation, AnnoSidecarVolumemountMountpath):
+		return false
+	case !s.setValue(&configmapName, as, annotation, AnnoConfigmapName):
+		return false
+	case !s.setValue(&s.configmapVolume.Name, as, annotation, AnnoConfigmapVolumeName):
+		return false
+	case !s.setValue(&s.configmapVolumeMount.Name, as, annotation, AnnoConfigmapVolumeName):
+		return false
+	case !s.setValue(&s.configmapVolumeMount.MountPath, as, annotation, AnnoConfigmapVolumemountMountpath):
+		return false
+	case !s.setValue(&s.env.Name, as, annotation, AnnoEnvVarName):
+		return false
+	case !s.setValue(&s.env.Value, as, annotation, AnnoEnvVarValue):
+		return false
+	}
+
+	s.initcontainer.Command = []string{command}
+	s.initcontainer.Args = []string{argOption, argCommand}
+	s.initcontainer.VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount}
+
+	// the sidecar volume's type is determined
+	s.sidecarVolume.VolumeSource.EmptyDir = nil
+
+	s.configmapVolume.VolumeSource = corev1.VolumeSource{
+		ConfigMap: &corev1.ConfigMapVolumeSource{
+			LocalObjectReference: corev1.LocalObjectReference{
+				Name: configmapName,
+			},
+		},
+	}
+
+	return true
+}
+
+// AgentOverlayandGetValue will do real annotation overlay
+func (s *SidecarInjectField) AgentOverlayandGetValue(as *Annotations, annotation *map[string]string,
+	a Annotation) (string, bool) {
+	if _, ok := (*annotation)[a.Name]; ok {
+		err := as.SetOverlay(annotation, a)
+		if err != nil {
+			s.injectErrorAnnotation(annotation, err.Error())
+			return "", false
+		}
+	}
+	return as.GetOverlayValue(a), true
+}
+
+func (s *SidecarInjectField) setJvmAgentStr(as *Annotations, annotation *map[string]string, a Annotation,
+	specialStr bool) bool {
+	v, ok := s.AgentOverlayandGetValue(as, annotation, a)
+	if v != "" && ok {
+		// get {config1}={value1}
+		configName := strings.TrimPrefix(a.Name, agentConfigAnnotationPrefix)
+		if specialStr {
+			v = strings.Join([]string{"'", "'"}, v)
+		}
+		config := strings.Join([]string{configName, v}, "=")
+
+		// add to jvmAgentConfigStr
+		if s.jvmAgentConfigStr != "" {
+			s.jvmAgentConfigStr = strings.Join([]string{s.jvmAgentConfigStr, config}, ",")
+		} else {
+			s.jvmAgentConfigStr = config
+		}
+	}
+	return ok
+}
+
+// OverlayAgentConfig overlays agent config
+func (s *SidecarInjectField) OverlayAgentConfig(as *Annotations, annotation *map[string]string) bool {
+	// jvmAgentConfigStr init
+	s.jvmAgentConfigStr = ""
+
+	switch {
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentNamespace, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentServiceName, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentSampleNumber, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentAuthentication, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentSpanLimit, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentIgnoreSuffix, true):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentIsOpenDebugging, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentIsCacheEnhaned, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentClassCache, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentOperationName, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentForceTLS, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileActive, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileMaxParallel, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileMaxDuration, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileDump, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileSnapshot, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentCollectorService, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingName, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingLevel, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingDir, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingMaxSize, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingMaxFiles, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentStatuscheckExceptions, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentStatuscheckDepth, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginMount, true):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentExcludePlugins, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginJdbc, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginKafkaServers, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginKafkaNamespace, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginSpringannotation, false):
+		return false
+	}
+
+	return true
+}
+
+// OverlayPluginsConfig will add Plugins' config to JvmAgentStr without verification
+// Notice, if a config is not in agent.config, it will be seen as a plugin config
+// user must ensure the accuracy of configuration.
+// Otherwides,If a separator(, or =) in the option or value, it should be wrapped in quotes.
+func (s *SidecarInjectField) OverlayPluginsConfig(annotation *map[string]string) bool {
+	for k, v := range *annotation {
+		if strings.HasPrefix(k, otherPluginsAnnotationPrefix) {
+			configName := strings.TrimPrefix(k, otherPluginsAnnotationPrefix)
+			config := strings.Join([]string{configName, v}, "=")
+			// add to jvmAgentConfigStr
+			if s.jvmAgentConfigStr != "" {
+				s.jvmAgentConfigStr = strings.Join([]string{s.jvmAgentConfigStr, config}, ",")
+			} else {
+				s.jvmAgentConfigStr = config
+			}
+		}
+	}
+	return true
+}
+
+// CreateConfigmap will find a configmap to set agent config
+// if not exist , then create a configmap
+func (s *SidecarInjectField) CreateConfigmap(ctx context.Context, kubeclient client.Client, namespace string,
+	annotation *map[string]string) bool {
+	configmap := &corev1.ConfigMap{}
+	configmapName := s.configmapVolume.VolumeSource.ConfigMap.LocalObjectReference.Name
+	// check whether the configmap is existed
+	err := kubeclient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: configmapName}, configmap)
+	// if configmap not exist , get configmap from defaultConfigmapNamespace
+	if err != nil {
+		defaultConfigmap := &corev1.ConfigMap{}
+		if err := kubeclient.Get(ctx, client.ObjectKey{Namespace: defaultConfigmapNamespace,
+			Name: AnnoConfigmapName.DefaultValue}, defaultConfigmap); err != nil {
+			log.Info(err.Error())
+			s.injectErrorAnnotation(annotation, fmt.Sprintf("get configmap %s from namespace %s error[%s]",
+				AnnoConfigmapName.DefaultValue, defaultConfigmapNamespace, err.Error()))
+			return false
+		}
+		// create new configmap and update namespace
+		injectConfigmap := corev1.ConfigMap{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      configmapName,
+				Namespace: namespace,
+			},
+			Data: defaultConfigmap.Data,

Review comment:
       To validate defaultConfigmap as the annotations.

##########
File path: pkg/operator/injector/injector.go
##########
@@ -0,0 +1,487 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 injector
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strings"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+)
+
+const (
+	labelKeyagentInjector       = "swck-java-agent-injected"
+	defaultConfigmapNamespace   = "skywalking-swck-system"
+	agentConfigAnnotationPrefix = "agent.skywalking.apache.org/"
+	// If user want to use other Plugins' config ,the annotation must have the following form
+	// plugins.skywalking.apache.org/${config.name} = ${config.value}
+	// for example , if user want to enable plugin.mongodb.trace_param
+	// the annotation is plugins.skywalking.apache.org/plugin.mongodb.trace_param: "true"
+	otherPluginsAnnotationPrefix = "plugins.skywalking.apache.org/"
+)
+
+// log is for logging in this package.
+var log = logf.Log.WithName("injector")
+
+// SidecarInjectField contains all info that will be injected
+type SidecarInjectField struct {
+	// determine whether to inject , default is not to inject
+	needInject bool
+	// determine whether to use annotation to overlay agent config ,
+	// default is not to overlay,which means only use configmap to set agent config
+	// Otherwise, the way to overlay is set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar={config1}={value1},{config2}={value2}
+	agentOverlay bool
+	// initcontainer is a container that has the agent folder
+	initcontainer corev1.Container
+	// sidecarVolume is a shared directory between App's container and initcontainer
+	sidecarVolume corev1.Volume
+	// sidecarVolumeMount is a path that specifies a shared directory
+	sidecarVolumeMount corev1.VolumeMount
+	// configmapVolume is volume that provide user with configmap
+	configmapVolume corev1.Volume
+	// configmapVolumeMount is the configmap's mountpath for user
+	// Notice : the mount path will overwrite the original agent/config/agent.config
+	// So the mount path must match the path of agent.config in the image
+	configmapVolumeMount corev1.VolumeMount
+	// env is used to set java agent’s parameters
+	env corev1.EnvVar
+	// the string is used to set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar=jvmAgentConfigStr
+	jvmAgentConfigStr string
+	// determine which container to inject , default is to inject all containers
+	injectContainer string
+}
+
+// NewSidecarInjectField will create a new SidecarInjectField
+func NewSidecarInjectField() *SidecarInjectField {
+	return new(SidecarInjectField)
+}
+
+// Inject will do real injection
+func (s *SidecarInjectField) Inject(pod *corev1.Pod) {
+	// add initcontrainers to spec
+	if pod.Spec.InitContainers != nil {
+		pod.Spec.InitContainers = append(pod.Spec.InitContainers, s.initcontainer)
+	} else {
+		pod.Spec.InitContainers = []corev1.Container{s.initcontainer}
+	}
+
+	// add volume to spec
+	if pod.Spec.Volumes != nil {
+		pod.Spec.Volumes = append(pod.Spec.Volumes, s.sidecarVolume, s.configmapVolume)
+	} else {
+		pod.Spec.Volumes = []corev1.Volume{s.sidecarVolume, s.configmapVolume}
+	}
+
+	// add volumemount and env to container
+	for i, c := range pod.Spec.Containers {
+		// choose a specific container to inject
+		if s.injectContainer != "" && c.Name == s.injectContainer {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+			break
+		} else {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+		}
+	}
+}
+
+// GetInjectStrategy gets user's injection strategy
+func (s *SidecarInjectField) GetInjectStrategy(containers []corev1.Container, labels,
+	annotation *map[string]string) {
+	// set default value
+	s.needInject = false
+	s.injectContainer = ""
+	s.agentOverlay = false
+
+	// set needInject's value , if the pod has the label "swck-java-agent-injected=true",means need inject
+	if *labels == nil {
+		labels = new(map[string]string)
+		s.needInject = false
+	}
+
+	switch strings.ToLower((*labels)[labelKeyagentInjector]) {
+	case "true":
+		s.needInject = true
+	}
+
+	// set injectContainer's value
+	if *annotation == nil {
+		annotation = new(map[string]string)
+		s.injectContainer = ""
+		s.agentOverlay = false
+	}
+
+	// validate the container is or not exist
+	if v, ok := (*annotation)[AnnoInjectContainerName.Name]; ok {
+		for _, c := range containers {
+			if c.Name == v {
+				s.injectContainer = v
+				break
+			}
+		}
+	}
+
+	// set agentOverlay's value
+	switch strings.ToLower((*annotation)[AnnoAgentConfigOverlay.Name]) {
+	case "true":
+		s.agentOverlay = true
+	}
+
+}
+
+func (s *SidecarInjectField) injectErrorAnnotation(annotation *map[string]string, errorInfo string) {
+	(*annotation)[AnnoInjectErrorInfo.Name] = errorInfo
+}
+
+// SidecarOverlayandGetValue get final value of sidecar
+func (s *SidecarInjectField) SidecarOverlayandGetValue(as *Annotations, annotation *map[string]string,
+	a Annotation) (string, bool) {
+	if _, ok := (*annotation)[a.Name]; ok {
+		err := as.SetOverlay(annotation, a)
+		if err != nil {
+			s.injectErrorAnnotation(annotation, err.Error())
+			return "", false
+		}
+	}
+	return as.GetFinalValue(a), true
+}
+
+func (s *SidecarInjectField) setValue(config *string, as *Annotations, annotation *map[string]string,
+	a Annotation) bool {
+	if v, ok := s.SidecarOverlayandGetValue(as, annotation, a); ok {
+		*config = v
+		return true
+	}
+	return false
+}
+
+// OverlaySidecar overlays default config
+func (s *SidecarInjectField) OverlaySidecar(as *Annotations, annotation *map[string]string) bool {
+	var (
+		command       string
+		argOption     string
+		argCommand    string
+		configmapName string
+	)
+
+	switch {
+	case !s.setValue(&s.initcontainer.Name, as, annotation, AnnoInitcontainerName):
+		return false
+	case !s.setValue(&s.initcontainer.Image, as, annotation, AnnoInitcontainerImage):
+		return false
+	case !s.setValue(&command, as, annotation, AnnoInitcontainerCommand):
+		return false
+	case !s.setValue(&argOption, as, annotation, AnnoInitcontainerArgsOption):
+		return false
+	case !s.setValue(&argCommand, as, annotation, AnnoInitcontainerArgsCommand):
+		return false
+	case !s.setValue(&s.sidecarVolume.Name, as, annotation, AnnoSidecarVolumeName):
+		return false
+	case !s.setValue(&s.sidecarVolumeMount.Name, as, annotation, AnnoSidecarVolumeName):
+		return false
+	case !s.setValue(&s.sidecarVolumeMount.MountPath, as, annotation, AnnoSidecarVolumemountMountpath):
+		return false
+	case !s.setValue(&configmapName, as, annotation, AnnoConfigmapName):
+		return false
+	case !s.setValue(&s.configmapVolume.Name, as, annotation, AnnoConfigmapVolumeName):
+		return false
+	case !s.setValue(&s.configmapVolumeMount.Name, as, annotation, AnnoConfigmapVolumeName):
+		return false
+	case !s.setValue(&s.configmapVolumeMount.MountPath, as, annotation, AnnoConfigmapVolumemountMountpath):
+		return false
+	case !s.setValue(&s.env.Name, as, annotation, AnnoEnvVarName):
+		return false
+	case !s.setValue(&s.env.Value, as, annotation, AnnoEnvVarValue):
+		return false
+	}
+
+	s.initcontainer.Command = []string{command}
+	s.initcontainer.Args = []string{argOption, argCommand}
+	s.initcontainer.VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount}
+
+	// the sidecar volume's type is determined
+	s.sidecarVolume.VolumeSource.EmptyDir = nil
+
+	s.configmapVolume.VolumeSource = corev1.VolumeSource{
+		ConfigMap: &corev1.ConfigMapVolumeSource{
+			LocalObjectReference: corev1.LocalObjectReference{
+				Name: configmapName,
+			},
+		},
+	}
+
+	return true
+}
+
+// AgentOverlayandGetValue will do real annotation overlay
+func (s *SidecarInjectField) AgentOverlayandGetValue(as *Annotations, annotation *map[string]string,
+	a Annotation) (string, bool) {
+	if _, ok := (*annotation)[a.Name]; ok {
+		err := as.SetOverlay(annotation, a)
+		if err != nil {
+			s.injectErrorAnnotation(annotation, err.Error())
+			return "", false
+		}
+	}
+	return as.GetOverlayValue(a), true
+}
+
+func (s *SidecarInjectField) setJvmAgentStr(as *Annotations, annotation *map[string]string, a Annotation,
+	specialStr bool) bool {
+	v, ok := s.AgentOverlayandGetValue(as, annotation, a)
+	if v != "" && ok {
+		// get {config1}={value1}
+		configName := strings.TrimPrefix(a.Name, agentConfigAnnotationPrefix)
+		if specialStr {
+			v = strings.Join([]string{"'", "'"}, v)
+		}
+		config := strings.Join([]string{configName, v}, "=")
+
+		// add to jvmAgentConfigStr
+		if s.jvmAgentConfigStr != "" {
+			s.jvmAgentConfigStr = strings.Join([]string{s.jvmAgentConfigStr, config}, ",")
+		} else {
+			s.jvmAgentConfigStr = config
+		}
+	}
+	return ok
+}
+
+// OverlayAgentConfig overlays agent config
+func (s *SidecarInjectField) OverlayAgentConfig(as *Annotations, annotation *map[string]string) bool {
+	// jvmAgentConfigStr init
+	s.jvmAgentConfigStr = ""
+
+	switch {
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentNamespace, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentServiceName, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentSampleNumber, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentAuthentication, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentSpanLimit, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentIgnoreSuffix, true):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentIsOpenDebugging, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentIsCacheEnhaned, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentClassCache, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentOperationName, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentForceTLS, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileActive, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileMaxParallel, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileMaxDuration, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileDump, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileSnapshot, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentCollectorService, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingName, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingLevel, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingDir, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingMaxSize, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingMaxFiles, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentStatuscheckExceptions, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentStatuscheckDepth, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginMount, true):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentExcludePlugins, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginJdbc, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginKafkaServers, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginKafkaNamespace, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginSpringannotation, false):
+		return false
+	}
+
+	return true
+}
+
+// OverlayPluginsConfig will add Plugins' config to JvmAgentStr without verification
+// Notice, if a config is not in agent.config, it will be seen as a plugin config
+// user must ensure the accuracy of configuration.
+// Otherwides,If a separator(, or =) in the option or value, it should be wrapped in quotes.
+func (s *SidecarInjectField) OverlayPluginsConfig(annotation *map[string]string) bool {
+	for k, v := range *annotation {
+		if strings.HasPrefix(k, otherPluginsAnnotationPrefix) {
+			configName := strings.TrimPrefix(k, otherPluginsAnnotationPrefix)
+			config := strings.Join([]string{configName, v}, "=")
+			// add to jvmAgentConfigStr
+			if s.jvmAgentConfigStr != "" {
+				s.jvmAgentConfigStr = strings.Join([]string{s.jvmAgentConfigStr, config}, ",")
+			} else {
+				s.jvmAgentConfigStr = config
+			}
+		}
+	}
+	return true
+}
+
+// CreateConfigmap will find a configmap to set agent config
+// if not exist , then create a configmap
+func (s *SidecarInjectField) CreateConfigmap(ctx context.Context, kubeclient client.Client, namespace string,
+	annotation *map[string]string) bool {
+	configmap := &corev1.ConfigMap{}
+	configmapName := s.configmapVolume.VolumeSource.ConfigMap.LocalObjectReference.Name
+	// check whether the configmap is existed
+	err := kubeclient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: configmapName}, configmap)
+	// if configmap not exist , get configmap from defaultConfigmapNamespace
+	if err != nil {
+		defaultConfigmap := &corev1.ConfigMap{}
+		if err := kubeclient.Get(ctx, client.ObjectKey{Namespace: defaultConfigmapNamespace,
+			Name: AnnoConfigmapName.DefaultValue}, defaultConfigmap); err != nil {
+			log.Info(err.Error())
+			s.injectErrorAnnotation(annotation, fmt.Sprintf("get configmap %s from namespace %s error[%s]",
+				AnnoConfigmapName.DefaultValue, defaultConfigmapNamespace, err.Error()))
+			return false
+		}
+		// create new configmap and update namespace
+		injectConfigmap := corev1.ConfigMap{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      configmapName,
+				Namespace: namespace,
+			},
+			Data: defaultConfigmap.Data,
+		}
+
+		if err := kubeclient.Create(ctx, &injectConfigmap); err != nil {
+			log.Info(err.Error())

Review comment:
       To use `log.Error`

##########
File path: pkg/operator/injector/injector.go
##########
@@ -0,0 +1,487 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 injector
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strings"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+)
+
+const (
+	labelKeyagentInjector       = "swck-java-agent-injected"
+	defaultConfigmapNamespace   = "skywalking-swck-system"
+	agentConfigAnnotationPrefix = "agent.skywalking.apache.org/"
+	// If user want to use other Plugins' config ,the annotation must have the following form
+	// plugins.skywalking.apache.org/${config.name} = ${config.value}
+	// for example , if user want to enable plugin.mongodb.trace_param
+	// the annotation is plugins.skywalking.apache.org/plugin.mongodb.trace_param: "true"
+	otherPluginsAnnotationPrefix = "plugins.skywalking.apache.org/"
+)
+
+// log is for logging in this package.
+var log = logf.Log.WithName("injector")
+
+// SidecarInjectField contains all info that will be injected
+type SidecarInjectField struct {
+	// determine whether to inject , default is not to inject
+	needInject bool
+	// determine whether to use annotation to overlay agent config ,
+	// default is not to overlay,which means only use configmap to set agent config
+	// Otherwise, the way to overlay is set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar={config1}={value1},{config2}={value2}
+	agentOverlay bool
+	// initcontainer is a container that has the agent folder
+	initcontainer corev1.Container
+	// sidecarVolume is a shared directory between App's container and initcontainer
+	sidecarVolume corev1.Volume
+	// sidecarVolumeMount is a path that specifies a shared directory
+	sidecarVolumeMount corev1.VolumeMount
+	// configmapVolume is volume that provide user with configmap
+	configmapVolume corev1.Volume
+	// configmapVolumeMount is the configmap's mountpath for user
+	// Notice : the mount path will overwrite the original agent/config/agent.config
+	// So the mount path must match the path of agent.config in the image
+	configmapVolumeMount corev1.VolumeMount
+	// env is used to set java agent’s parameters
+	env corev1.EnvVar
+	// the string is used to set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar=jvmAgentConfigStr
+	jvmAgentConfigStr string
+	// determine which container to inject , default is to inject all containers
+	injectContainer string
+}
+
+// NewSidecarInjectField will create a new SidecarInjectField
+func NewSidecarInjectField() *SidecarInjectField {
+	return new(SidecarInjectField)
+}
+
+// Inject will do real injection
+func (s *SidecarInjectField) Inject(pod *corev1.Pod) {
+	// add initcontrainers to spec
+	if pod.Spec.InitContainers != nil {
+		pod.Spec.InitContainers = append(pod.Spec.InitContainers, s.initcontainer)
+	} else {
+		pod.Spec.InitContainers = []corev1.Container{s.initcontainer}
+	}
+
+	// add volume to spec
+	if pod.Spec.Volumes != nil {
+		pod.Spec.Volumes = append(pod.Spec.Volumes, s.sidecarVolume, s.configmapVolume)
+	} else {
+		pod.Spec.Volumes = []corev1.Volume{s.sidecarVolume, s.configmapVolume}
+	}
+
+	// add volumemount and env to container
+	for i, c := range pod.Spec.Containers {
+		// choose a specific container to inject
+		if s.injectContainer != "" && c.Name == s.injectContainer {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+			break
+		} else {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+		}
+	}
+}
+
+// GetInjectStrategy gets user's injection strategy
+func (s *SidecarInjectField) GetInjectStrategy(containers []corev1.Container, labels,
+	annotation *map[string]string) {
+	// set default value
+	s.needInject = false
+	s.injectContainer = ""
+	s.agentOverlay = false
+
+	// set needInject's value , if the pod has the label "swck-java-agent-injected=true",means need inject
+	if *labels == nil {
+		labels = new(map[string]string)
+		s.needInject = false
+	}
+
+	switch strings.ToLower((*labels)[labelKeyagentInjector]) {
+	case "true":
+		s.needInject = true
+	}
+
+	// set injectContainer's value
+	if *annotation == nil {
+		annotation = new(map[string]string)
+		s.injectContainer = ""
+		s.agentOverlay = false
+	}
+
+	// validate the container is or not exist
+	if v, ok := (*annotation)[AnnoInjectContainerName.Name]; ok {
+		for _, c := range containers {
+			if c.Name == v {
+				s.injectContainer = v
+				break
+			}
+		}
+	}
+
+	// set agentOverlay's value
+	switch strings.ToLower((*annotation)[AnnoAgentConfigOverlay.Name]) {
+	case "true":
+		s.agentOverlay = true
+	}
+
+}
+
+func (s *SidecarInjectField) injectErrorAnnotation(annotation *map[string]string, errorInfo string) {
+	(*annotation)[AnnoInjectErrorInfo.Name] = errorInfo
+}
+
+// SidecarOverlayandGetValue get final value of sidecar
+func (s *SidecarInjectField) SidecarOverlayandGetValue(as *Annotations, annotation *map[string]string,
+	a Annotation) (string, bool) {
+	if _, ok := (*annotation)[a.Name]; ok {
+		err := as.SetOverlay(annotation, a)
+		if err != nil {
+			s.injectErrorAnnotation(annotation, err.Error())
+			return "", false
+		}
+	}
+	return as.GetFinalValue(a), true
+}
+
+func (s *SidecarInjectField) setValue(config *string, as *Annotations, annotation *map[string]string,
+	a Annotation) bool {
+	if v, ok := s.SidecarOverlayandGetValue(as, annotation, a); ok {
+		*config = v
+		return true
+	}
+	return false
+}
+
+// OverlaySidecar overlays default config
+func (s *SidecarInjectField) OverlaySidecar(as *Annotations, annotation *map[string]string) bool {
+	var (
+		command       string
+		argOption     string
+		argCommand    string
+		configmapName string
+	)
+
+	switch {
+	case !s.setValue(&s.initcontainer.Name, as, annotation, AnnoInitcontainerName):
+		return false
+	case !s.setValue(&s.initcontainer.Image, as, annotation, AnnoInitcontainerImage):
+		return false
+	case !s.setValue(&command, as, annotation, AnnoInitcontainerCommand):
+		return false
+	case !s.setValue(&argOption, as, annotation, AnnoInitcontainerArgsOption):
+		return false
+	case !s.setValue(&argCommand, as, annotation, AnnoInitcontainerArgsCommand):
+		return false
+	case !s.setValue(&s.sidecarVolume.Name, as, annotation, AnnoSidecarVolumeName):
+		return false
+	case !s.setValue(&s.sidecarVolumeMount.Name, as, annotation, AnnoSidecarVolumeName):
+		return false
+	case !s.setValue(&s.sidecarVolumeMount.MountPath, as, annotation, AnnoSidecarVolumemountMountpath):
+		return false
+	case !s.setValue(&configmapName, as, annotation, AnnoConfigmapName):
+		return false
+	case !s.setValue(&s.configmapVolume.Name, as, annotation, AnnoConfigmapVolumeName):
+		return false
+	case !s.setValue(&s.configmapVolumeMount.Name, as, annotation, AnnoConfigmapVolumeName):
+		return false
+	case !s.setValue(&s.configmapVolumeMount.MountPath, as, annotation, AnnoConfigmapVolumemountMountpath):
+		return false
+	case !s.setValue(&s.env.Name, as, annotation, AnnoEnvVarName):
+		return false
+	case !s.setValue(&s.env.Value, as, annotation, AnnoEnvVarValue):
+		return false
+	}
+
+	s.initcontainer.Command = []string{command}
+	s.initcontainer.Args = []string{argOption, argCommand}
+	s.initcontainer.VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount}
+
+	// the sidecar volume's type is determined
+	s.sidecarVolume.VolumeSource.EmptyDir = nil
+
+	s.configmapVolume.VolumeSource = corev1.VolumeSource{
+		ConfigMap: &corev1.ConfigMapVolumeSource{
+			LocalObjectReference: corev1.LocalObjectReference{
+				Name: configmapName,
+			},
+		},
+	}
+
+	return true
+}
+
+// AgentOverlayandGetValue will do real annotation overlay
+func (s *SidecarInjectField) AgentOverlayandGetValue(as *Annotations, annotation *map[string]string,
+	a Annotation) (string, bool) {
+	if _, ok := (*annotation)[a.Name]; ok {
+		err := as.SetOverlay(annotation, a)
+		if err != nil {
+			s.injectErrorAnnotation(annotation, err.Error())
+			return "", false
+		}
+	}
+	return as.GetOverlayValue(a), true
+}
+
+func (s *SidecarInjectField) setJvmAgentStr(as *Annotations, annotation *map[string]string, a Annotation,
+	specialStr bool) bool {
+	v, ok := s.AgentOverlayandGetValue(as, annotation, a)
+	if v != "" && ok {
+		// get {config1}={value1}
+		configName := strings.TrimPrefix(a.Name, agentConfigAnnotationPrefix)
+		if specialStr {
+			v = strings.Join([]string{"'", "'"}, v)
+		}
+		config := strings.Join([]string{configName, v}, "=")
+
+		// add to jvmAgentConfigStr
+		if s.jvmAgentConfigStr != "" {
+			s.jvmAgentConfigStr = strings.Join([]string{s.jvmAgentConfigStr, config}, ",")
+		} else {
+			s.jvmAgentConfigStr = config
+		}
+	}
+	return ok
+}
+
+// OverlayAgentConfig overlays agent config
+func (s *SidecarInjectField) OverlayAgentConfig(as *Annotations, annotation *map[string]string) bool {
+	// jvmAgentConfigStr init
+	s.jvmAgentConfigStr = ""
+
+	switch {
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentNamespace, false):

Review comment:
       The same as above, too cases here. 

##########
File path: pkg/operator/injector/injector.go
##########
@@ -0,0 +1,487 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 injector
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strings"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+)
+
+const (
+	labelKeyagentInjector       = "swck-java-agent-injected"
+	defaultConfigmapNamespace   = "skywalking-swck-system"
+	agentConfigAnnotationPrefix = "agent.skywalking.apache.org/"
+	// If user want to use other Plugins' config ,the annotation must have the following form
+	// plugins.skywalking.apache.org/${config.name} = ${config.value}
+	// for example , if user want to enable plugin.mongodb.trace_param
+	// the annotation is plugins.skywalking.apache.org/plugin.mongodb.trace_param: "true"
+	otherPluginsAnnotationPrefix = "plugins.skywalking.apache.org/"
+)
+
+// log is for logging in this package.
+var log = logf.Log.WithName("injector")
+
+// SidecarInjectField contains all info that will be injected
+type SidecarInjectField struct {
+	// determine whether to inject , default is not to inject
+	needInject bool
+	// determine whether to use annotation to overlay agent config ,
+	// default is not to overlay,which means only use configmap to set agent config
+	// Otherwise, the way to overlay is set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar={config1}={value1},{config2}={value2}
+	agentOverlay bool
+	// initcontainer is a container that has the agent folder
+	initcontainer corev1.Container
+	// sidecarVolume is a shared directory between App's container and initcontainer
+	sidecarVolume corev1.Volume
+	// sidecarVolumeMount is a path that specifies a shared directory
+	sidecarVolumeMount corev1.VolumeMount
+	// configmapVolume is volume that provide user with configmap
+	configmapVolume corev1.Volume
+	// configmapVolumeMount is the configmap's mountpath for user
+	// Notice : the mount path will overwrite the original agent/config/agent.config
+	// So the mount path must match the path of agent.config in the image
+	configmapVolumeMount corev1.VolumeMount
+	// env is used to set java agent’s parameters
+	env corev1.EnvVar
+	// the string is used to set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar=jvmAgentConfigStr
+	jvmAgentConfigStr string
+	// determine which container to inject , default is to inject all containers
+	injectContainer string
+}
+
+// NewSidecarInjectField will create a new SidecarInjectField
+func NewSidecarInjectField() *SidecarInjectField {
+	return new(SidecarInjectField)
+}
+
+// Inject will do real injection
+func (s *SidecarInjectField) Inject(pod *corev1.Pod) {
+	// add initcontrainers to spec
+	if pod.Spec.InitContainers != nil {
+		pod.Spec.InitContainers = append(pod.Spec.InitContainers, s.initcontainer)
+	} else {
+		pod.Spec.InitContainers = []corev1.Container{s.initcontainer}
+	}
+
+	// add volume to spec
+	if pod.Spec.Volumes != nil {
+		pod.Spec.Volumes = append(pod.Spec.Volumes, s.sidecarVolume, s.configmapVolume)
+	} else {
+		pod.Spec.Volumes = []corev1.Volume{s.sidecarVolume, s.configmapVolume}
+	}
+
+	// add volumemount and env to container
+	for i, c := range pod.Spec.Containers {
+		// choose a specific container to inject
+		if s.injectContainer != "" && c.Name == s.injectContainer {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+			break
+		} else {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+		}
+	}
+}
+
+// GetInjectStrategy gets user's injection strategy
+func (s *SidecarInjectField) GetInjectStrategy(containers []corev1.Container, labels,
+	annotation *map[string]string) {
+	// set default value
+	s.needInject = false
+	s.injectContainer = ""
+	s.agentOverlay = false
+
+	// set needInject's value , if the pod has the label "swck-java-agent-injected=true",means need inject
+	if *labels == nil {
+		labels = new(map[string]string)
+		s.needInject = false
+	}
+
+	switch strings.ToLower((*labels)[labelKeyagentInjector]) {
+	case "true":
+		s.needInject = true
+	}
+
+	// set injectContainer's value
+	if *annotation == nil {
+		annotation = new(map[string]string)
+		s.injectContainer = ""
+		s.agentOverlay = false
+	}
+
+	// validate the container is or not exist
+	if v, ok := (*annotation)[AnnoInjectContainerName.Name]; ok {
+		for _, c := range containers {
+			if c.Name == v {
+				s.injectContainer = v
+				break
+			}
+		}
+	}
+
+	// set agentOverlay's value
+	switch strings.ToLower((*annotation)[AnnoAgentConfigOverlay.Name]) {
+	case "true":
+		s.agentOverlay = true
+	}
+
+}
+
+func (s *SidecarInjectField) injectErrorAnnotation(annotation *map[string]string, errorInfo string) {
+	(*annotation)[AnnoInjectErrorInfo.Name] = errorInfo
+}
+
+// SidecarOverlayandGetValue get final value of sidecar
+func (s *SidecarInjectField) SidecarOverlayandGetValue(as *Annotations, annotation *map[string]string,
+	a Annotation) (string, bool) {
+	if _, ok := (*annotation)[a.Name]; ok {
+		err := as.SetOverlay(annotation, a)
+		if err != nil {
+			s.injectErrorAnnotation(annotation, err.Error())
+			return "", false
+		}
+	}
+	return as.GetFinalValue(a), true
+}
+
+func (s *SidecarInjectField) setValue(config *string, as *Annotations, annotation *map[string]string,
+	a Annotation) bool {
+	if v, ok := s.SidecarOverlayandGetValue(as, annotation, a); ok {
+		*config = v
+		return true
+	}
+	return false
+}
+
+// OverlaySidecar overlays default config
+func (s *SidecarInjectField) OverlaySidecar(as *Annotations, annotation *map[string]string) bool {
+	var (
+		command       string
+		argOption     string
+		argCommand    string
+		configmapName string
+	)
+
+	switch {
+	case !s.setValue(&s.initcontainer.Name, as, annotation, AnnoInitcontainerName):
+		return false
+	case !s.setValue(&s.initcontainer.Image, as, annotation, AnnoInitcontainerImage):
+		return false
+	case !s.setValue(&command, as, annotation, AnnoInitcontainerCommand):
+		return false
+	case !s.setValue(&argOption, as, annotation, AnnoInitcontainerArgsOption):
+		return false
+	case !s.setValue(&argCommand, as, annotation, AnnoInitcontainerArgsCommand):
+		return false
+	case !s.setValue(&s.sidecarVolume.Name, as, annotation, AnnoSidecarVolumeName):
+		return false
+	case !s.setValue(&s.sidecarVolumeMount.Name, as, annotation, AnnoSidecarVolumeName):
+		return false
+	case !s.setValue(&s.sidecarVolumeMount.MountPath, as, annotation, AnnoSidecarVolumemountMountpath):
+		return false
+	case !s.setValue(&configmapName, as, annotation, AnnoConfigmapName):
+		return false
+	case !s.setValue(&s.configmapVolume.Name, as, annotation, AnnoConfigmapVolumeName):
+		return false
+	case !s.setValue(&s.configmapVolumeMount.Name, as, annotation, AnnoConfigmapVolumeName):
+		return false
+	case !s.setValue(&s.configmapVolumeMount.MountPath, as, annotation, AnnoConfigmapVolumemountMountpath):
+		return false
+	case !s.setValue(&s.env.Name, as, annotation, AnnoEnvVarName):
+		return false
+	case !s.setValue(&s.env.Value, as, annotation, AnnoEnvVarValue):
+		return false
+	}
+
+	s.initcontainer.Command = []string{command}
+	s.initcontainer.Args = []string{argOption, argCommand}
+	s.initcontainer.VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount}
+
+	// the sidecar volume's type is determined
+	s.sidecarVolume.VolumeSource.EmptyDir = nil
+
+	s.configmapVolume.VolumeSource = corev1.VolumeSource{
+		ConfigMap: &corev1.ConfigMapVolumeSource{
+			LocalObjectReference: corev1.LocalObjectReference{
+				Name: configmapName,
+			},
+		},
+	}
+
+	return true
+}
+
+// AgentOverlayandGetValue will do real annotation overlay
+func (s *SidecarInjectField) AgentOverlayandGetValue(as *Annotations, annotation *map[string]string,
+	a Annotation) (string, bool) {
+	if _, ok := (*annotation)[a.Name]; ok {
+		err := as.SetOverlay(annotation, a)
+		if err != nil {
+			s.injectErrorAnnotation(annotation, err.Error())
+			return "", false
+		}
+	}
+	return as.GetOverlayValue(a), true
+}
+
+func (s *SidecarInjectField) setJvmAgentStr(as *Annotations, annotation *map[string]string, a Annotation,
+	specialStr bool) bool {
+	v, ok := s.AgentOverlayandGetValue(as, annotation, a)
+	if v != "" && ok {
+		// get {config1}={value1}
+		configName := strings.TrimPrefix(a.Name, agentConfigAnnotationPrefix)
+		if specialStr {
+			v = strings.Join([]string{"'", "'"}, v)
+		}
+		config := strings.Join([]string{configName, v}, "=")
+
+		// add to jvmAgentConfigStr
+		if s.jvmAgentConfigStr != "" {
+			s.jvmAgentConfigStr = strings.Join([]string{s.jvmAgentConfigStr, config}, ",")
+		} else {
+			s.jvmAgentConfigStr = config
+		}
+	}
+	return ok
+}
+
+// OverlayAgentConfig overlays agent config
+func (s *SidecarInjectField) OverlayAgentConfig(as *Annotations, annotation *map[string]string) bool {
+	// jvmAgentConfigStr init
+	s.jvmAgentConfigStr = ""
+
+	switch {
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentNamespace, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentServiceName, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentSampleNumber, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentAuthentication, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentSpanLimit, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentIgnoreSuffix, true):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentIsOpenDebugging, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentIsCacheEnhaned, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentClassCache, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentOperationName, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentForceTLS, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileActive, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileMaxParallel, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileMaxDuration, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileDump, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileSnapshot, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentCollectorService, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingName, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingLevel, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingDir, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingMaxSize, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingMaxFiles, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentStatuscheckExceptions, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentStatuscheckDepth, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginMount, true):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentExcludePlugins, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginJdbc, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginKafkaServers, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginKafkaNamespace, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginSpringannotation, false):
+		return false
+	}
+
+	return true
+}
+
+// OverlayPluginsConfig will add Plugins' config to JvmAgentStr without verification
+// Notice, if a config is not in agent.config, it will be seen as a plugin config
+// user must ensure the accuracy of configuration.
+// Otherwides,If a separator(, or =) in the option or value, it should be wrapped in quotes.
+func (s *SidecarInjectField) OverlayPluginsConfig(annotation *map[string]string) bool {
+	for k, v := range *annotation {
+		if strings.HasPrefix(k, otherPluginsAnnotationPrefix) {
+			configName := strings.TrimPrefix(k, otherPluginsAnnotationPrefix)
+			config := strings.Join([]string{configName, v}, "=")
+			// add to jvmAgentConfigStr
+			if s.jvmAgentConfigStr != "" {
+				s.jvmAgentConfigStr = strings.Join([]string{s.jvmAgentConfigStr, config}, ",")
+			} else {
+				s.jvmAgentConfigStr = config
+			}
+		}
+	}
+	return true
+}
+
+// CreateConfigmap will find a configmap to set agent config
+// if not exist , then create a configmap
+func (s *SidecarInjectField) CreateConfigmap(ctx context.Context, kubeclient client.Client, namespace string,
+	annotation *map[string]string) bool {
+	configmap := &corev1.ConfigMap{}
+	configmapName := s.configmapVolume.VolumeSource.ConfigMap.LocalObjectReference.Name
+	// check whether the configmap is existed
+	err := kubeclient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: configmapName}, configmap)
+	// if configmap not exist , get configmap from defaultConfigmapNamespace
+	if err != nil {
+		defaultConfigmap := &corev1.ConfigMap{}
+		if err := kubeclient.Get(ctx, client.ObjectKey{Namespace: defaultConfigmapNamespace,
+			Name: AnnoConfigmapName.DefaultValue}, defaultConfigmap); err != nil {
+			log.Info(err.Error())

Review comment:
       To use `log.Error`

##########
File path: pkg/operator/injector/injector.go
##########
@@ -0,0 +1,487 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 injector
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strings"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+)
+
+const (
+	labelKeyagentInjector       = "swck-java-agent-injected"
+	defaultConfigmapNamespace   = "skywalking-swck-system"
+	agentConfigAnnotationPrefix = "agent.skywalking.apache.org/"
+	// If user want to use other Plugins' config ,the annotation must have the following form
+	// plugins.skywalking.apache.org/${config.name} = ${config.value}
+	// for example , if user want to enable plugin.mongodb.trace_param
+	// the annotation is plugins.skywalking.apache.org/plugin.mongodb.trace_param: "true"
+	otherPluginsAnnotationPrefix = "plugins.skywalking.apache.org/"
+)
+
+// log is for logging in this package.
+var log = logf.Log.WithName("injector")
+
+// SidecarInjectField contains all info that will be injected
+type SidecarInjectField struct {
+	// determine whether to inject , default is not to inject
+	needInject bool
+	// determine whether to use annotation to overlay agent config ,
+	// default is not to overlay,which means only use configmap to set agent config
+	// Otherwise, the way to overlay is set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar={config1}={value1},{config2}={value2}
+	agentOverlay bool
+	// initcontainer is a container that has the agent folder
+	initcontainer corev1.Container
+	// sidecarVolume is a shared directory between App's container and initcontainer
+	sidecarVolume corev1.Volume
+	// sidecarVolumeMount is a path that specifies a shared directory
+	sidecarVolumeMount corev1.VolumeMount
+	// configmapVolume is volume that provide user with configmap
+	configmapVolume corev1.Volume
+	// configmapVolumeMount is the configmap's mountpath for user
+	// Notice : the mount path will overwrite the original agent/config/agent.config
+	// So the mount path must match the path of agent.config in the image
+	configmapVolumeMount corev1.VolumeMount
+	// env is used to set java agent’s parameters
+	env corev1.EnvVar
+	// the string is used to set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar=jvmAgentConfigStr
+	jvmAgentConfigStr string
+	// determine which container to inject , default is to inject all containers
+	injectContainer string
+}
+
+// NewSidecarInjectField will create a new SidecarInjectField
+func NewSidecarInjectField() *SidecarInjectField {
+	return new(SidecarInjectField)
+}
+
+// Inject will do real injection
+func (s *SidecarInjectField) Inject(pod *corev1.Pod) {
+	// add initcontrainers to spec
+	if pod.Spec.InitContainers != nil {
+		pod.Spec.InitContainers = append(pod.Spec.InitContainers, s.initcontainer)
+	} else {
+		pod.Spec.InitContainers = []corev1.Container{s.initcontainer}
+	}
+
+	// add volume to spec
+	if pod.Spec.Volumes != nil {
+		pod.Spec.Volumes = append(pod.Spec.Volumes, s.sidecarVolume, s.configmapVolume)
+	} else {
+		pod.Spec.Volumes = []corev1.Volume{s.sidecarVolume, s.configmapVolume}
+	}
+
+	// add volumemount and env to container
+	for i, c := range pod.Spec.Containers {
+		// choose a specific container to inject
+		if s.injectContainer != "" && c.Name == s.injectContainer {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+			break
+		} else {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+		}
+	}
+}
+
+// GetInjectStrategy gets user's injection strategy
+func (s *SidecarInjectField) GetInjectStrategy(containers []corev1.Container, labels,
+	annotation *map[string]string) {
+	// set default value
+	s.needInject = false
+	s.injectContainer = ""
+	s.agentOverlay = false
+
+	// set needInject's value , if the pod has the label "swck-java-agent-injected=true",means need inject
+	if *labels == nil {
+		labels = new(map[string]string)
+		s.needInject = false
+	}
+
+	switch strings.ToLower((*labels)[labelKeyagentInjector]) {
+	case "true":
+		s.needInject = true
+	}
+
+	// set injectContainer's value
+	if *annotation == nil {
+		annotation = new(map[string]string)
+		s.injectContainer = ""
+		s.agentOverlay = false
+	}
+
+	// validate the container is or not exist
+	if v, ok := (*annotation)[AnnoInjectContainerName.Name]; ok {
+		for _, c := range containers {
+			if c.Name == v {
+				s.injectContainer = v
+				break
+			}
+		}
+	}
+
+	// set agentOverlay's value
+	switch strings.ToLower((*annotation)[AnnoAgentConfigOverlay.Name]) {
+	case "true":
+		s.agentOverlay = true
+	}
+
+}
+
+func (s *SidecarInjectField) injectErrorAnnotation(annotation *map[string]string, errorInfo string) {
+	(*annotation)[AnnoInjectErrorInfo.Name] = errorInfo
+}
+
+// SidecarOverlayandGetValue get final value of sidecar
+func (s *SidecarInjectField) SidecarOverlayandGetValue(as *Annotations, annotation *map[string]string,
+	a Annotation) (string, bool) {
+	if _, ok := (*annotation)[a.Name]; ok {
+		err := as.SetOverlay(annotation, a)
+		if err != nil {
+			s.injectErrorAnnotation(annotation, err.Error())
+			return "", false
+		}
+	}
+	return as.GetFinalValue(a), true
+}
+
+func (s *SidecarInjectField) setValue(config *string, as *Annotations, annotation *map[string]string,
+	a Annotation) bool {
+	if v, ok := s.SidecarOverlayandGetValue(as, annotation, a); ok {
+		*config = v
+		return true
+	}
+	return false
+}
+
+// OverlaySidecar overlays default config
+func (s *SidecarInjectField) OverlaySidecar(as *Annotations, annotation *map[string]string) bool {
+	var (
+		command       string
+		argOption     string
+		argCommand    string
+		configmapName string
+	)
+
+	switch {
+	case !s.setValue(&s.initcontainer.Name, as, annotation, AnnoInitcontainerName):
+		return false
+	case !s.setValue(&s.initcontainer.Image, as, annotation, AnnoInitcontainerImage):
+		return false
+	case !s.setValue(&command, as, annotation, AnnoInitcontainerCommand):
+		return false
+	case !s.setValue(&argOption, as, annotation, AnnoInitcontainerArgsOption):
+		return false
+	case !s.setValue(&argCommand, as, annotation, AnnoInitcontainerArgsCommand):
+		return false
+	case !s.setValue(&s.sidecarVolume.Name, as, annotation, AnnoSidecarVolumeName):
+		return false
+	case !s.setValue(&s.sidecarVolumeMount.Name, as, annotation, AnnoSidecarVolumeName):
+		return false
+	case !s.setValue(&s.sidecarVolumeMount.MountPath, as, annotation, AnnoSidecarVolumemountMountpath):
+		return false
+	case !s.setValue(&configmapName, as, annotation, AnnoConfigmapName):
+		return false
+	case !s.setValue(&s.configmapVolume.Name, as, annotation, AnnoConfigmapVolumeName):
+		return false
+	case !s.setValue(&s.configmapVolumeMount.Name, as, annotation, AnnoConfigmapVolumeName):
+		return false
+	case !s.setValue(&s.configmapVolumeMount.MountPath, as, annotation, AnnoConfigmapVolumemountMountpath):
+		return false
+	case !s.setValue(&s.env.Name, as, annotation, AnnoEnvVarName):
+		return false
+	case !s.setValue(&s.env.Value, as, annotation, AnnoEnvVarValue):
+		return false
+	}
+
+	s.initcontainer.Command = []string{command}
+	s.initcontainer.Args = []string{argOption, argCommand}
+	s.initcontainer.VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount}
+
+	// the sidecar volume's type is determined
+	s.sidecarVolume.VolumeSource.EmptyDir = nil
+
+	s.configmapVolume.VolumeSource = corev1.VolumeSource{
+		ConfigMap: &corev1.ConfigMapVolumeSource{
+			LocalObjectReference: corev1.LocalObjectReference{
+				Name: configmapName,
+			},
+		},
+	}
+
+	return true
+}
+
+// AgentOverlayandGetValue will do real annotation overlay
+func (s *SidecarInjectField) AgentOverlayandGetValue(as *Annotations, annotation *map[string]string,
+	a Annotation) (string, bool) {
+	if _, ok := (*annotation)[a.Name]; ok {
+		err := as.SetOverlay(annotation, a)
+		if err != nil {
+			s.injectErrorAnnotation(annotation, err.Error())
+			return "", false
+		}
+	}
+	return as.GetOverlayValue(a), true
+}
+
+func (s *SidecarInjectField) setJvmAgentStr(as *Annotations, annotation *map[string]string, a Annotation,
+	specialStr bool) bool {
+	v, ok := s.AgentOverlayandGetValue(as, annotation, a)
+	if v != "" && ok {
+		// get {config1}={value1}
+		configName := strings.TrimPrefix(a.Name, agentConfigAnnotationPrefix)
+		if specialStr {
+			v = strings.Join([]string{"'", "'"}, v)
+		}
+		config := strings.Join([]string{configName, v}, "=")
+
+		// add to jvmAgentConfigStr
+		if s.jvmAgentConfigStr != "" {
+			s.jvmAgentConfigStr = strings.Join([]string{s.jvmAgentConfigStr, config}, ",")
+		} else {
+			s.jvmAgentConfigStr = config
+		}
+	}
+	return ok
+}
+
+// OverlayAgentConfig overlays agent config
+func (s *SidecarInjectField) OverlayAgentConfig(as *Annotations, annotation *map[string]string) bool {
+	// jvmAgentConfigStr init
+	s.jvmAgentConfigStr = ""
+
+	switch {
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentNamespace, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentServiceName, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentSampleNumber, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentAuthentication, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentSpanLimit, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentIgnoreSuffix, true):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentIsOpenDebugging, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentIsCacheEnhaned, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentClassCache, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentOperationName, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentForceTLS, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileActive, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileMaxParallel, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileMaxDuration, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileDump, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileSnapshot, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentCollectorService, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingName, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingLevel, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingDir, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingMaxSize, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingMaxFiles, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentStatuscheckExceptions, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentStatuscheckDepth, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginMount, true):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentExcludePlugins, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginJdbc, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginKafkaServers, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginKafkaNamespace, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginSpringannotation, false):
+		return false
+	}
+
+	return true
+}
+
+// OverlayPluginsConfig will add Plugins' config to JvmAgentStr without verification
+// Notice, if a config is not in agent.config, it will be seen as a plugin config
+// user must ensure the accuracy of configuration.
+// Otherwides,If a separator(, or =) in the option or value, it should be wrapped in quotes.
+func (s *SidecarInjectField) OverlayPluginsConfig(annotation *map[string]string) bool {
+	for k, v := range *annotation {
+		if strings.HasPrefix(k, otherPluginsAnnotationPrefix) {
+			configName := strings.TrimPrefix(k, otherPluginsAnnotationPrefix)
+			config := strings.Join([]string{configName, v}, "=")
+			// add to jvmAgentConfigStr
+			if s.jvmAgentConfigStr != "" {
+				s.jvmAgentConfigStr = strings.Join([]string{s.jvmAgentConfigStr, config}, ",")
+			} else {
+				s.jvmAgentConfigStr = config
+			}
+		}
+	}
+	return true

Review comment:
       This func always returns `true`. Please remove the return value.

##########
File path: pkg/operator/injector/injector.go
##########
@@ -0,0 +1,487 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 injector
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strings"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+)
+
+const (
+	labelKeyagentInjector       = "swck-java-agent-injected"
+	defaultConfigmapNamespace   = "skywalking-swck-system"
+	agentConfigAnnotationPrefix = "agent.skywalking.apache.org/"
+	// If user want to use other Plugins' config ,the annotation must have the following form
+	// plugins.skywalking.apache.org/${config.name} = ${config.value}
+	// for example , if user want to enable plugin.mongodb.trace_param
+	// the annotation is plugins.skywalking.apache.org/plugin.mongodb.trace_param: "true"
+	otherPluginsAnnotationPrefix = "plugins.skywalking.apache.org/"
+)
+
+// log is for logging in this package.
+var log = logf.Log.WithName("injector")
+
+// SidecarInjectField contains all info that will be injected
+type SidecarInjectField struct {
+	// determine whether to inject , default is not to inject
+	needInject bool
+	// determine whether to use annotation to overlay agent config ,
+	// default is not to overlay,which means only use configmap to set agent config
+	// Otherwise, the way to overlay is set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar={config1}={value1},{config2}={value2}
+	agentOverlay bool
+	// initcontainer is a container that has the agent folder
+	initcontainer corev1.Container
+	// sidecarVolume is a shared directory between App's container and initcontainer
+	sidecarVolume corev1.Volume
+	// sidecarVolumeMount is a path that specifies a shared directory
+	sidecarVolumeMount corev1.VolumeMount
+	// configmapVolume is volume that provide user with configmap
+	configmapVolume corev1.Volume
+	// configmapVolumeMount is the configmap's mountpath for user
+	// Notice : the mount path will overwrite the original agent/config/agent.config
+	// So the mount path must match the path of agent.config in the image
+	configmapVolumeMount corev1.VolumeMount
+	// env is used to set java agent’s parameters
+	env corev1.EnvVar
+	// the string is used to set jvm agent ,just like following
+	// -javaagent: /sky/agent/skywalking-agent,jar=jvmAgentConfigStr
+	jvmAgentConfigStr string
+	// determine which container to inject , default is to inject all containers
+	injectContainer string
+}
+
+// NewSidecarInjectField will create a new SidecarInjectField
+func NewSidecarInjectField() *SidecarInjectField {
+	return new(SidecarInjectField)
+}
+
+// Inject will do real injection
+func (s *SidecarInjectField) Inject(pod *corev1.Pod) {
+	// add initcontrainers to spec
+	if pod.Spec.InitContainers != nil {
+		pod.Spec.InitContainers = append(pod.Spec.InitContainers, s.initcontainer)
+	} else {
+		pod.Spec.InitContainers = []corev1.Container{s.initcontainer}
+	}
+
+	// add volume to spec
+	if pod.Spec.Volumes != nil {
+		pod.Spec.Volumes = append(pod.Spec.Volumes, s.sidecarVolume, s.configmapVolume)
+	} else {
+		pod.Spec.Volumes = []corev1.Volume{s.sidecarVolume, s.configmapVolume}
+	}
+
+	// add volumemount and env to container
+	for i, c := range pod.Spec.Containers {
+		// choose a specific container to inject
+		if s.injectContainer != "" && c.Name == s.injectContainer {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+			break
+		} else {
+			if c.VolumeMounts != nil {
+				pod.Spec.Containers[i].VolumeMounts = append(pod.Spec.Containers[i].VolumeMounts,
+					s.sidecarVolumeMount, s.configmapVolumeMount)
+			} else {
+				pod.Spec.Containers[i].VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount,
+					s.configmapVolumeMount}
+			}
+			if c.Env != nil {
+				pod.Spec.Containers[i].Env = append(pod.Spec.Containers[i].Env, s.env)
+			} else {
+				pod.Spec.Containers[i].Env = []corev1.EnvVar{s.env}
+			}
+		}
+	}
+}
+
+// GetInjectStrategy gets user's injection strategy
+func (s *SidecarInjectField) GetInjectStrategy(containers []corev1.Container, labels,
+	annotation *map[string]string) {
+	// set default value
+	s.needInject = false
+	s.injectContainer = ""
+	s.agentOverlay = false
+
+	// set needInject's value , if the pod has the label "swck-java-agent-injected=true",means need inject
+	if *labels == nil {
+		labels = new(map[string]string)
+		s.needInject = false
+	}
+
+	switch strings.ToLower((*labels)[labelKeyagentInjector]) {
+	case "true":
+		s.needInject = true
+	}
+
+	// set injectContainer's value
+	if *annotation == nil {
+		annotation = new(map[string]string)
+		s.injectContainer = ""
+		s.agentOverlay = false
+	}
+
+	// validate the container is or not exist
+	if v, ok := (*annotation)[AnnoInjectContainerName.Name]; ok {
+		for _, c := range containers {
+			if c.Name == v {
+				s.injectContainer = v
+				break
+			}
+		}
+	}
+
+	// set agentOverlay's value
+	switch strings.ToLower((*annotation)[AnnoAgentConfigOverlay.Name]) {
+	case "true":
+		s.agentOverlay = true
+	}
+
+}
+
+func (s *SidecarInjectField) injectErrorAnnotation(annotation *map[string]string, errorInfo string) {
+	(*annotation)[AnnoInjectErrorInfo.Name] = errorInfo
+}
+
+// SidecarOverlayandGetValue get final value of sidecar
+func (s *SidecarInjectField) SidecarOverlayandGetValue(as *Annotations, annotation *map[string]string,
+	a Annotation) (string, bool) {
+	if _, ok := (*annotation)[a.Name]; ok {
+		err := as.SetOverlay(annotation, a)
+		if err != nil {
+			s.injectErrorAnnotation(annotation, err.Error())
+			return "", false
+		}
+	}
+	return as.GetFinalValue(a), true
+}
+
+func (s *SidecarInjectField) setValue(config *string, as *Annotations, annotation *map[string]string,
+	a Annotation) bool {
+	if v, ok := s.SidecarOverlayandGetValue(as, annotation, a); ok {
+		*config = v
+		return true
+	}
+	return false
+}
+
+// OverlaySidecar overlays default config
+func (s *SidecarInjectField) OverlaySidecar(as *Annotations, annotation *map[string]string) bool {
+	var (
+		command       string
+		argOption     string
+		argCommand    string
+		configmapName string
+	)
+
+	switch {
+	case !s.setValue(&s.initcontainer.Name, as, annotation, AnnoInitcontainerName):
+		return false
+	case !s.setValue(&s.initcontainer.Image, as, annotation, AnnoInitcontainerImage):
+		return false
+	case !s.setValue(&command, as, annotation, AnnoInitcontainerCommand):
+		return false
+	case !s.setValue(&argOption, as, annotation, AnnoInitcontainerArgsOption):
+		return false
+	case !s.setValue(&argCommand, as, annotation, AnnoInitcontainerArgsCommand):
+		return false
+	case !s.setValue(&s.sidecarVolume.Name, as, annotation, AnnoSidecarVolumeName):
+		return false
+	case !s.setValue(&s.sidecarVolumeMount.Name, as, annotation, AnnoSidecarVolumeName):
+		return false
+	case !s.setValue(&s.sidecarVolumeMount.MountPath, as, annotation, AnnoSidecarVolumemountMountpath):
+		return false
+	case !s.setValue(&configmapName, as, annotation, AnnoConfigmapName):
+		return false
+	case !s.setValue(&s.configmapVolume.Name, as, annotation, AnnoConfigmapVolumeName):
+		return false
+	case !s.setValue(&s.configmapVolumeMount.Name, as, annotation, AnnoConfigmapVolumeName):
+		return false
+	case !s.setValue(&s.configmapVolumeMount.MountPath, as, annotation, AnnoConfigmapVolumemountMountpath):
+		return false
+	case !s.setValue(&s.env.Name, as, annotation, AnnoEnvVarName):
+		return false
+	case !s.setValue(&s.env.Value, as, annotation, AnnoEnvVarValue):
+		return false
+	}
+
+	s.initcontainer.Command = []string{command}
+	s.initcontainer.Args = []string{argOption, argCommand}
+	s.initcontainer.VolumeMounts = []corev1.VolumeMount{s.sidecarVolumeMount}
+
+	// the sidecar volume's type is determined
+	s.sidecarVolume.VolumeSource.EmptyDir = nil
+
+	s.configmapVolume.VolumeSource = corev1.VolumeSource{
+		ConfigMap: &corev1.ConfigMapVolumeSource{
+			LocalObjectReference: corev1.LocalObjectReference{
+				Name: configmapName,
+			},
+		},
+	}
+
+	return true
+}
+
+// AgentOverlayandGetValue will do real annotation overlay
+func (s *SidecarInjectField) AgentOverlayandGetValue(as *Annotations, annotation *map[string]string,
+	a Annotation) (string, bool) {
+	if _, ok := (*annotation)[a.Name]; ok {
+		err := as.SetOverlay(annotation, a)
+		if err != nil {
+			s.injectErrorAnnotation(annotation, err.Error())
+			return "", false
+		}
+	}
+	return as.GetOverlayValue(a), true
+}
+
+func (s *SidecarInjectField) setJvmAgentStr(as *Annotations, annotation *map[string]string, a Annotation,
+	specialStr bool) bool {
+	v, ok := s.AgentOverlayandGetValue(as, annotation, a)
+	if v != "" && ok {
+		// get {config1}={value1}
+		configName := strings.TrimPrefix(a.Name, agentConfigAnnotationPrefix)
+		if specialStr {
+			v = strings.Join([]string{"'", "'"}, v)
+		}
+		config := strings.Join([]string{configName, v}, "=")
+
+		// add to jvmAgentConfigStr
+		if s.jvmAgentConfigStr != "" {
+			s.jvmAgentConfigStr = strings.Join([]string{s.jvmAgentConfigStr, config}, ",")
+		} else {
+			s.jvmAgentConfigStr = config
+		}
+	}
+	return ok
+}
+
+// OverlayAgentConfig overlays agent config
+func (s *SidecarInjectField) OverlayAgentConfig(as *Annotations, annotation *map[string]string) bool {
+	// jvmAgentConfigStr init
+	s.jvmAgentConfigStr = ""
+
+	switch {
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentNamespace, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentServiceName, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentSampleNumber, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentAuthentication, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentSpanLimit, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentIgnoreSuffix, true):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentIsOpenDebugging, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentIsCacheEnhaned, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentClassCache, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentOperationName, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentForceTLS, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileActive, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileMaxParallel, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileMaxDuration, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileDump, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentProfileSnapshot, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentCollectorService, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingName, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingLevel, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingDir, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingMaxSize, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentLoggingMaxFiles, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentStatuscheckExceptions, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentStatuscheckDepth, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginMount, true):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentExcludePlugins, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginJdbc, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginKafkaServers, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginKafkaNamespace, false):
+		return false
+	case !s.setJvmAgentStr(as, annotation, AnnoAgentPluginSpringannotation, false):
+		return false
+	}
+
+	return true
+}
+
+// OverlayPluginsConfig will add Plugins' config to JvmAgentStr without verification
+// Notice, if a config is not in agent.config, it will be seen as a plugin config
+// user must ensure the accuracy of configuration.
+// Otherwides,If a separator(, or =) in the option or value, it should be wrapped in quotes.
+func (s *SidecarInjectField) OverlayPluginsConfig(annotation *map[string]string) bool {
+	for k, v := range *annotation {
+		if strings.HasPrefix(k, otherPluginsAnnotationPrefix) {
+			configName := strings.TrimPrefix(k, otherPluginsAnnotationPrefix)
+			config := strings.Join([]string{configName, v}, "=")
+			// add to jvmAgentConfigStr
+			if s.jvmAgentConfigStr != "" {
+				s.jvmAgentConfigStr = strings.Join([]string{s.jvmAgentConfigStr, config}, ",")
+			} else {
+				s.jvmAgentConfigStr = config
+			}
+		}
+	}
+	return true
+}
+
+// CreateConfigmap will find a configmap to set agent config
+// if not exist , then create a configmap
+func (s *SidecarInjectField) CreateConfigmap(ctx context.Context, kubeclient client.Client, namespace string,
+	annotation *map[string]string) bool {
+	configmap := &corev1.ConfigMap{}
+	configmapName := s.configmapVolume.VolumeSource.ConfigMap.LocalObjectReference.Name
+	// check whether the configmap is existed
+	err := kubeclient.Get(ctx, client.ObjectKey{Namespace: namespace, Name: configmapName}, configmap)
+	// if configmap not exist , get configmap from defaultConfigmapNamespace
+	if err != nil {
+		defaultConfigmap := &corev1.ConfigMap{}
+		if err := kubeclient.Get(ctx, client.ObjectKey{Namespace: defaultConfigmapNamespace,
+			Name: AnnoConfigmapName.DefaultValue}, defaultConfigmap); err != nil {
+			log.Info(err.Error())
+			s.injectErrorAnnotation(annotation, fmt.Sprintf("get configmap %s from namespace %s error[%s]",
+				AnnoConfigmapName.DefaultValue, defaultConfigmapNamespace, err.Error()))
+			return false
+		}
+		// create new configmap and update namespace
+		injectConfigmap := corev1.ConfigMap{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      configmapName,
+				Namespace: namespace,
+			},
+			Data: defaultConfigmap.Data,
+		}
+
+		if err := kubeclient.Create(ctx, &injectConfigmap); err != nil {
+			log.Info(err.Error())
+			s.injectErrorAnnotation(annotation, fmt.Sprintf("create configmap %s in namespace %s error[%s]",
+				configmapName, namespace, err.Error()))
+			return false
+		}
+	}
+	return true
+}
+
+// PodInjector injects agent into Pods
+type PodInjector struct {
+	Client  client.Client
+	decoder *admission.Decoder
+}
+
+// Handle will process every coming pod under the
+// specified namespace which labeled "swck-injection=enabled"
+func (r *PodInjector) Handle(ctx context.Context, req admission.Request) admission.Response {
+	pod := &corev1.Pod{}
+
+	err := r.decoder.Decode(req, pod)
+	if err != nil {
+		return admission.Errored(http.StatusBadRequest, err)
+	}
+
+	// init Annotations to store the overlaied value
+	as := NewAnnotations()
+
+	// init SidecarInjectField and get injected strategy from annotations
+	s := NewSidecarInjectField()
+	s.GetInjectStrategy(pod.Spec.Containers, &pod.ObjectMeta.Labels, &pod.ObjectMeta.Annotations)
+
+	if s.needInject {
+		log.Info("will inject agent,please wait for a moment!")
+		// the overlation function is always on
+		s.OverlaySidecar(as, &pod.ObjectMeta.Annotations)
+		switch {
+		// only if user choose to overlay agent config in annotations, then start agent and plugins' overlay
+		case s.agentOverlay && (!s.OverlayAgentConfig(as, &pod.ObjectMeta.Annotations) ||
+			!s.OverlayPluginsConfig(&pod.ObjectMeta.Annotations)):
+			log.Info("overlay agent config error!please look the error annotation!")
+		// configmap will always overwrite the original agent.config in image
+		case !s.CreateConfigmap(ctx, r.Client, req.Namespace, &pod.ObjectMeta.Annotations):
+			log.Info("create configmap error!please look the error annotation!")
+		// add jvmStr to EnvVar and do real injection
+		default:
+			if s.jvmAgentConfigStr != "" {
+				s.env.Value = strings.Join([]string{s.env.Value, s.jvmAgentConfigStr}, "=")
+			}
+			s.Inject(pod)
+			log.Info("inject successfully!")
+		}
+	} else {
+		// if the pod don't have the label "swck-java-agent-injected=true",return ok
+		log.Info("don't inject agent!")

Review comment:
       remove `!` from logs.

##########
File path: pkg/operator/injector/injector.go
##########
@@ -0,0 +1,487 @@
+// Licensed to Apache Software Foundation (ASF) under one or more contributor
+// license agreements. See the NOTICE file distributed with
+// this work for additional information regarding copyright
+// ownership. Apache Software Foundation (ASF) licenses this file to you 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 injector
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strings"
+
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	logf "sigs.k8s.io/controller-runtime/pkg/log"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+)
+
+const (
+	labelKeyagentInjector       = "swck-java-agent-injected"
+	defaultConfigmapNamespace   = "skywalking-swck-system"

Review comment:
       Users can change it when installing the operator. Please add a controller to watch a specific configmap as the default configmap template.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: notifications-unsubscribe@skywalking.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org