istio.io/istio@v0.0.0-20240520182934-d79c90f27776/istioctl/pkg/writer/envoy/configdump/configdump.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 configdump
    16  
    17  import (
    18  	"fmt"
    19  	"io"
    20  	"strings"
    21  	"text/tabwriter"
    22  
    23  	adminv3 "github.com/envoyproxy/go-control-plane/envoy/admin/v3"
    24  	"sigs.k8s.io/yaml"
    25  
    26  	"istio.io/istio/istioctl/pkg/util/configdump"
    27  	sdscompare "istio.io/istio/istioctl/pkg/writer/compare/sds"
    28  	"istio.io/istio/pkg/util/protomarshal"
    29  )
    30  
    31  // ConfigWriter is a writer for processing responses from the Envoy Admin config_dump endpoint
    32  type ConfigWriter struct {
    33  	Stdout     io.Writer
    34  	configDump *configdump.Wrapper
    35  }
    36  
    37  // includeConfigType is a flag to indicate whether to include the config type in the output
    38  var includeConfigType bool
    39  
    40  func SetPrintConfigTypeInSummary(p bool) {
    41  	includeConfigType = p
    42  }
    43  
    44  // Prime loads the config dump into the writer ready for printing
    45  func (c *ConfigWriter) Prime(b []byte) error {
    46  	w := &configdump.Wrapper{}
    47  	err := w.UnmarshalJSON(b)
    48  	if err != nil {
    49  		return fmt.Errorf("error unmarshalling config dump response from Envoy: %v", err)
    50  	}
    51  	c.configDump = w
    52  	return nil
    53  }
    54  
    55  // PrintBootstrapDump prints just the bootstrap config dump to the ConfigWriter stdout
    56  func (c *ConfigWriter) PrintBootstrapDump(outputFormat string) error {
    57  	if c.configDump == nil {
    58  		return fmt.Errorf("config writer has not been primed")
    59  	}
    60  	bootstrapDump, err := c.configDump.GetBootstrapConfigDump()
    61  	if err != nil {
    62  		return err
    63  	}
    64  	out, err := protomarshal.ToJSONWithIndent(bootstrapDump, "    ")
    65  	if err != nil {
    66  		return fmt.Errorf("unable to marshal bootstrap in Envoy config dump: %v", err)
    67  	}
    68  	if outputFormat == "yaml" {
    69  		outbyte, err := yaml.JSONToYAML([]byte(out))
    70  		if err != nil {
    71  			return err
    72  		}
    73  		out = string(outbyte)
    74  	}
    75  	fmt.Fprintln(c.Stdout, out)
    76  	return nil
    77  }
    78  
    79  // PrintSecretDump prints just the secret config dump to the ConfigWriter stdout
    80  func (c *ConfigWriter) PrintSecretDump(outputFormat string) error {
    81  	if c.configDump == nil {
    82  		return fmt.Errorf("config writer has not been primed")
    83  	}
    84  	secretDump, err := c.configDump.GetSecretConfigDump()
    85  	if err != nil {
    86  		return fmt.Errorf("sidecar doesn't support secrets: %v", err)
    87  	}
    88  	out, err := protomarshal.ToJSONWithIndent(secretDump, "    ")
    89  	if err != nil {
    90  		return fmt.Errorf("unable to marshal secrets in Envoy config dump: %v", err)
    91  	}
    92  	if outputFormat == "yaml" {
    93  		outbyte, err := yaml.JSONToYAML([]byte(out))
    94  		if err != nil {
    95  			return err
    96  		}
    97  		out = string(outbyte)
    98  	}
    99  	fmt.Fprintln(c.Stdout, out)
   100  	return nil
   101  }
   102  
   103  // PrintSecretSummary prints a summary of dynamic active secrets from the config dump
   104  func (c *ConfigWriter) PrintSecretSummary() error {
   105  	secretDump, err := c.configDump.GetSecretConfigDump()
   106  	if err != nil {
   107  		return err
   108  	}
   109  	if len(secretDump.DynamicActiveSecrets) == 0 &&
   110  		len(secretDump.DynamicWarmingSecrets) == 0 {
   111  		fmt.Fprintln(c.Stdout, "No active or warming secrets found.")
   112  		return nil
   113  	}
   114  	secretItems, err := sdscompare.GetEnvoySecrets(c.configDump)
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	secretWriter := sdscompare.NewSDSWriter(c.Stdout, sdscompare.TABULAR)
   120  	return secretWriter.PrintSecretItems(secretItems)
   121  }
   122  
   123  func (c *ConfigWriter) PrintFullSummary(cf ClusterFilter, lf ListenerFilter, rf RouteFilter, epf EndpointFilter) error {
   124  	if err := c.PrintBootstrapSummary(); err != nil {
   125  		return err
   126  	}
   127  	_, _ = c.Stdout.Write([]byte("\n"))
   128  	if err := c.PrintClusterSummary(cf); err != nil {
   129  		return err
   130  	}
   131  	_, _ = c.Stdout.Write([]byte("\n"))
   132  	if err := c.PrintListenerSummary(lf); err != nil {
   133  		return err
   134  	}
   135  	_, _ = c.Stdout.Write([]byte("\n"))
   136  	if err := c.PrintRouteSummary(rf); err != nil {
   137  		return err
   138  	}
   139  	_, _ = c.Stdout.Write([]byte("\n"))
   140  	if err := c.PrintSecretSummary(); err != nil {
   141  		return err
   142  	}
   143  	_, _ = c.Stdout.Write([]byte("\n"))
   144  	if err := c.PrintEndpointsSummary(epf); err != nil {
   145  		return err
   146  	}
   147  	return nil
   148  }
   149  
   150  // PrintBootstrapSummary prints bootstrap information for Istio and Envoy from the config dump
   151  func (c *ConfigWriter) PrintBootstrapSummary() error {
   152  	if c.configDump == nil {
   153  		return fmt.Errorf("config writer has not been primed")
   154  	}
   155  
   156  	bootstrapDump, err := c.configDump.GetBootstrapConfigDump()
   157  	if err != nil {
   158  		return err
   159  	}
   160  
   161  	var (
   162  		istioVersion, istioProxySha = c.getIstioVersionInfo(bootstrapDump)
   163  		envoyVersion                = c.getUserAgentVersionInfo(bootstrapDump)
   164  
   165  		tw = tabwriter.NewWriter(c.Stdout, 0, 8, 1, ' ', 0)
   166  	)
   167  
   168  	if len(istioVersion) > 0 {
   169  		fmt.Fprintf(tw, "Istio Version:\t%s\n", istioVersion)
   170  	}
   171  	if len(istioProxySha) > 0 {
   172  		fmt.Fprintf(tw, "Istio Proxy Version:\t%s\n", istioProxySha)
   173  	}
   174  	if len(envoyVersion) > 0 {
   175  		fmt.Fprintf(tw, "Envoy Version:\t%s\n", envoyVersion)
   176  	}
   177  
   178  	return tw.Flush()
   179  }
   180  
   181  // PrintPodRootCAFromDynamicSecretDump prints just pod's root ca from dynamic secret config dump to the ConfigWriter stdout
   182  func (c *ConfigWriter) PrintPodRootCAFromDynamicSecretDump() (string, error) {
   183  	if c.configDump == nil {
   184  		return "", fmt.Errorf("config writer has not been primed")
   185  	}
   186  	secretDump, err := c.configDump.GetSecretConfigDump()
   187  	if err != nil {
   188  		return "", fmt.Errorf("sidecar doesn't support secrets: %v", err)
   189  	}
   190  	for _, secret := range secretDump.DynamicActiveSecrets {
   191  		// check the ROOTCA from secret dump
   192  		if secret.Name == "ROOTCA" {
   193  			var returnStr string
   194  			var returnErr error
   195  			strCA, err := c.configDump.GetRootCAFromSecretConfigDump(secret.GetSecret())
   196  			if err != nil {
   197  				returnStr = ""
   198  				returnErr = fmt.Errorf("can not dump ROOTCA from secret: %v", err)
   199  			} else {
   200  				returnStr = strCA
   201  				returnErr = nil
   202  			}
   203  			return returnStr, returnErr
   204  		}
   205  	}
   206  	return "", fmt.Errorf("can not find ROOTCA from secret")
   207  }
   208  
   209  func (c *ConfigWriter) getIstioVersionInfo(bootstrapDump *adminv3.BootstrapConfigDump) (version, sha string) {
   210  	const (
   211  		istioVersionKey  = "ISTIO_VERSION"
   212  		istioProxyShaKey = "ISTIO_PROXY_SHA"
   213  	)
   214  
   215  	md := bootstrapDump.GetBootstrap().GetNode().GetMetadata().GetFields()
   216  
   217  	if versionPB, ok := md[istioVersionKey]; ok {
   218  		version = versionPB.GetStringValue()
   219  	}
   220  	if shaPB, ok := md[istioProxyShaKey]; ok {
   221  		sha = shaPB.GetStringValue()
   222  		if shaParts := strings.Split(sha, ":"); len(shaParts) > 1 {
   223  			sha = shaParts[1]
   224  		}
   225  	}
   226  
   227  	return
   228  }
   229  
   230  func (c *ConfigWriter) getUserAgentVersionInfo(bootstrapDump *adminv3.BootstrapConfigDump) string {
   231  	const (
   232  		buildLabelKey = "build.label"
   233  		buildTypeKey  = "build.type"
   234  		statusKey     = "revision.status"
   235  		sslVersionKey = "ssl.version"
   236  	)
   237  
   238  	var (
   239  		buildVersion = bootstrapDump.GetBootstrap().GetNode().GetUserAgentBuildVersion()
   240  		version      = buildVersion.GetVersion()
   241  		md           = buildVersion.GetMetadata().GetFields()
   242  
   243  		sb strings.Builder
   244  	)
   245  
   246  	fmt.Fprintf(&sb, "%d.%d.%d", version.GetMajorNumber(), version.GetMinorNumber(), version.GetPatch())
   247  	if label, ok := md[buildLabelKey]; ok {
   248  		fmt.Fprintf(&sb, "-%s", label.GetStringValue())
   249  	}
   250  	if status, ok := md[statusKey]; ok {
   251  		fmt.Fprintf(&sb, "/%s", status.GetStringValue())
   252  	}
   253  	if typ, ok := md[buildTypeKey]; ok {
   254  		fmt.Fprintf(&sb, "/%s", typ.GetStringValue())
   255  	}
   256  	if sslVersion, ok := md[sslVersionKey]; ok {
   257  		fmt.Fprintf(&sb, "/%s", sslVersion.GetStringValue())
   258  	}
   259  
   260  	return sb.String()
   261  }