mirror of https://github.com/linkerd/linkerd2.git
176 lines
4.6 KiB
Go
176 lines
4.6 KiB
Go
package cmd
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"sigs.k8s.io/yaml"
|
|
|
|
"github.com/linkerd/linkerd2-proxy-api/go/inbound"
|
|
"github.com/linkerd/linkerd2-proxy-api/go/outbound"
|
|
pkgcmd "github.com/linkerd/linkerd2/pkg/cmd"
|
|
"github.com/linkerd/linkerd2/pkg/k8s"
|
|
"github.com/spf13/cobra"
|
|
"go.opencensus.io/plugin/ocgrpc"
|
|
"google.golang.org/grpc"
|
|
"google.golang.org/grpc/credentials/insecure"
|
|
)
|
|
|
|
const (
|
|
policyPort = 8090
|
|
policyDeployment = "linkerd-destination"
|
|
)
|
|
|
|
func newCmdPolicy() *cobra.Command {
|
|
options := newEndpointsOptions()
|
|
var (
|
|
namespace = "default"
|
|
output = "yaml"
|
|
)
|
|
|
|
example := ` # get the inbound policy for pod emoji-6d66d87995-bvrnn on port 8080
|
|
linkerd diagnostics policy -n emojivoto po/emoji-6d66d87995-bvrnn 8080
|
|
|
|
# get the outbound policy for Service emoji-svc on port 8080
|
|
linkerd diagnostics policy -n emojivoto svc/emoji-svc 8080`
|
|
|
|
cmd := &cobra.Command{
|
|
Use: "policy [flags] resource port",
|
|
Short: "Introspect Linkerd's policy state",
|
|
Long: `Introspect Linkerd's policy state.
|
|
|
|
This command provides debug information about the internal state of the
|
|
control-plane's policy controller. It queries the same control-plane
|
|
endpoint as the linkerd-proxy's, and returns the policies associated with the
|
|
given resource. If the resource is a Pod, inbound policy for that Pod is
|
|
displayed. If the resource is a Service, outbound policy for that Service is
|
|
displayed.`,
|
|
Example: example,
|
|
Args: cobra.ExactArgs(2),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
err := options.validate()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
k8sAPI, err := k8s.NewAPI(kubeconfigPath, kubeContext, impersonate, impersonateGroup, 0)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if apiAddr == "" {
|
|
var portForward *k8s.PortForward
|
|
var err error
|
|
if options.destinationPod == "" {
|
|
portForward, err = k8s.NewPortForward(
|
|
cmd.Context(),
|
|
k8sAPI,
|
|
controlPlaneNamespace,
|
|
policyDeployment,
|
|
"localhost",
|
|
0,
|
|
policyPort,
|
|
false,
|
|
)
|
|
} else {
|
|
portForward, err = k8s.NewPodPortForward(k8sAPI, controlPlaneNamespace, options.destinationPod, "localhost", 0, policyPort, false)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
apiAddr = portForward.AddressAndPort()
|
|
if err = portForward.Init(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
conn, err := grpc.Dial(apiAddr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithStatsHandler(&ocgrpc.ClientHandler{}))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer conn.Close()
|
|
|
|
elems := strings.Split(args[0], "/")
|
|
|
|
if len(elems) == 1 {
|
|
return errors.New("resource type and name are required")
|
|
}
|
|
|
|
if len(elems) != 2 {
|
|
return fmt.Errorf("invalid resource string: %s", args[0])
|
|
}
|
|
typ, err := k8s.CanonicalResourceNameFromFriendlyName(elems[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
name := elems[1]
|
|
|
|
port, err := strconv.ParseUint(args[1], 10, 32)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var result interface{}
|
|
|
|
if typ == k8s.Pod {
|
|
client := inbound.NewInboundServerPoliciesClient(conn)
|
|
|
|
result, err = client.GetPort(cmd.Context(), &inbound.PortSpec{
|
|
Workload: fmt.Sprintf("%s:%s", namespace, name),
|
|
Port: uint32(port),
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
} else if typ == k8s.Service {
|
|
client := outbound.NewOutboundPoliciesClient(conn)
|
|
|
|
result, err = client.Get(cmd.Context(), &outbound.TrafficSpec{
|
|
SourceWorkload: "default:diagnostics",
|
|
Target: &outbound.TrafficSpec_Authority{Authority: fmt.Sprintf("%s.%s.svc:%d", name, namespace, port)},
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
return fmt.Errorf("invalid resource type %s; must be one of Pod or Service", args[0])
|
|
}
|
|
|
|
var out []byte
|
|
switch output {
|
|
case "json":
|
|
out, err = json.MarshalIndent(result, "", " ")
|
|
if err != nil {
|
|
fmt.Fprint(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
case "yaml":
|
|
out, err = yaml.Marshal(result)
|
|
if err != nil {
|
|
fmt.Fprint(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
default:
|
|
return errors.New("output must be one of: yaml, json")
|
|
}
|
|
|
|
_, err = fmt.Print(string(out))
|
|
return err
|
|
},
|
|
}
|
|
|
|
cmd.PersistentFlags().StringVar(&options.destinationPod, "destination-pod", "", "Target a specific destination Pod when there are multiple running")
|
|
cmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", namespace, "Namespace of resource")
|
|
cmd.PersistentFlags().StringVarP(&output, "output", "o", output, "Output format. One of: yaml, json")
|
|
|
|
pkgcmd.ConfigureOutputFlagCompletion(cmd)
|
|
|
|
return cmd
|
|
}
|