github.com/telepresenceio/telepresence/v2@v2.20.0-pro.6.0.20240517030216-236ea954e789/pkg/client/cli/intercept/info.go (about)

     1  package intercept
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"net"
     8  	"strings"
     9  
    10  	"github.com/telepresenceio/telepresence/rpc/v2/manager"
    11  	"github.com/telepresenceio/telepresence/v2/pkg/client"
    12  	"github.com/telepresenceio/telepresence/v2/pkg/ioutil"
    13  )
    14  
    15  type Ingress struct {
    16  	Host   string `json:"host,omitempty"    yaml:"host,omitempty"`
    17  	Port   int32  `json:"port,omitempty"    yaml:"port,omitempty"`
    18  	UseTLS bool   `json:"use_tls,omitempty" yaml:"use_tls,omitempty"`
    19  	L5Host string `json:"l5host,omitempty"  yaml:"l5host,omitempty"`
    20  }
    21  
    22  type Mount struct {
    23  	LocalDir  string   `json:"local_dir,omitempty"     yaml:"local_dir,omitempty"`
    24  	RemoteDir string   `json:"remote_dir,omitempty"    yaml:"remote_dir,omitempty"`
    25  	Error     string   `json:"error,omitempty"         yaml:"error,omitempty"`
    26  	PodIP     string   `json:"pod_ip,omitempty"        yaml:"pod_ip,omitempty"`
    27  	Port      int32    `json:"port,omitempty"          yaml:"port,omitempty"`
    28  	Mounts    []string `json:"mounts,omitempty"        yaml:"mounts,omitempty"`
    29  }
    30  
    31  type Info struct {
    32  	ID            string            `json:"id,omitempty"              yaml:"id,omitempty"`
    33  	Name          string            `json:"name,omitempty"            yaml:"name,omitempty"`
    34  	Disposition   string            `json:"disposition,omitempty"     yaml:"disposition,omitempty"`
    35  	Message       string            `json:"message,omitempty"         yaml:"message,omitempty"`
    36  	WorkloadKind  string            `json:"workload_kind,omitempty"   yaml:"workload_kind,omitempty"`
    37  	TargetHost    string            `json:"target_host,omitempty"     yaml:"target_host,omitempty"`
    38  	TargetPort    int32             `json:"target_port,omitempty"     yaml:"target_port,omitempty"`
    39  	ServicePortID string            `json:"service_port_id,omitempty" yaml:"service_port_id,omitempty"`
    40  	Environment   map[string]string `json:"environment,omitempty"     yaml:"environment,omitempty"`
    41  	Mount         *Mount            `json:"mount,omitempty"           yaml:"mount,omitempty"`
    42  	FilterDesc    string            `json:"filter_desc,omitempty"     yaml:"filter_desc,omitempty"`
    43  	Metadata      map[string]string `json:"metadata,omitempty"     yaml:"metadata,omitempty"`
    44  	HttpFilter    []string          `json:"http_filter,omitempty"     yaml:"http_filter,omitempty"`
    45  	Global        bool              `json:"global,omitempty"          yaml:"global,omitempty"`
    46  	PreviewURL    string            `json:"preview_url,omitempty"     yaml:"preview_url,omitempty"`
    47  	Ingress       *Ingress          `json:"ingress,omitempty"         yaml:"ingress,omitempty"`
    48  	debug         bool
    49  }
    50  
    51  func NewIngress(ps *manager.PreviewSpec) *Ingress {
    52  	if ps == nil {
    53  		return nil
    54  	}
    55  	ii := ps.Ingress
    56  	if ii == nil {
    57  		return nil
    58  	}
    59  	return &Ingress{
    60  		Host:   ii.Host,
    61  		Port:   ii.Port,
    62  		UseTLS: ii.UseTls,
    63  		L5Host: ii.L5Host,
    64  	}
    65  }
    66  
    67  func PreviewURL(pu string) string {
    68  	if !(pu == "" || strings.HasPrefix(pu, "https://") || strings.HasPrefix(pu, "http://")) {
    69  		pu = "https://" + pu
    70  	}
    71  	return pu
    72  }
    73  
    74  func NewMount(ctx context.Context, ii *manager.InterceptInfo, mountError string) *Mount {
    75  	if mountError != "" {
    76  		return &Mount{Error: mountError}
    77  	}
    78  	if ii.MountPoint != "" {
    79  		var port int32
    80  		if client.GetConfig(ctx).Intercept().UseFtp {
    81  			port = ii.FtpPort
    82  		} else {
    83  			port = ii.SftpPort
    84  		}
    85  		var mounts []string
    86  		if tpMounts := ii.Environment["TELEPRESENCE_MOUNTS"]; tpMounts != "" {
    87  			// This is a Unix path, so we cannot use filepath.SplitList
    88  			mounts = strings.Split(tpMounts, ":")
    89  		}
    90  		return &Mount{
    91  			LocalDir:  ii.ClientMountPoint,
    92  			RemoteDir: ii.MountPoint,
    93  			PodIP:     ii.PodIp,
    94  			Port:      port,
    95  			Mounts:    mounts,
    96  		}
    97  	}
    98  	return nil
    99  }
   100  
   101  func NewInfo(ctx context.Context, ii *manager.InterceptInfo, mountError string) *Info {
   102  	spec := ii.Spec
   103  	return &Info{
   104  		ID:            ii.Id,
   105  		Name:          spec.Name,
   106  		Disposition:   ii.Disposition.String(),
   107  		Message:       ii.Message,
   108  		WorkloadKind:  spec.WorkloadKind,
   109  		TargetHost:    spec.TargetHost,
   110  		TargetPort:    spec.TargetPort,
   111  		Mount:         NewMount(ctx, ii, mountError),
   112  		ServicePortID: spec.ServicePortIdentifier,
   113  		Environment:   ii.Environment,
   114  		FilterDesc:    ii.MechanismArgsDesc,
   115  		Metadata:      ii.Metadata,
   116  		HttpFilter:    spec.MechanismArgs,
   117  		Global:        spec.Mechanism == "tcp",
   118  		PreviewURL:    PreviewURL(ii.PreviewDomain),
   119  		Ingress:       NewIngress(ii.PreviewSpec),
   120  	}
   121  }
   122  
   123  func (ii *Info) WriteTo(w io.Writer) (int64, error) {
   124  	kvf := ioutil.DefaultKeyValueFormatter()
   125  	kvf.Prefix = "   "
   126  	kvf.Add("Intercept name", ii.Name)
   127  	kvf.Add("State", func() string {
   128  		msg := ""
   129  		if manager.InterceptDispositionType_value[ii.Disposition] > int32(manager.InterceptDispositionType_WAITING) {
   130  			msg += "error: "
   131  		}
   132  		msg += ii.Disposition
   133  		if ii.Message != "" {
   134  			msg += ": " + ii.Message
   135  		}
   136  		return msg
   137  	}())
   138  	kvf.Add("Workload kind", ii.WorkloadKind)
   139  
   140  	if ii.debug {
   141  		kvf.Add("ID", ii.ID)
   142  	}
   143  
   144  	kvf.Add(
   145  		"Destination",
   146  		net.JoinHostPort(ii.TargetHost, fmt.Sprintf("%d", ii.TargetPort)),
   147  	)
   148  
   149  	if ii.ServicePortID != "" {
   150  		kvf.Add("Service Port Identifier", ii.ServicePortID)
   151  	}
   152  	if ii.debug {
   153  		m := "http"
   154  		if ii.Global {
   155  			m = "tcp"
   156  		}
   157  		kvf.Add("Mechanism", m)
   158  		kvf.Add("Mechanism Command", fmt.Sprintf("%q", ii.FilterDesc))
   159  		kvf.Add("Metadata", fmt.Sprintf("%q", ii.Metadata))
   160  	}
   161  
   162  	if m := ii.Mount; m != nil {
   163  		if m.LocalDir != "" {
   164  			kvf.Add("Volume Mount Point", m.LocalDir)
   165  		} else if m.Error != "" {
   166  			kvf.Add("Volume Mount Error", m.Error)
   167  		}
   168  	}
   169  
   170  	kvf.Add("Intercepting", func() string {
   171  		if ii.FilterDesc != "" {
   172  			return ii.FilterDesc
   173  		}
   174  		if ii.Global {
   175  			return `using mechanism "tcp"`
   176  		}
   177  		return fmt.Sprintf("using mechanism=%q with args=%q", "http", ii.HttpFilter)
   178  	}())
   179  
   180  	if ii.PreviewURL != "" {
   181  		previewURL := ii.PreviewURL
   182  		// Right now SystemA gives back domains with the leading "https://", but
   183  		// let's not rely on that.
   184  		if !strings.HasPrefix(previewURL, "https://") && !strings.HasPrefix(previewURL, "http://") {
   185  			previewURL = "https://" + previewURL
   186  		}
   187  		kvf.Add("Preview URL", previewURL)
   188  	}
   189  	if in := ii.Ingress; in != nil {
   190  		kvf.Add("Layer 5 Hostname", in.L5Host)
   191  	}
   192  	return kvf.WriteTo(w)
   193  }