istio.io/istio@v0.0.0-20240520182934-d79c90f27776/istioctl/pkg/kubeinject/kubeinject.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package kubeinject 16 17 import ( 18 "bytes" 19 "context" 20 "crypto/tls" 21 "crypto/x509" 22 "encoding/json" 23 "errors" 24 "fmt" 25 "io" 26 "net/http" 27 "os" 28 "sort" 29 "strings" 30 "time" 31 32 "github.com/hashicorp/go-multierror" 33 "github.com/spf13/cobra" 34 admission "k8s.io/api/admission/v1" 35 admissionv1beta1 "k8s.io/api/admission/v1beta1" 36 admissionregistration "k8s.io/api/admissionregistration/v1" 37 corev1 "k8s.io/api/core/v1" 38 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 39 "k8s.io/apimachinery/pkg/runtime" 40 "k8s.io/apimachinery/pkg/runtime/serializer" 41 v1 "k8s.io/client-go/kubernetes/typed/core/v1" 42 "k8s.io/kubectl/pkg/polymorphichelpers" 43 "k8s.io/kubectl/pkg/util/podutils" 44 "sigs.k8s.io/yaml" 45 46 "istio.io/api/label" 47 meshconfig "istio.io/api/mesh/v1alpha1" 48 "istio.io/istio/istioctl/pkg/cli" 49 "istio.io/istio/istioctl/pkg/clioptions" 50 "istio.io/istio/istioctl/pkg/util" 51 iopv1alpha1 "istio.io/istio/operator/pkg/apis/istio/v1alpha1" 52 "istio.io/istio/operator/pkg/manifest" 53 "istio.io/istio/operator/pkg/validate" 54 "istio.io/istio/pkg/config/mesh" 55 "istio.io/istio/pkg/kube" 56 "istio.io/istio/pkg/kube/inject" 57 "istio.io/istio/pkg/log" 58 "istio.io/istio/pkg/util/protomarshal" 59 "istio.io/istio/pkg/version" 60 ) 61 62 const ( 63 configMapKey = "mesh" 64 injectConfigMapKey = "config" 65 valuesConfigMapKey = "values" 66 ) 67 68 type ExternalInjector struct { 69 client kube.CLIClient 70 clientConfig *admissionregistration.WebhookClientConfig 71 injectorAddress string 72 } 73 74 func (e ExternalInjector) Inject(pod *corev1.Pod, deploymentNS string) ([]byte, error) { 75 cc := e.clientConfig 76 if cc == nil { 77 return nil, nil 78 } 79 var address string 80 if cc.URL != nil { 81 address = *cc.URL 82 } 83 var certPool *x509.CertPool 84 if len(cc.CABundle) > 0 { 85 certPool = x509.NewCertPool() 86 certPool.AppendCertsFromPEM(cc.CABundle) 87 } else { 88 var err error 89 certPool, err = x509.SystemCertPool() 90 if err != nil { 91 return nil, err 92 } 93 } 94 tlsClientConfig := &tls.Config{RootCAs: certPool, MinVersion: tls.VersionTLS12} 95 client := http.Client{ 96 Timeout: time.Second * 5, 97 Transport: &http.Transport{ 98 TLSClientConfig: tlsClientConfig, 99 }, 100 } 101 if cc.Service != nil { 102 svc, err := e.client.Kube().CoreV1().Services(cc.Service.Namespace).Get(context.Background(), cc.Service.Name, metav1.GetOptions{}) 103 if err != nil { 104 return nil, err 105 } 106 namespace, selector, err := polymorphichelpers.SelectorsForObject(svc) 107 if err != nil { 108 if e.injectorAddress == "" { 109 return nil, fmt.Errorf("cannot attach to %T: %v", svc, err) 110 } 111 address = fmt.Sprintf("https://%s:%d%s", e.injectorAddress, *cc.Service.Port, *cc.Service.Path) 112 } else { 113 pod, err := GetFirstPod(e.client.Kube().CoreV1(), namespace, selector.String()) 114 if err != nil { 115 return nil, err 116 } 117 webhookPort := cc.Service.Port 118 podPort := 15017 119 for _, v := range svc.Spec.Ports { 120 if v.Port == *webhookPort { 121 podPort = v.TargetPort.IntValue() 122 break 123 } 124 } 125 f, err := e.client.NewPortForwarder(pod.Name, pod.Namespace, "", 0, podPort) 126 if err != nil { 127 return nil, err 128 } 129 if err := f.Start(); err != nil { 130 return nil, err 131 } 132 address = fmt.Sprintf("https://%s%s", f.Address(), *cc.Service.Path) 133 defer func() { 134 f.Close() 135 f.WaitForStop() 136 }() 137 } 138 tlsClientConfig.ServerName = fmt.Sprintf("%s.%s.%s", cc.Service.Name, cc.Service.Namespace, "svc") 139 } else if isMCPAddr(address) { 140 var err error 141 client.Transport, err = mcpTransport(context.TODO(), client.Transport) 142 if err != nil { 143 return nil, err 144 } 145 } 146 podBytes, err := json.Marshal(pod) 147 if pod.Namespace != "" { 148 deploymentNS = pod.Namespace 149 } 150 if err != nil { 151 return nil, err 152 } 153 rev := &admission.AdmissionReview{ 154 TypeMeta: metav1.TypeMeta{ 155 APIVersion: admission.SchemeGroupVersion.String(), 156 Kind: "AdmissionReview", 157 }, 158 Request: &admission.AdmissionRequest{ 159 Object: runtime.RawExtension{Raw: podBytes}, 160 Kind: metav1.GroupVersionKind{ 161 Group: admission.GroupName, 162 Version: admission.SchemeGroupVersion.Version, 163 Kind: "AdmissionRequest", 164 }, 165 Resource: metav1.GroupVersionResource{}, 166 SubResource: "", 167 RequestKind: nil, 168 RequestResource: nil, 169 RequestSubResource: "", 170 Name: pod.Name, 171 Namespace: deploymentNS, 172 }, 173 Response: nil, 174 } 175 revBytes, err := json.Marshal(rev) 176 if err != nil { 177 return nil, err 178 } 179 resp, err := client.Post(address, "application/json", bytes.NewBuffer(revBytes)) 180 if err != nil { 181 return nil, err 182 } 183 defer resp.Body.Close() 184 body, err := io.ReadAll(resp.Body) 185 if err != nil { 186 return nil, err 187 } 188 var obj runtime.Object 189 var ar *kube.AdmissionReview 190 out, _, err := deserializer.Decode(body, nil, obj) 191 if err != nil { 192 return nil, fmt.Errorf("could not decode body: %v", err) 193 } 194 ar, err = kube.AdmissionReviewKubeToAdapter(out) 195 if err != nil { 196 return nil, fmt.Errorf("could not decode object: %v", err) 197 } 198 199 return ar.Response.Patch, nil 200 } 201 202 var ( 203 runtimeScheme = func() *runtime.Scheme { 204 r := runtime.NewScheme() 205 r.AddKnownTypes(admissionv1beta1.SchemeGroupVersion, &admissionv1beta1.AdmissionReview{}) 206 r.AddKnownTypes(admission.SchemeGroupVersion, &admission.AdmissionReview{}) 207 return r 208 }() 209 codecs = serializer.NewCodecFactory(runtimeScheme) 210 deserializer = codecs.UniversalDeserializer() 211 ) 212 213 // GetFirstPod returns a pod matching the namespace and label selector 214 // and the number of all pods that match the label selector. 215 // This is forked from polymorphichelpers.GetFirstPod to not watch and instead return an error if no pods are found 216 func GetFirstPod(client v1.CoreV1Interface, namespace string, selector string) (*corev1.Pod, error) { 217 options := metav1.ListOptions{LabelSelector: selector} 218 219 sortBy := func(pods []*corev1.Pod) sort.Interface { return sort.Reverse(podutils.ActivePods(pods)) } 220 podList, err := client.Pods(namespace).List(context.TODO(), options) 221 if err != nil { 222 return nil, err 223 } 224 pods := make([]*corev1.Pod, 0, len(podList.Items)) 225 for i := range podList.Items { 226 pod := podList.Items[i] 227 pods = append(pods, &pod) 228 } 229 if len(pods) > 0 { 230 sort.Sort(sortBy(pods)) 231 return pods[0], nil 232 } 233 return nil, fmt.Errorf("no pods matching selector %q found in namespace %q", selector, namespace) 234 } 235 236 func getMeshConfigFromConfigMap(ctx cli.Context, command, revision string) (*meshconfig.MeshConfig, error) { 237 client, err := ctx.CLIClient() 238 if err != nil { 239 return nil, err 240 } 241 242 if meshConfigMapName == defaultMeshConfigMapName && revision != "" { 243 meshConfigMapName = fmt.Sprintf("%s-%s", defaultMeshConfigMapName, revision) 244 } 245 meshConfigMap, err := client.Kube().CoreV1().ConfigMaps(ctx.IstioNamespace()).Get(context.TODO(), meshConfigMapName, metav1.GetOptions{}) 246 if err != nil { 247 return nil, fmt.Errorf("could not read valid configmap %q from namespace %q: %v - "+ 248 "Use --meshConfigFile or re-run "+command+" with `-i <istioSystemNamespace> and ensure valid MeshConfig exists", 249 meshConfigMapName, ctx.IstioNamespace(), err) 250 } 251 // values in the data are strings, while proto might use a 252 // different data type. therefore, we have to get a value by a 253 // key 254 configYaml, exists := meshConfigMap.Data[configMapKey] 255 if !exists { 256 return nil, fmt.Errorf("missing configuration map key %q", configMapKey) 257 } 258 cfg, err := mesh.ApplyMeshConfigDefaults(configYaml) 259 if err != nil { 260 err = multierror.Append(err, fmt.Errorf("istioctl version %s cannot parse mesh config. Install istioctl from the latest Istio release", 261 version.Info.Version)) 262 } 263 return cfg, err 264 } 265 266 // grabs the raw values from the ConfigMap. These are encoded as JSON. 267 func GetValuesFromConfigMap(ctx cli.Context, revision string) (string, error) { 268 client, err := ctx.CLIClient() 269 if err != nil { 270 return "", err 271 } 272 273 if revision != "" { 274 injectConfigMapName = fmt.Sprintf("%s-%s", defaultInjectConfigMapName, revision) 275 } 276 meshConfigMap, err := client.Kube().CoreV1().ConfigMaps(ctx.IstioNamespace()).Get(context.TODO(), injectConfigMapName, metav1.GetOptions{}) 277 if err != nil { 278 return "", fmt.Errorf("could not find valid configmap %q from namespace %q: %v - "+ 279 "Use --valuesFile or re-run kube-inject with `-i <istioSystemNamespace> and ensure istio-sidecar-injector configmap exists", 280 injectConfigMapName, ctx.IstioNamespace(), err) 281 } 282 283 valuesData, exists := meshConfigMap.Data[valuesConfigMapKey] 284 if !exists { 285 return "", fmt.Errorf("missing configuration map key %q in %q", 286 valuesConfigMapKey, injectConfigMapName) 287 } 288 289 return valuesData, nil 290 } 291 292 func readInjectConfigFile(f []byte) (inject.RawTemplates, error) { 293 var injectConfig inject.Config 294 err := yaml.Unmarshal(f, &injectConfig) 295 if err != nil || len(injectConfig.RawTemplates) == 0 { 296 // This must be a direct template, instead of an inject.Config. We support both formats 297 return map[string]string{inject.SidecarTemplateName: string(f)}, nil 298 } 299 cfg, err := inject.UnmarshalConfig(f) 300 if err != nil { 301 return nil, err 302 } 303 return cfg.RawTemplates, err 304 } 305 306 func getInjectConfigFromConfigMap(ctx cli.Context, revision string) (inject.RawTemplates, error) { 307 client, err := ctx.CLIClient() 308 if err != nil { 309 return nil, err 310 } 311 312 if injectConfigMapName == defaultInjectConfigMapName && revision != "" { 313 injectConfigMapName = fmt.Sprintf("%s-%s", defaultInjectConfigMapName, revision) 314 } 315 meshConfigMap, err := client.Kube().CoreV1().ConfigMaps(ctx.IstioNamespace()).Get(context.TODO(), injectConfigMapName, metav1.GetOptions{}) 316 if err != nil { 317 return nil, fmt.Errorf("could not find valid configmap %q from namespace %q: %v - "+ 318 "Use --injectConfigFile or re-run kube-inject with `-i <istioSystemNamespace>` and ensure istio-sidecar-injector configmap exists", 319 injectConfigMapName, ctx.IstioNamespace(), err) 320 } 321 // values in the data are strings, while proto might use a 322 // different data type. therefore, we have to get a value by a 323 // key 324 injectData, exists := meshConfigMap.Data[injectConfigMapKey] 325 if !exists { 326 return nil, fmt.Errorf("missing configuration map key %q in %q", 327 injectConfigMapKey, injectConfigMapName) 328 } 329 injectConfig, err := inject.UnmarshalConfig([]byte(injectData)) 330 if err != nil { 331 return nil, fmt.Errorf("unable to convert data from configmap %q: %v", 332 injectConfigMapName, err) 333 } 334 log.Debugf("using inject template from configmap %q", injectConfigMapName) 335 return injectConfig.RawTemplates, nil 336 } 337 338 func setUpExternalInjector(ctx cli.Context, revision, injectorAddress string) (*ExternalInjector, error) { 339 e := &ExternalInjector{} 340 client, err := ctx.CLIClient() 341 if err != nil { 342 return e, err 343 } 344 if revision == "" { 345 revision = "default" 346 } 347 whcList, err := client.Kube().AdmissionregistrationV1().MutatingWebhookConfigurations().List(context.TODO(), 348 metav1.ListOptions{LabelSelector: fmt.Sprintf("%s=%s", label.IoIstioRev.Name, revision)}) 349 if err != nil { 350 return e, fmt.Errorf("could not find valid mutatingWebhookConfiguration %q from cluster %v", 351 whcName, err) 352 } 353 if whcList != nil && len(whcList.Items) != 0 { 354 for _, wh := range whcList.Items[0].Webhooks { 355 if strings.HasSuffix(wh.Name, defaultWebhookName) { 356 return &ExternalInjector{client, &wh.ClientConfig, injectorAddress}, nil 357 } 358 } 359 } 360 return e, fmt.Errorf("could not find valid mutatingWebhookConfiguration %q from cluster", defaultWebhookName) 361 } 362 363 func validateFlags() error { 364 var err error 365 if inFilename == "" { 366 err = multierror.Append(err, errors.New("filename not specified (see --filename or -f)")) 367 } 368 if meshConfigFile == "" && meshConfigMapName == "" && iopFilename == "" { 369 err = multierror.Append(err, 370 errors.New("--meshConfigFile or --meshConfigMapName or --operatorFileName must be set")) 371 } 372 return err 373 } 374 375 func setupKubeInjectParameters(cliContext cli.Context, sidecarTemplate *inject.RawTemplates, valuesConfig *string, 376 revision, injectorAddress string, 377 ) (*ExternalInjector, *meshconfig.MeshConfig, error) { 378 var err error 379 // Get configs from IOP files firstly, and if not exists, get configs from files and configmaps. 380 values, meshConfig, err := getIOPConfigs() 381 if err != nil { 382 return nil, nil, err 383 } 384 if meshConfig == nil { 385 if meshConfigFile != "" { 386 if meshConfig, err = mesh.ReadMeshConfig(meshConfigFile); err != nil { 387 return nil, nil, err 388 } 389 } else { 390 if meshConfig, err = getMeshConfigFromConfigMap(cliContext, "kube-inject", revision); err != nil { 391 return nil, nil, err 392 } 393 } 394 } 395 396 injector := &ExternalInjector{} 397 if injectConfigFile != "" { 398 injectionConfig, err := os.ReadFile(injectConfigFile) // nolint: vetshadow 399 if err != nil { 400 return nil, nil, err 401 } 402 injectConfig, err := readInjectConfigFile(injectionConfig) 403 if err != nil { 404 return nil, nil, multierror.Append(err, fmt.Errorf("loading --injectConfigFile")) 405 } 406 *sidecarTemplate = injectConfig 407 } else { 408 injector, err = setUpExternalInjector(cliContext, revision, injectorAddress) 409 if err != nil || injector.clientConfig == nil { 410 log.Warnf("failed to get injection config from mutatingWebhookConfigurations %q, will fall back to "+ 411 "get injection from the injection configmap %q : %v", whcName, defaultInjectWebhookConfigName, err) 412 if *sidecarTemplate, err = getInjectConfigFromConfigMap(cliContext, revision); err != nil { 413 return nil, nil, err 414 } 415 } 416 return injector, meshConfig, nil 417 } 418 419 if values != "" { 420 *valuesConfig = values 421 } 422 if valuesConfig == nil || *valuesConfig == "" { 423 if valuesFile != "" { 424 valuesConfigBytes, err := os.ReadFile(valuesFile) // nolint: vetshadow 425 if err != nil { 426 return nil, nil, err 427 } 428 *valuesConfig = string(valuesConfigBytes) 429 } else if *valuesConfig, err = GetValuesFromConfigMap(cliContext, revision); err != nil { 430 return nil, nil, err 431 } 432 } 433 return injector, meshConfig, err 434 } 435 436 // getIOPConfigs gets the configs in IOPs. 437 func getIOPConfigs() (string, *meshconfig.MeshConfig, error) { 438 var meshConfig *meshconfig.MeshConfig 439 var valuesConfig string 440 if iopFilename != "" { 441 var iop *iopv1alpha1.IstioOperator 442 y, err := manifest.ReadLayeredYAMLs([]string{iopFilename}) 443 if err != nil { 444 return "", nil, err 445 } 446 iop, err = validate.UnmarshalIOP(y) 447 if err != nil { 448 return "", nil, err 449 } 450 if err := validate.ValidIOP(iop); err != nil { 451 return "", nil, fmt.Errorf("validation errors: \n%s", err) 452 } 453 if err != nil { 454 return "", nil, err 455 } 456 if iop.Spec.Values != nil { 457 values, err := protomarshal.ToJSON(iop.Spec.Values) 458 if err != nil { 459 return "", nil, err 460 } 461 valuesConfig = values 462 } 463 if iop.Spec.MeshConfig != nil { 464 meshConfigYaml, err := protomarshal.ToYAML(iop.Spec.MeshConfig) 465 if err != nil { 466 return "", nil, err 467 } 468 meshConfig, err = mesh.ApplyMeshConfigDefaults(meshConfigYaml) 469 if err != nil { 470 return "", nil, err 471 } 472 } 473 } 474 return valuesConfig, meshConfig, nil 475 } 476 477 var ( 478 inFilename string 479 outFilename string 480 meshConfigFile string 481 meshConfigMapName string 482 valuesFile string 483 injectConfigFile string 484 injectConfigMapName string 485 whcName string 486 iopFilename string 487 ) 488 489 const ( 490 defaultMeshConfigMapName = "istio" 491 defaultInjectConfigMapName = "istio-sidecar-injector" 492 defaultInjectWebhookConfigName = "istio-sidecar-injector" 493 defaultWebhookName = "sidecar-injector.istio.io" 494 ) 495 496 func InjectCommand(cliContext cli.Context) *cobra.Command { 497 var opts clioptions.ControlPlaneOptions 498 var centralOpts clioptions.CentralControlPlaneOptions 499 500 injectCmd := &cobra.Command{ 501 Use: "kube-inject", 502 Short: "Inject Istio sidecar into Kubernetes pod resources", 503 Long: ` 504 kube-inject manually injects the Istio sidecar into Kubernetes 505 workloads. Unsupported resources are left unmodified so it is safe to 506 run kube-inject over a single file that contains multiple Service, 507 ConfigMap, Deployment, etc. definitions for a complex application. When in 508 doubt re-run istioctl kube-inject on deployments to get the most up-to-date changes. 509 510 It's best to do kube-inject when the resource is initially created. 511 `, 512 Example: ` # Update resources on the fly before applying. 513 kubectl apply -f <(istioctl kube-inject -f <resource.yaml>) 514 515 # Create a persistent version of the deployment with Istio sidecar injected. 516 istioctl kube-inject -f deployment.yaml -o deployment-injected.yaml 517 518 # Update an existing deployment. 519 kubectl get deployment -o yaml | istioctl kube-inject -f - | kubectl apply -f - 520 521 # Capture cluster configuration for later use with kube-inject 522 kubectl -n istio-system get cm istio-sidecar-injector -o jsonpath="{.data.config}" > /tmp/inj-template.tmpl 523 kubectl -n istio-system get cm istio -o jsonpath="{.data.mesh}" > /tmp/mesh.yaml 524 kubectl -n istio-system get cm istio-sidecar-injector -o jsonpath="{.data.values}" > /tmp/values.json 525 526 # Use kube-inject based on captured configuration 527 istioctl kube-inject -f samples/bookinfo/platform/kube/bookinfo.yaml \ 528 --injectConfigFile /tmp/inj-template.tmpl \ 529 --meshConfigFile /tmp/mesh.yaml \ 530 --valuesFile /tmp/values.json 531 `, 532 RunE: func(c *cobra.Command, _ []string) (err error) { 533 if err = validateFlags(); err != nil { 534 return err 535 } 536 var reader io.Reader 537 538 if inFilename == "-" { 539 reader = os.Stdin 540 } else { 541 var in *os.File 542 if in, err = os.Open(inFilename); err != nil { 543 return err 544 } 545 reader = in 546 defer func() { 547 if errClose := in.Close(); errClose != nil { 548 log.Errorf("Error: close file from %s, %s", inFilename, errClose) 549 550 // don't overwrite the previous error 551 if err == nil { 552 err = errClose 553 } 554 } 555 }() 556 } 557 558 var writer io.Writer 559 if outFilename == "" { 560 writer = c.OutOrStdout() 561 } else { 562 var out *os.File 563 if out, err = os.Create(outFilename); err != nil { 564 return err 565 } 566 writer = out 567 defer func() { 568 if errClose := out.Close(); errClose != nil { 569 log.Errorf("Error: close file from %s, %s", outFilename, errClose) 570 571 // don't overwrite the previous error 572 if err == nil { 573 err = errClose 574 } 575 } 576 }() 577 } 578 var valuesConfig string 579 var sidecarTemplate inject.RawTemplates 580 var meshConfig *meshconfig.MeshConfig 581 rev := opts.Revision 582 // if the revision is "default", render templates with an empty revision 583 if rev == util.DefaultRevisionName { 584 rev = "" 585 } 586 injectorAddress := centralOpts.Xds 587 index := strings.IndexByte(injectorAddress, ':') 588 if index != -1 { 589 injectorAddress = injectorAddress[:index] 590 } 591 injector, meshConfig, err := setupKubeInjectParameters(cliContext, &sidecarTemplate, &valuesConfig, rev, injectorAddress) 592 if err != nil { 593 return err 594 } 595 if injector.client == nil && meshConfig == nil { 596 return fmt.Errorf( 597 "failed to get injection config from mutatingWebhookConfigurations and injection configmap - " + 598 "check injection configmap or pass --revision flag", 599 ) 600 } 601 var warnings []string 602 templs, err := inject.ParseTemplates(sidecarTemplate) 603 if err != nil { 604 return err 605 } 606 vc, err := inject.NewValuesConfig(valuesConfig) 607 if err != nil { 608 return err 609 } 610 retval := inject.IntoResourceFile(injector, templs, vc, rev, meshConfig, 611 reader, writer, func(warning string) { 612 warnings = append(warnings, warning) 613 }) 614 if len(warnings) > 0 { 615 fmt.Fprintln(c.ErrOrStderr()) 616 } 617 for _, warning := range warnings { 618 fmt.Fprintln(c.ErrOrStderr(), warning) 619 } 620 return retval 621 }, 622 PersistentPreRunE: func(c *cobra.Command, args []string) error { 623 // istioctl kube-inject is typically redirected to a .yaml file; 624 // the default for log messages should be stderr, not stdout 625 root := c.Root() 626 if root != nil { 627 _ = c.Root().PersistentFlags().Set("log_target", "stderr") 628 } 629 if c.Parent() != nil && c.Parent().PersistentPreRunE != nil { 630 return c.Parent().PersistentPreRunE(c, args) 631 } 632 633 return nil 634 }, 635 } 636 637 injectCmd.PersistentFlags().StringVar(&meshConfigFile, "meshConfigFile", "", 638 "Mesh configuration filename. Takes precedence over --meshConfigMapName if set") 639 injectCmd.PersistentFlags().StringVar(&injectConfigFile, "injectConfigFile", "", 640 "Injection configuration filename. Cannot be used with --injectConfigMapName") 641 injectCmd.PersistentFlags().StringVar(&valuesFile, "valuesFile", "", 642 "Injection values configuration filename.") 643 644 injectCmd.PersistentFlags().StringVarP(&inFilename, "filename", "f", 645 "", "Input Kubernetes resource filename") 646 injectCmd.PersistentFlags().StringVarP(&outFilename, "output", "o", 647 "", "Modified output Kubernetes resource filename") 648 injectCmd.PersistentFlags().StringVar(&iopFilename, "operatorFileName", "", 649 "Path to file containing IstioOperator custom resources. If configs from files like "+ 650 "meshConfigFile, valuesFile are provided, they will be overridden by iop config values.") 651 652 injectCmd.PersistentFlags().StringVar(&meshConfigMapName, "meshConfigMapName", defaultMeshConfigMapName, 653 fmt.Sprintf("ConfigMap name for Istio mesh configuration, key should be %q", configMapKey)) 654 injectCmd.PersistentFlags().StringVar(&injectConfigMapName, "injectConfigMapName", defaultInjectConfigMapName, 655 fmt.Sprintf("ConfigMap name for Istio sidecar injection, key should be %q.", injectConfigMapKey)) 656 _ = injectCmd.PersistentFlags().MarkHidden("injectConfigMapName") 657 injectCmd.PersistentFlags().StringVar(&whcName, "webhookConfig", defaultInjectWebhookConfigName, 658 "MutatingWebhookConfiguration name for Istio") 659 opts.AttachControlPlaneFlags(injectCmd) 660 centralOpts.AttachControlPlaneFlags(injectCmd) 661 return injectCmd 662 }