1 package cmd
2
3 import (
4 "encoding/json"
5 "errors"
6 "fmt"
7 "os"
8 "strconv"
9 "strings"
10
11 "sigs.k8s.io/yaml"
12
13 "github.com/linkerd/linkerd2-proxy-api/go/inbound"
14 "github.com/linkerd/linkerd2-proxy-api/go/outbound"
15 pkgcmd "github.com/linkerd/linkerd2/pkg/cmd"
16 "github.com/linkerd/linkerd2/pkg/k8s"
17 "github.com/spf13/cobra"
18 "go.opencensus.io/plugin/ocgrpc"
19 "google.golang.org/grpc"
20 "google.golang.org/grpc/credentials/insecure"
21 )
22
23 const (
24 policyPort = 8090
25 policyDeployment = "linkerd-destination"
26 )
27
28 func newCmdPolicy() *cobra.Command {
29 options := newEndpointsOptions()
30 var (
31 namespace = "default"
32 output = "yaml"
33 )
34
35 example := ` # get the inbound policy for pod emoji-6d66d87995-bvrnn on port 8080
36 linkerd diagnostics policy -n emojivoto po/emoji-6d66d87995-bvrnn 8080
37
38 # get the outbound policy for Service emoji-svc on port 8080
39 linkerd diagnostics policy -n emojivoto svc/emoji-svc 8080`
40
41 cmd := &cobra.Command{
42 Use: "policy [flags] resource port",
43 Short: "Introspect Linkerd's policy state",
44 Long: `Introspect Linkerd's policy state.
45
46 This command provides debug information about the internal state of the
47 control-plane's policy controller. It queries the same control-plane
48 endpoint as the linkerd-proxy's, and returns the policies associated with the
49 given resource. If the resource is a Pod, inbound policy for that Pod is
50 displayed. If the resource is a Service, outbound policy for that Service is
51 displayed.`,
52 Example: example,
53 Args: cobra.ExactArgs(2),
54 RunE: func(cmd *cobra.Command, args []string) error {
55 err := options.validate()
56 if err != nil {
57 return err
58 }
59
60 k8sAPI, err := k8s.NewAPI(kubeconfigPath, kubeContext, impersonate, impersonateGroup, 0)
61 if err != nil {
62 return err
63 }
64
65 if apiAddr == "" {
66 var portForward *k8s.PortForward
67 var err error
68 if options.destinationPod == "" {
69 portForward, err = k8s.NewPortForward(
70 cmd.Context(),
71 k8sAPI,
72 controlPlaneNamespace,
73 policyDeployment,
74 "localhost",
75 0,
76 policyPort,
77 false,
78 )
79 } else {
80 portForward, err = k8s.NewPodPortForward(k8sAPI, controlPlaneNamespace, options.destinationPod, "localhost", 0, policyPort, false)
81 }
82 if err != nil {
83 return err
84 }
85
86 apiAddr = portForward.AddressAndPort()
87 if err = portForward.Init(); err != nil {
88 return err
89 }
90 }
91
92 conn, err := grpc.NewClient(apiAddr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithStatsHandler(&ocgrpc.ClientHandler{}))
93 if err != nil {
94 return err
95 }
96 defer conn.Close()
97
98 elems := strings.Split(args[0], "/")
99
100 if len(elems) == 1 {
101 return errors.New("resource type and name are required")
102 }
103
104 if len(elems) != 2 {
105 return fmt.Errorf("invalid resource string: %s", args[0])
106 }
107 typ, err := k8s.CanonicalResourceNameFromFriendlyName(elems[0])
108 if err != nil {
109 return err
110 }
111 name := elems[1]
112
113 port, err := strconv.ParseUint(args[1], 10, 32)
114 if err != nil {
115 return err
116 }
117
118 var result interface{}
119
120 if typ == k8s.Pod {
121 client := inbound.NewInboundServerPoliciesClient(conn)
122
123 result, err = client.GetPort(cmd.Context(), &inbound.PortSpec{
124 Workload: fmt.Sprintf("%s:%s", namespace, name),
125 Port: uint32(port),
126 })
127 if err != nil {
128 return err
129 }
130
131 } else if typ == k8s.Service {
132 client := outbound.NewOutboundPoliciesClient(conn)
133
134 result, err = client.Get(cmd.Context(), &outbound.TrafficSpec{
135 SourceWorkload: options.contextToken,
136 Target: &outbound.TrafficSpec_Authority{Authority: fmt.Sprintf("%s.%s.svc:%d", name, namespace, port)},
137 })
138 if err != nil {
139 return err
140 }
141 } else {
142 return fmt.Errorf("invalid resource type %s; must be one of Pod or Service", args[0])
143 }
144
145 var out []byte
146 switch output {
147 case "json":
148 out, err = json.MarshalIndent(result, "", " ")
149 if err != nil {
150 fmt.Fprint(os.Stderr, err)
151 os.Exit(1)
152 }
153 case "yaml":
154 out, err = yaml.Marshal(result)
155 if err != nil {
156 fmt.Fprint(os.Stderr, err)
157 os.Exit(1)
158 }
159 default:
160 return errors.New("output must be one of: yaml, json")
161 }
162
163 _, err = fmt.Print(string(out))
164 return err
165 },
166 }
167
168 cmd.PersistentFlags().StringVar(&options.destinationPod, "destination-pod", "", "Target a specific destination Pod when there are multiple running")
169 cmd.PersistentFlags().StringVar(&options.contextToken, "token", "default:diagnostics", "Token to use when querying the policy service")
170 cmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", namespace, "Namespace of resource")
171 cmd.PersistentFlags().StringVarP(&output, "output", "o", output, "Output format. One of: yaml, json")
172
173 pkgcmd.ConfigureOutputFlagCompletion(cmd)
174
175 return cmd
176 }
177
View as plain text