istio.io/istio@v0.0.0-20240520182934-d79c90f27776/istioctl/pkg/proxystatus/proxystatus.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 proxystatus
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"io"
    21  	"os"
    22  
    23  	discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
    24  	"github.com/spf13/cobra"
    25  
    26  	"istio.io/istio/istioctl/pkg/cli"
    27  	"istio.io/istio/istioctl/pkg/clioptions"
    28  	"istio.io/istio/istioctl/pkg/completion"
    29  	"istio.io/istio/istioctl/pkg/multixds"
    30  	"istio.io/istio/istioctl/pkg/util/ambient"
    31  	"istio.io/istio/istioctl/pkg/writer/compare"
    32  	"istio.io/istio/istioctl/pkg/writer/pilot"
    33  	pilotxds "istio.io/istio/pilot/pkg/xds"
    34  	"istio.io/istio/pkg/log"
    35  )
    36  
    37  var configDumpFile string
    38  
    39  func readConfigFile(filename string) ([]byte, error) {
    40  	file := os.Stdin
    41  	if filename != "-" {
    42  		var err error
    43  		file, err = os.Open(filename)
    44  		if err != nil {
    45  			return nil, err
    46  		}
    47  	}
    48  	defer func() {
    49  		if err := file.Close(); err != nil {
    50  			log.Errorf("failed to close %s: %s", filename, err)
    51  		}
    52  	}()
    53  	data, err := io.ReadAll(file)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  	return data, nil
    58  }
    59  
    60  func StableXdsStatusCommand(ctx cli.Context) *cobra.Command {
    61  	cmd := XdsStatusCommand(ctx)
    62  	unstableFlags := []string{"xds-via-agents", "xds-via-agents-limit"}
    63  	cmd.PreRunE = func(cmd *cobra.Command, args []string) error {
    64  		for _, flag := range unstableFlags {
    65  			if cmd.PersistentFlags().Changed(flag) {
    66  				return fmt.Errorf("--%s is experimental. Use `istioctl experimental ps --%s`", flag, flag)
    67  			}
    68  		}
    69  		return nil
    70  	}
    71  	for _, flag := range unstableFlags {
    72  		_ = cmd.PersistentFlags().MarkHidden(flag)
    73  	}
    74  	return cmd
    75  }
    76  
    77  func XdsStatusCommand(ctx cli.Context) *cobra.Command {
    78  	var opts clioptions.ControlPlaneOptions
    79  	var centralOpts clioptions.CentralControlPlaneOptions
    80  	var multiXdsOpts multixds.Options
    81  
    82  	statusCmd := &cobra.Command{
    83  		Use:   "proxy-status [<type>/]<name>[.<namespace>]",
    84  		Short: "Retrieves the synchronization status of each Envoy in the mesh",
    85  		Long: `
    86  Retrieves last sent and last acknowledged xDS sync from Istiod to each Envoy in the mesh
    87  `,
    88  		Example: `  # Retrieve sync status for all Envoys in a mesh
    89    istioctl proxy-status
    90  
    91    # Retrieve sync status for Envoys in a specific namespace
    92    istioctl proxy-status --namespace foo
    93  
    94    # Retrieve sync diff for a single Envoy and Istiod
    95    istioctl proxy-status istio-egressgateway-59585c5b9c-ndc59.istio-system
    96  
    97    # SECURITY OPTIONS
    98  
    99    # Retrieve proxy status information directly from the control plane, using token security
   100    # (This is the usual way to get the proxy-status with an out-of-cluster control plane.)
   101    istioctl ps --xds-address istio.cloudprovider.example.com:15012
   102  
   103    # Retrieve proxy status information via Kubernetes config, using token security
   104    # (This is the usual way to get the proxy-status with an in-cluster control plane.)
   105    istioctl proxy-status
   106  
   107    # Retrieve proxy status information directly from the control plane, using RSA certificate security
   108    # (Certificates must be obtained before this step.  The --cert-dir flag lets istioctl bypass the Kubernetes API server.)
   109    istioctl ps --xds-address istio.example.com:15012 --cert-dir ~/.istio-certs
   110  
   111    # Retrieve proxy status information via XDS from specific control plane in multi-control plane in-cluster configuration
   112    # (Select a specific control plane in an in-cluster canary Istio configuration.)
   113    istioctl ps --xds-label istio.io/rev=default
   114  `,
   115  		Aliases: []string{"ps"},
   116  		RunE: func(c *cobra.Command, args []string) error {
   117  			kubeClient, err := ctx.CLIClientWithRevision(opts.Revision)
   118  			if err != nil {
   119  				return err
   120  			}
   121  			multiXdsOpts.MessageWriter = c.OutOrStdout()
   122  
   123  			if len(args) > 0 {
   124  				podName, ns, err := ctx.InferPodInfoFromTypedResource(args[0], ctx.Namespace())
   125  				if err != nil {
   126  					return err
   127  				}
   128  				if ambient.IsZtunnelPod(kubeClient, podName, ns) {
   129  					_, _ = fmt.Fprintf(c.OutOrStdout(),
   130  						"Sync diff is not available for ztunnel pod %s.%s\n", podName, ns)
   131  					return nil
   132  				}
   133  				var envoyDump []byte
   134  				if configDumpFile != "" {
   135  					envoyDump, err = readConfigFile(configDumpFile)
   136  				} else {
   137  					path := "config_dump"
   138  					envoyDump, err = kubeClient.EnvoyDo(context.TODO(), podName, ns, "GET", path)
   139  				}
   140  				if err != nil {
   141  					return fmt.Errorf("could not contact sidecar: %w", err)
   142  				}
   143  
   144  				xdsRequest := discovery.DiscoveryRequest{
   145  					ResourceNames: []string{fmt.Sprintf("%s.%s", podName, ns)},
   146  					TypeUrl:       pilotxds.TypeDebugConfigDump,
   147  				}
   148  				xdsResponses, err := multixds.FirstRequestAndProcessXds(&xdsRequest, centralOpts, ctx.IstioNamespace(), "", "", kubeClient, multiXdsOpts)
   149  				if err != nil {
   150  					return err
   151  				}
   152  				c, err := compare.NewXdsComparator(c.OutOrStdout(), xdsResponses, envoyDump)
   153  				if err != nil {
   154  					return err
   155  				}
   156  				return c.Diff()
   157  			}
   158  			xdsRequest := discovery.DiscoveryRequest{
   159  				TypeUrl: pilotxds.TypeDebugSyncronization,
   160  			}
   161  			xdsResponses, err := multixds.AllRequestAndProcessXds(&xdsRequest, centralOpts, ctx.IstioNamespace(), "", "", kubeClient, multiXdsOpts)
   162  			if err != nil {
   163  				return err
   164  			}
   165  			sw := pilot.XdsStatusWriter{
   166  				Writer:    c.OutOrStdout(),
   167  				Namespace: ctx.Namespace(),
   168  			}
   169  			return sw.PrintAll(xdsResponses)
   170  		},
   171  		ValidArgsFunction: completion.ValidPodsNameArgs(ctx),
   172  	}
   173  
   174  	opts.AttachControlPlaneFlags(statusCmd)
   175  	centralOpts.AttachControlPlaneFlags(statusCmd)
   176  	statusCmd.PersistentFlags().StringVarP(&configDumpFile, "file", "f", "",
   177  		"Envoy config dump JSON file")
   178  	statusCmd.PersistentFlags().BoolVar(&multiXdsOpts.XdsViaAgents, "xds-via-agents", false,
   179  		"Access Istiod via the tap service of each agent")
   180  	statusCmd.PersistentFlags().IntVar(&multiXdsOpts.XdsViaAgentsLimit, "xds-via-agents-limit", 100,
   181  		"Maximum number of pods being visited by istioctl when `xds-via-agent` flag is true."+
   182  			"To iterate all the agent pods without limit, set to 0")
   183  
   184  	return statusCmd
   185  }