2023-03-28 19:12:30 +00:00
// Copyright (c) HashiCorp, Inc.
2023-08-11 13:12:13 +00:00
// SPDX-License-Identifier: BUSL-1.1
2023-03-28 19:12:30 +00:00
2023-02-01 19:37:30 +00:00
package upstreams
import (
"flag"
"fmt"
"net"
"os"
2023-02-10 19:12:13 +00:00
"strconv"
"strings"
2023-02-01 19:37:30 +00:00
2023-02-10 19:12:13 +00:00
"github.com/hashicorp/consul/command/cli"
2023-02-01 19:37:30 +00:00
"github.com/hashicorp/consul/command/flags"
2023-02-06 17:14:35 +00:00
troubleshoot "github.com/hashicorp/consul/troubleshoot/proxy"
2023-02-01 19:37:30 +00:00
)
func New ( ui cli . Ui ) * cmd {
c := & cmd { UI : ui }
c . init ( )
return c
}
type cmd struct {
UI cli . Ui
flags * flag . FlagSet
http * flags . HTTPFlags
help string
// flags
2023-02-09 00:05:22 +00:00
envoyAdminEndpoint string
2023-02-01 19:37:30 +00:00
}
func ( c * cmd ) init ( ) {
c . flags = flag . NewFlagSet ( "" , flag . ContinueOnError )
2023-02-09 00:05:22 +00:00
defaultEnvoyAdminEndpoint := "localhost:19000"
if envoyAdminEndpoint := os . Getenv ( "ENVOY_ADMIN_ENDPOINT" ) ; envoyAdminEndpoint != "" {
defaultEnvoyAdminEndpoint = envoyAdminEndpoint
2023-02-01 19:37:30 +00:00
}
2023-02-09 00:05:22 +00:00
c . flags . StringVar ( & c . envoyAdminEndpoint , "envoy-admin-endpoint" , defaultEnvoyAdminEndpoint , "The address:port that envoy's admin endpoint is on." )
2023-02-01 19:37:30 +00:00
c . http = & flags . HTTPFlags { }
flags . Merge ( c . flags , c . http . ClientFlags ( ) )
flags . Merge ( c . flags , c . http . ServerFlags ( ) )
c . help = flags . Usage ( help , c . flags )
}
func ( c * cmd ) Run ( args [ ] string ) int {
if err := c . flags . Parse ( args ) ; err != nil {
c . UI . Error ( fmt . Sprintf ( "Failed to parse args: %v" , err ) )
return 1
}
2023-02-09 00:05:22 +00:00
adminAddr , adminPort , err := net . SplitHostPort ( c . envoyAdminEndpoint )
2023-02-01 19:37:30 +00:00
if err != nil {
c . UI . Error ( "Invalid Envoy Admin endpoint: " + err . Error ( ) )
return 1
}
// Envoy requires IP addresses to bind too when using static so resolve DNS or
// localhost here.
adminBindIP , err := net . ResolveIPAddr ( "ip" , adminAddr )
if err != nil {
2023-02-09 00:05:22 +00:00
c . UI . Error ( "Failed to resolve envoy admin endpoint: " + err . Error ( ) )
c . UI . Error ( "Please make sure Envoy's Admin API is enabled." )
2023-02-01 19:37:30 +00:00
return 1
}
t , err := troubleshoot . NewTroubleshoot ( adminBindIP , adminPort )
if err != nil {
c . UI . Error ( "error generating troubleshoot client: " + err . Error ( ) )
return 1
}
2023-02-07 22:57:31 +00:00
envoyIDs , upstreamIPs , err := t . GetUpstreams ( )
2023-02-01 19:37:30 +00:00
if err != nil {
c . UI . Error ( "error calling GetUpstreams: " + err . Error ( ) )
return 1
}
2023-02-17 15:43:05 +00:00
c . UI . HeaderOutput ( fmt . Sprintf ( "Upstreams (explicit upstreams only) (%v)\n" , len ( envoyIDs ) ) )
2023-02-07 22:57:31 +00:00
for _ , u := range envoyIDs {
2023-02-17 15:43:05 +00:00
c . UI . UnchangedOutput ( u )
2023-02-01 19:37:30 +00:00
}
2023-02-07 22:57:31 +00:00
2023-02-17 15:43:05 +00:00
c . UI . HeaderOutput ( fmt . Sprintf ( "Upstream IPs (transparent proxy only) (%v)" , len ( upstreamIPs ) ) )
2023-02-10 19:12:13 +00:00
tbl := cli . NewTable ( "IPs " , "Virtual " , "Cluster Names" )
2023-02-07 22:57:31 +00:00
for _ , u := range upstreamIPs {
2023-02-10 19:12:13 +00:00
tbl . AddRow ( [ ] string { formatIPs ( u . IPs ) , strconv . FormatBool ( u . IsVirtual ) , formatClusterNames ( u . ClusterNames ) } , [ ] string { } )
2023-02-07 22:57:31 +00:00
}
2023-02-10 19:12:13 +00:00
c . UI . Table ( tbl )
2023-02-07 22:57:31 +00:00
2023-02-17 15:43:05 +00:00
c . UI . UnchangedOutput ( "\nIf you cannot find the upstream address or cluster for a transparent proxy upstream:" )
c . UI . UnchangedOutput ( "-> Check intentions: Transparent proxy upstreams are configured based on intentions. Make sure you " +
2023-02-09 00:05:22 +00:00
"have configured intentions to allow traffic to your upstream." )
2023-02-17 15:43:05 +00:00
c . UI . UnchangedOutput ( "-> To check that the right cluster is being dialed, run a DNS lookup " +
"for the upstream you are dialing. For example, run `dig backend.svc.consul` to return the IP address for the `backend` service. If the address you get from that is missing " +
"from the upstream IPs, it means that your proxy may be misconfigured." )
2023-02-01 19:37:30 +00:00
return 0
}
func ( c * cmd ) Synopsis ( ) string {
return synopsis
}
func ( c * cmd ) Help ( ) string {
return c . help
}
const (
2023-02-01 23:11:05 +00:00
synopsis = "Get upstream envoy identifiers for the current envoy instance"
2023-02-01 19:37:30 +00:00
help = `
Usage : consul troubleshoot upstreams [ options ]
2023-02-01 23:11:05 +00:00
Connects to local Envoy and lists upstream service envoy identifiers .
2023-02-01 19:37:30 +00:00
This command is used in combination with
2023-02-01 23:11:05 +00:00
' consul troubleshoot proxy ' to diagnose issues in Consul service mesh .
2023-02-01 19:37:30 +00:00
Examples :
$ consul troubleshoot upstreams
`
)
2023-02-10 19:12:13 +00:00
func formatIPs ( ips [ ] string ) string {
return strings . Join ( ips , ", " )
}
func formatClusterNames ( names map [ string ] struct { } ) string {
var out [ ] string
for k := range names {
out = append ( out , k )
}
return strings . Join ( out , ", " )
}