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.NewClient(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: options.contextToken, 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().StringVar(&options.contextToken, "token", "default:diagnostics", "Token to use when querying the policy service") 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 }