| // Copyright Istio Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package cmd |
| |
| import ( |
| "bytes" |
| "context" |
| "crypto/tls" |
| "crypto/x509" |
| "encoding/json" |
| "errors" |
| "fmt" |
| "io" |
| "net/http" |
| "os" |
| "sort" |
| "strings" |
| "time" |
| ) |
| |
| import ( |
| "github.com/hashicorp/go-multierror" |
| "github.com/spf13/cobra" |
| "istio.io/api/label" |
| meshconfig "istio.io/api/mesh/v1alpha1" |
| "istio.io/pkg/log" |
| "istio.io/pkg/version" |
| admission "k8s.io/api/admission/v1" |
| admissionv1beta1 "k8s.io/api/admission/v1beta1" |
| admissionregistration "k8s.io/api/admissionregistration/v1" |
| corev1 "k8s.io/api/core/v1" |
| metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" |
| "k8s.io/apimachinery/pkg/runtime" |
| "k8s.io/apimachinery/pkg/runtime/serializer" |
| "k8s.io/client-go/kubernetes" |
| v1 "k8s.io/client-go/kubernetes/typed/core/v1" |
| "k8s.io/kubectl/pkg/polymorphichelpers" |
| "k8s.io/kubectl/pkg/util/podutils" |
| "sigs.k8s.io/yaml" |
| ) |
| |
| import ( |
| "github.com/apache/dubbo-go-pixiu/istioctl/pkg/clioptions" |
| "github.com/apache/dubbo-go-pixiu/istioctl/pkg/tag" |
| iopv1alpha1 "github.com/apache/dubbo-go-pixiu/operator/pkg/apis/istio/v1alpha1" |
| "github.com/apache/dubbo-go-pixiu/operator/pkg/manifest" |
| "github.com/apache/dubbo-go-pixiu/operator/pkg/validate" |
| "github.com/apache/dubbo-go-pixiu/pkg/config/mesh" |
| "github.com/apache/dubbo-go-pixiu/pkg/kube" |
| "github.com/apache/dubbo-go-pixiu/pkg/kube/inject" |
| "github.com/apache/dubbo-go-pixiu/pkg/util/protomarshal" |
| ) |
| |
| const ( |
| configMapKey = "mesh" |
| injectConfigMapKey = "config" |
| valuesConfigMapKey = "values" |
| ) |
| |
| type ExternalInjector struct { |
| client kube.ExtendedClient |
| clientConfig *admissionregistration.WebhookClientConfig |
| injectorAddress string |
| } |
| |
| func (e ExternalInjector) Inject(pod *corev1.Pod, deploymentNS string) ([]byte, error) { |
| cc := e.clientConfig |
| if cc == nil { |
| return nil, nil |
| } |
| var address string |
| if cc.URL != nil { |
| address = *cc.URL |
| } |
| var certPool *x509.CertPool |
| if len(cc.CABundle) > 0 { |
| certPool = x509.NewCertPool() |
| certPool.AppendCertsFromPEM(cc.CABundle) |
| } else { |
| var err error |
| certPool, err = x509.SystemCertPool() |
| if err != nil { |
| return nil, err |
| } |
| } |
| tlsClientConfig := &tls.Config{RootCAs: certPool} |
| client := http.Client{ |
| Timeout: time.Second * 5, |
| Transport: &http.Transport{ |
| TLSClientConfig: tlsClientConfig, |
| }, |
| } |
| if cc.Service != nil { |
| svc, err := e.client.CoreV1().Services(cc.Service.Namespace).Get(context.Background(), cc.Service.Name, metav1.GetOptions{}) |
| if err != nil { |
| return nil, err |
| } |
| namespace, selector, err := polymorphichelpers.SelectorsForObject(svc) |
| if err != nil { |
| if e.injectorAddress == "" { |
| return nil, fmt.Errorf("cannot attach to %T: %v", svc, err) |
| } |
| address = fmt.Sprintf("https://%s:%d%s", e.injectorAddress, *cc.Service.Port, *cc.Service.Path) |
| } else { |
| pod, err := GetFirstPod(e.client.CoreV1(), namespace, selector.String()) |
| if err != nil { |
| return nil, err |
| } |
| webhookPort := cc.Service.Port |
| podPort := 15017 |
| for _, v := range svc.Spec.Ports { |
| if v.Port == *webhookPort { |
| podPort = v.TargetPort.IntValue() |
| break |
| } |
| } |
| f, err := e.client.NewPortForwarder(pod.Name, pod.Namespace, "", 0, podPort) |
| if err != nil { |
| return nil, err |
| } |
| if err := f.Start(); err != nil { |
| return nil, err |
| } |
| address = fmt.Sprintf("https://%s%s", f.Address(), *cc.Service.Path) |
| defer func() { |
| f.Close() |
| f.WaitForStop() |
| }() |
| } |
| tlsClientConfig.ServerName = fmt.Sprintf("%s.%s.%s", cc.Service.Name, cc.Service.Namespace, "svc") |
| } else if isMCPAddr(address) { |
| var err error |
| client.Transport, err = mcpTransport(context.TODO(), client.Transport) |
| if err != nil { |
| return nil, err |
| } |
| } |
| podBytes, err := json.Marshal(pod) |
| if pod.Namespace != "" { |
| deploymentNS = pod.Namespace |
| } |
| if err != nil { |
| return nil, err |
| } |
| rev := &admission.AdmissionReview{ |
| TypeMeta: metav1.TypeMeta{ |
| APIVersion: admission.SchemeGroupVersion.String(), |
| Kind: "AdmissionReview", |
| }, |
| Request: &admission.AdmissionRequest{ |
| Object: runtime.RawExtension{Raw: podBytes}, |
| Kind: metav1.GroupVersionKind{ |
| Group: admission.GroupName, |
| Version: admission.SchemeGroupVersion.Version, |
| Kind: "AdmissionRequest", |
| }, |
| Resource: metav1.GroupVersionResource{}, |
| SubResource: "", |
| RequestKind: nil, |
| RequestResource: nil, |
| RequestSubResource: "", |
| Name: pod.Name, |
| Namespace: deploymentNS, |
| }, |
| Response: nil, |
| } |
| revBytes, err := json.Marshal(rev) |
| if err != nil { |
| return nil, err |
| } |
| resp, err := client.Post(address, "application/json", bytes.NewBuffer(revBytes)) |
| if err != nil { |
| return nil, err |
| } |
| defer resp.Body.Close() |
| body, err := io.ReadAll(resp.Body) |
| if err != nil { |
| return nil, err |
| } |
| var obj runtime.Object |
| var ar *kube.AdmissionReview |
| out, _, err := deserializer.Decode(body, nil, obj) |
| if err != nil { |
| return nil, fmt.Errorf("could not decode body: %v", err) |
| } |
| ar, err = kube.AdmissionReviewKubeToAdapter(out) |
| if err != nil { |
| return nil, fmt.Errorf("could not decode object: %v", err) |
| } |
| |
| return ar.Response.Patch, nil |
| } |
| |
| var ( |
| runtimeScheme = func() *runtime.Scheme { |
| r := runtime.NewScheme() |
| r.AddKnownTypes(admissionv1beta1.SchemeGroupVersion, &admissionv1beta1.AdmissionReview{}) |
| r.AddKnownTypes(admission.SchemeGroupVersion, &admission.AdmissionReview{}) |
| return r |
| }() |
| codecs = serializer.NewCodecFactory(runtimeScheme) |
| deserializer = codecs.UniversalDeserializer() |
| ) |
| |
| // GetFirstPod returns a pod matching the namespace and label selector |
| // and the number of all pods that match the label selector. |
| // This is forked from polymorphichelpers.GetFirstPod to not watch and instead return an error if no pods are found |
| func GetFirstPod(client v1.CoreV1Interface, namespace string, selector string) (*corev1.Pod, error) { |
| options := metav1.ListOptions{LabelSelector: selector} |
| |
| sortBy := func(pods []*corev1.Pod) sort.Interface { return sort.Reverse(podutils.ActivePods(pods)) } |
| podList, err := client.Pods(namespace).List(context.TODO(), options) |
| if err != nil { |
| return nil, err |
| } |
| pods := make([]*corev1.Pod, 0, len(podList.Items)) |
| for i := range podList.Items { |
| pod := podList.Items[i] |
| pods = append(pods, &pod) |
| } |
| if len(pods) > 0 { |
| sort.Sort(sortBy(pods)) |
| return pods[0], nil |
| } |
| return nil, fmt.Errorf("no pods matching selector %q found in namespace %q", selector, namespace) |
| } |
| |
| func createInterface(kubeconfig string) (kubernetes.Interface, error) { |
| restConfig, err := kube.BuildClientConfig(kubeconfig, configContext) |
| if err != nil { |
| return nil, err |
| } |
| return kubernetes.NewForConfig(restConfig) |
| } |
| |
| func getMeshConfigFromConfigMap(kubeconfig, command, revision string) (*meshconfig.MeshConfig, error) { |
| client, err := createInterface(kubeconfig) |
| if err != nil { |
| return nil, err |
| } |
| |
| if meshConfigMapName == defaultMeshConfigMapName && revision != "" { |
| meshConfigMapName = fmt.Sprintf("%s-%s", defaultMeshConfigMapName, revision) |
| } |
| meshConfigMap, err := client.CoreV1().ConfigMaps(istioNamespace).Get(context.TODO(), meshConfigMapName, metav1.GetOptions{}) |
| if err != nil { |
| return nil, fmt.Errorf("could not read valid configmap %q from namespace %q: %v - "+ |
| "Use --meshConfigFile or re-run "+command+" with `-i <istioSystemNamespace> and ensure valid MeshConfig exists", |
| meshConfigMapName, istioNamespace, err) |
| } |
| // values in the data are strings, while proto might use a |
| // different data type. therefore, we have to get a value by a |
| // key |
| configYaml, exists := meshConfigMap.Data[configMapKey] |
| if !exists { |
| return nil, fmt.Errorf("missing configuration map key %q", configMapKey) |
| } |
| cfg, err := mesh.ApplyMeshConfigDefaults(configYaml) |
| if err != nil { |
| err = multierror.Append(err, fmt.Errorf("istioctl version %s cannot parse mesh config. Install istioctl from the latest Istio release", |
| version.Info.Version)) |
| } |
| return cfg, err |
| } |
| |
| // grabs the raw values from the ConfigMap. These are encoded as JSON. |
| func getValuesFromConfigMap(kubeconfig, revision string) (string, error) { |
| client, err := createInterface(kubeconfig) |
| if err != nil { |
| return "", err |
| } |
| |
| if revision != "" { |
| injectConfigMapName = fmt.Sprintf("%s-%s", defaultInjectConfigMapName, revision) |
| } |
| meshConfigMap, err := client.CoreV1().ConfigMaps(istioNamespace).Get(context.TODO(), injectConfigMapName, metav1.GetOptions{}) |
| if err != nil { |
| return "", fmt.Errorf("could not find valid configmap %q from namespace %q: %v - "+ |
| "Use --valuesFile or re-run kube-inject with `-i <istioSystemNamespace> and ensure istio-sidecar-injector configmap exists", |
| injectConfigMapName, istioNamespace, err) |
| } |
| |
| valuesData, exists := meshConfigMap.Data[valuesConfigMapKey] |
| if !exists { |
| return "", fmt.Errorf("missing configuration map key %q in %q", |
| valuesConfigMapKey, injectConfigMapName) |
| } |
| |
| return valuesData, nil |
| } |
| |
| func readInjectConfigFile(f []byte) (inject.RawTemplates, error) { |
| var injectConfig inject.Config |
| err := yaml.Unmarshal(f, &injectConfig) |
| if err != nil || len(injectConfig.RawTemplates) == 0 { |
| // This must be a direct template, instead of an inject.Config. We support both formats |
| return map[string]string{inject.SidecarTemplateName: string(f)}, nil |
| } |
| cfg, err := inject.UnmarshalConfig(f) |
| if err != nil { |
| return nil, err |
| } |
| return cfg.RawTemplates, err |
| } |
| |
| func getInjectConfigFromConfigMap(kubeconfig, revision string) (inject.RawTemplates, error) { |
| client, err := createInterface(kubeconfig) |
| if err != nil { |
| return nil, err |
| } |
| |
| if injectConfigMapName == defaultInjectConfigMapName && revision != "" { |
| injectConfigMapName = fmt.Sprintf("%s-%s", defaultInjectConfigMapName, revision) |
| } |
| meshConfigMap, err := client.CoreV1().ConfigMaps(istioNamespace).Get(context.TODO(), injectConfigMapName, metav1.GetOptions{}) |
| if err != nil { |
| return nil, fmt.Errorf("could not find valid configmap %q from namespace %q: %v - "+ |
| "Use --injectConfigFile or re-run kube-inject with `-i <istioSystemNamespace>` and ensure istio-sidecar-injector configmap exists", |
| injectConfigMapName, istioNamespace, err) |
| } |
| // values in the data are strings, while proto might use a |
| // different data type. therefore, we have to get a value by a |
| // key |
| injectData, exists := meshConfigMap.Data[injectConfigMapKey] |
| if !exists { |
| return nil, fmt.Errorf("missing configuration map key %q in %q", |
| injectConfigMapKey, injectConfigMapName) |
| } |
| injectConfig, err := inject.UnmarshalConfig([]byte(injectData)) |
| if err != nil { |
| return nil, fmt.Errorf("unable to convert data from configmap %q: %v", |
| injectConfigMapName, err) |
| } |
| log.Debugf("using inject template from configmap %q", injectConfigMapName) |
| return injectConfig.RawTemplates, nil |
| } |
| |
| func setUpExternalInjector(kubeconfig, revision, injectorAddress string) (*ExternalInjector, error) { |
| e := &ExternalInjector{} |
| client, err := kube.NewExtendedClient(kube.BuildClientCmd(kubeconfig, configContext), "") |
| if err != nil { |
| return e, err |
| } |
| if revision == "" { |
| revision = "default" |
| } |
| whcList, err := client.AdmissionregistrationV1().MutatingWebhookConfigurations().List(context.TODO(), |
| metav1.ListOptions{LabelSelector: fmt.Sprintf("%s=%s", label.IoIstioRev.Name, revision)}) |
| if err != nil { |
| return e, fmt.Errorf("could not find valid mutatingWebhookConfiguration %q from cluster %v", |
| whcName, err) |
| } |
| if whcList != nil && len(whcList.Items) != 0 { |
| for _, wh := range whcList.Items[0].Webhooks { |
| if strings.HasSuffix(wh.Name, defaultWebhookName) { |
| return &ExternalInjector{client, &wh.ClientConfig, injectorAddress}, nil |
| } |
| } |
| } |
| return e, fmt.Errorf("could not find valid mutatingWebhookConfiguration %q from cluster", defaultWebhookName) |
| } |
| |
| func validateFlags() error { |
| var err error |
| if inFilename == "" { |
| err = multierror.Append(err, errors.New("filename not specified (see --filename or -f)")) |
| } |
| if meshConfigFile == "" && meshConfigMapName == "" && iopFilename == "" { |
| err = multierror.Append(err, |
| errors.New("--meshConfigFile or --meshConfigMapName or --operatorFileName must be set")) |
| } |
| return err |
| } |
| |
| func setupKubeInjectParameters(sidecarTemplate *inject.RawTemplates, valuesConfig *string, |
| revision, injectorAddress string) (*ExternalInjector, *meshconfig.MeshConfig, error) { |
| var err error |
| injector := &ExternalInjector{} |
| if injectConfigFile != "" { |
| injectionConfig, err := os.ReadFile(injectConfigFile) // nolint: vetshadow |
| if err != nil { |
| return nil, nil, err |
| } |
| injectConfig, err := readInjectConfigFile(injectionConfig) |
| if err != nil { |
| return nil, nil, multierror.Append(err, fmt.Errorf("loading --injectConfigFile")) |
| } |
| *sidecarTemplate = injectConfig |
| } else { |
| injector, err = setUpExternalInjector(kubeconfig, revision, injectorAddress) |
| if err != nil || injector.clientConfig == nil { |
| log.Warnf("failed to get injection config from mutatingWebhookConfigurations %q, will fall back to "+ |
| "get injection from the injection configmap %q : %v", whcName, defaultInjectWebhookConfigName, err) |
| if *sidecarTemplate, err = getInjectConfigFromConfigMap(kubeconfig, revision); err != nil { |
| return nil, nil, err |
| } |
| } |
| return injector, nil, nil |
| } |
| |
| // Get configs from IOP files firstly, and if not exists, get configs from files and configmaps. |
| values, meshConfig, err := getIOPConfigs() |
| if err != nil { |
| return nil, nil, err |
| } |
| if meshConfig == nil { |
| if meshConfigFile != "" { |
| if meshConfig, err = mesh.ReadMeshConfig(meshConfigFile); err != nil { |
| return nil, nil, err |
| } |
| } else { |
| if meshConfig, err = getMeshConfigFromConfigMap(kubeconfig, "kube-inject", revision); err != nil { |
| return nil, nil, err |
| } |
| } |
| } |
| |
| if values != "" { |
| *valuesConfig = values |
| } |
| if valuesConfig == nil || *valuesConfig == "" { |
| if valuesFile != "" { |
| valuesConfigBytes, err := os.ReadFile(valuesFile) // nolint: vetshadow |
| if err != nil { |
| return nil, nil, err |
| } |
| *valuesConfig = string(valuesConfigBytes) |
| } else if *valuesConfig, err = getValuesFromConfigMap(kubeconfig, revision); err != nil { |
| return nil, nil, err |
| } |
| } |
| return injector, meshConfig, err |
| } |
| |
| // getIOPConfigs gets the configs in IOPs. |
| func getIOPConfigs() (string, *meshconfig.MeshConfig, error) { |
| var meshConfig *meshconfig.MeshConfig |
| var valuesConfig string |
| if iopFilename != "" { |
| var iop *iopv1alpha1.IstioOperator |
| y, err := manifest.ReadLayeredYAMLs([]string{iopFilename}) |
| if err != nil { |
| return "", nil, err |
| } |
| iop, err = validate.UnmarshalIOP(y) |
| if err != nil { |
| return "", nil, err |
| } |
| if err := validate.ValidIOP(iop); err != nil { |
| return "", nil, fmt.Errorf("validation errors: \n%s", err) |
| } |
| if err != nil { |
| return "", nil, err |
| } |
| if iop.Spec.Values != nil { |
| values, err := protomarshal.ToJSON(iop.Spec.Values) |
| if err != nil { |
| return "", nil, err |
| } |
| valuesConfig = values |
| } |
| if iop.Spec.MeshConfig != nil { |
| meshConfigYaml, err := protomarshal.ToYAML(iop.Spec.MeshConfig) |
| if err != nil { |
| return "", nil, err |
| } |
| meshConfig, err = mesh.ApplyMeshConfigDefaults(meshConfigYaml) |
| if err != nil { |
| return "", nil, err |
| } |
| } |
| } |
| return valuesConfig, meshConfig, nil |
| } |
| |
| var ( |
| inFilename string |
| outFilename string |
| meshConfigFile string |
| meshConfigMapName string |
| valuesFile string |
| injectConfigFile string |
| injectConfigMapName string |
| whcName string |
| iopFilename string |
| ) |
| |
| const ( |
| defaultMeshConfigMapName = "istio" |
| defaultMeshConfigMapKey = "mesh" |
| defaultInjectConfigMapName = "istio-sidecar-injector" |
| defaultInjectWebhookConfigName = "istio-sidecar-injector" |
| defaultWebhookName = "sidecar-injector.istio.io" |
| ) |
| |
| func injectCommand() *cobra.Command { |
| var opts clioptions.ControlPlaneOptions |
| var centralOpts clioptions.CentralControlPlaneOptions |
| |
| injectCmd := &cobra.Command{ |
| Use: "kube-inject", |
| Short: "Inject Istio sidecar into Kubernetes pod resources", |
| Long: ` |
| kube-inject manually injects the Istio sidecar into Kubernetes |
| workloads. Unsupported resources are left unmodified so it is safe to |
| run kube-inject over a single file that contains multiple Service, |
| ConfigMap, Deployment, etc. definitions for a complex application. When in |
| doubt re-run istioctl kube-inject on deployments to get the most up-to-date changes. |
| |
| It's best to do kube-inject when the resource is initially created. |
| `, |
| Example: ` # Update resources on the fly before applying. |
| kubectl apply -f <(istioctl kube-inject -f <resource.yaml>) |
| |
| # Create a persistent version of the deployment with Istio sidecar injected. |
| istioctl kube-inject -f deployment.yaml -o deployment-injected.yaml |
| |
| # Update an existing deployment. |
| kubectl get deployment -o yaml | istioctl kube-inject -f - | kubectl apply -f - |
| |
| # Capture cluster configuration for later use with kube-inject |
| kubectl -n dubbo-system get cm istio-sidecar-injector -o jsonpath="{.data.config}" > /tmp/inj-template.tmpl |
| kubectl -n dubbo-system get cm istio -o jsonpath="{.data.mesh}" > /tmp/mesh.yaml |
| kubectl -n dubbo-system get cm istio-sidecar-injector -o jsonpath="{.data.values}" > /tmp/values.json |
| |
| # Use kube-inject based on captured configuration |
| istioctl kube-inject -f samples/bookinfo/platform/kube/bookinfo.yaml \ |
| --injectConfigFile /tmp/inj-template.tmpl \ |
| --meshConfigFile /tmp/mesh.yaml \ |
| --valuesFile /tmp/values.json |
| `, |
| RunE: func(c *cobra.Command, _ []string) (err error) { |
| if err = validateFlags(); err != nil { |
| return err |
| } |
| var reader io.Reader |
| |
| if inFilename == "-" { |
| reader = os.Stdin |
| } else { |
| var in *os.File |
| if in, err = os.Open(inFilename); err != nil { |
| return err |
| } |
| reader = in |
| defer func() { |
| if errClose := in.Close(); errClose != nil { |
| log.Errorf("Error: close file from %s, %s", inFilename, errClose) |
| |
| // don't overwrite the previous error |
| if err == nil { |
| err = errClose |
| } |
| } |
| }() |
| } |
| |
| var writer io.Writer |
| if outFilename == "" { |
| writer = c.OutOrStdout() |
| } else { |
| var out *os.File |
| if out, err = os.Create(outFilename); err != nil { |
| return err |
| } |
| writer = out |
| defer func() { |
| if errClose := out.Close(); errClose != nil { |
| log.Errorf("Error: close file from %s, %s", outFilename, errClose) |
| |
| // don't overwrite the previous error |
| if err == nil { |
| err = errClose |
| } |
| } |
| }() |
| } |
| var valuesConfig string |
| var sidecarTemplate inject.RawTemplates |
| var meshConfig *meshconfig.MeshConfig |
| rev := opts.Revision |
| // if the revision is "default", render templates with an empty revision |
| if rev == tag.DefaultRevisionName { |
| rev = "" |
| } |
| injectorAddress := centralOpts.Xds |
| index := strings.IndexByte(injectorAddress, ':') |
| if index != -1 { |
| injectorAddress = injectorAddress[:index] |
| } |
| injector, meshConfig, err := setupKubeInjectParameters(&sidecarTemplate, &valuesConfig, rev, injectorAddress) |
| if err != nil { |
| return err |
| } |
| if injector.client == nil && meshConfig == nil { |
| return fmt.Errorf( |
| "failed to get injection config from mutatingWebhookConfigurations and injection configmap - " + |
| "check injection configmap or pass --revision flag", |
| ) |
| } |
| var warnings []string |
| templs, err := inject.ParseTemplates(sidecarTemplate) |
| if err != nil { |
| return err |
| } |
| vc, err := inject.NewValuesConfig(valuesConfig) |
| if err != nil { |
| return err |
| } |
| retval := inject.IntoResourceFile(injector, templs, vc, rev, meshConfig, |
| reader, writer, func(warning string) { |
| warnings = append(warnings, warning) |
| }) |
| if len(warnings) > 0 { |
| fmt.Fprintln(c.ErrOrStderr()) |
| } |
| for _, warning := range warnings { |
| fmt.Fprintln(c.ErrOrStderr(), warning) |
| } |
| return retval |
| }, |
| PersistentPreRunE: func(c *cobra.Command, args []string) error { |
| // istioctl kube-inject is typically redirected to a .yaml file; |
| // the default for log messages should be stderr, not stdout |
| _ = c.Root().PersistentFlags().Set("log_target", "stderr") |
| |
| return c.Parent().PersistentPreRunE(c, args) |
| }, |
| } |
| |
| injectCmd.PersistentFlags().StringVar(&meshConfigFile, "meshConfigFile", "", |
| "Mesh configuration filename. Takes precedence over --meshConfigMapName if set") |
| injectCmd.PersistentFlags().StringVar(&injectConfigFile, "injectConfigFile", "", |
| "Injection configuration filename. Cannot be used with --injectConfigMapName") |
| injectCmd.PersistentFlags().StringVar(&valuesFile, "valuesFile", "", |
| "Injection values configuration filename.") |
| |
| injectCmd.PersistentFlags().StringVarP(&inFilename, "filename", "f", |
| "", "Input Kubernetes resource filename") |
| injectCmd.PersistentFlags().StringVarP(&outFilename, "output", "o", |
| "", "Modified output Kubernetes resource filename") |
| injectCmd.PersistentFlags().StringVar(&iopFilename, "operatorFileName", "", |
| "Path to file containing IstioOperator custom resources. If configs from files like "+ |
| "meshConfigFile, valuesFile are provided, they will be overridden by iop config values.") |
| |
| injectCmd.PersistentFlags().StringVar(&meshConfigMapName, "meshConfigMapName", defaultMeshConfigMapName, |
| fmt.Sprintf("ConfigMap name for Istio mesh configuration, key should be %q", configMapKey)) |
| injectCmd.PersistentFlags().StringVar(&injectConfigMapName, "injectConfigMapName", defaultInjectConfigMapName, |
| fmt.Sprintf("ConfigMap name for Istio sidecar injection, key should be %q.", injectConfigMapKey)) |
| _ = injectCmd.PersistentFlags().MarkHidden("injectConfigMapName") |
| injectCmd.PersistentFlags().StringVar(&whcName, "webhookConfig", defaultInjectWebhookConfigName, |
| "MutatingWebhookConfiguration name for Istio") |
| opts.AttachControlPlaneFlags(injectCmd) |
| centralOpts.AttachControlPlaneFlags(injectCmd) |
| return injectCmd |
| } |