Skip to content
This repository has been archived by the owner on Jul 11, 2023. It is now read-only.

cli: add proxy command to dump config #1883

Merged
merged 3 commits into from
Oct 21, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/cli/osm.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func newRootCmd(config *action.Configuration, in io.Reader, out io.Writer, args
newNamespaceCmd(out),
newMetricsCmd(out),
newVersionCmd(out),
newProxyCmd(config, out),
)

flags.Parse(args)
Expand Down
25 changes: 25 additions & 0 deletions cmd/cli/proxy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package main

import (
"io"

"github.com/spf13/cobra"
"helm.sh/helm/v3/pkg/action"
)

const proxyCmdDescription = `
This command consists of multiple subcommands related to managing the
sidecar proxy on pods.
`

func newProxyCmd(config *action.Configuration, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "proxy",
Short: "manage sidecar proxy",
Long: proxyCmdDescription,
Args: cobra.NoArgs,
}
cmd.AddCommand(newProxyDumpConfig(config, out))

return cmd
}
122 changes: 122 additions & 0 deletions cmd/cli/proxy_configdump.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package main

import (
"context"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"

"github.com/pkg/errors"
"github.com/spf13/cobra"
"helm.sh/helm/v3/pkg/action"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"

"github.com/openservicemesh/osm/pkg/constants"
)

const dumpConfigDescription = `
This command will dump the sidecar proxy configuration for the given pod.
`

const dumpConfigExample = `
# Dump the proxy configuration for pod 'bookbuyer-5ccf77f46d-rc5mg' in the 'bookbuyer' namespace
osm proxy dump-config bookbuyer-5ccf77f46d-rc5mg -n bookbuyer
`

type proxyDumpConfigCmd struct {
out io.Writer
config *rest.Config
clientSet kubernetes.Interface
namespace string
pod string
localPort uint16
sigintChan chan os.Signal
}

func newProxyDumpConfig(config *action.Configuration, out io.Writer) *cobra.Command {
dumpConfigCmd := &proxyDumpConfigCmd{
out: out,
sigintChan: make(chan os.Signal, 1),
}

cmd := &cobra.Command{
Use: "dump-config POD ...",
shashankram marked this conversation as resolved.
Show resolved Hide resolved
Short: "dump proxy config",
Long: dumpConfigDescription,
Args: cobra.ExactArgs(1),
RunE: func(_ *cobra.Command, args []string) error {
dumpConfigCmd.pod = args[0]
conf, err := config.RESTClientGetter.ToRESTConfig()
if err != nil {
return errors.Errorf("Error fetching kubeconfig")
}
dumpConfigCmd.config = conf

clientset, err := kubernetes.NewForConfig(conf)
if err != nil {
return errors.Errorf("Could not access Kubernetes cluster. Check kubeconfig")
}
dumpConfigCmd.clientSet = clientset
return dumpConfigCmd.run()
},
Example: dumpConfigExample,
}

//add mesh name flag
f := cmd.Flags()
f.StringVarP(&dumpConfigCmd.namespace, "namespace", "n", metav1.NamespaceDefault, "Namespace of pod")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's already a -n/--namespace flag automatically added to every command from pkg/cli/environment.go to define the control plane namespace. Does it still work to define both the pod namespace and control plane namespace? If the control plane namespace doesn't need to be defined, then do both flags show up in the help text? That would be confusing.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not mix the pod namespace with the control plane namespace. This is what the help text looks like:

This command will dump the sidecar proxy configuration for the given pod.

Usage:
  osm proxy dump-config POD ... [flags]

Examples:

# Dump the proxy configuration for pod bookbuyer-5ccf77f46d-rc5mg in the bookbuyer namespace
osm proxy dump-config bookbuyer-5ccf77f46d-rc5mg -n bookbuyer


Flags:
  -h, --help                help for dump-config
  -p, --local-port uint16   Local port to use for port forwarding (default 15000)

Global Flags:
  -n, --namespace string   namespace for osm control plane (default "osm-system")

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So then does the flag on the command work to change the pod namespace?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The flag on the command is meant to specify the pod namespace, not to change it. The pod here is the application pod, not the controller pod. If you did specify a controller pod, the command will error due to the controller not being a meshed pod.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I filed #1884 to track the ambiguity between the namespace flags.

f.Uint16VarP(&dumpConfigCmd.localPort, "local-port", "p", constants.EnvoyAdminPort, "Local port to use for port forwarding")

return cmd
}

func (cmd *proxyDumpConfigCmd) run() error {
// Check if the pod belongs to the mesh
pod, err := cmd.clientSet.CoreV1().Pods(cmd.namespace).Get(context.TODO(), cmd.pod, metav1.GetOptions{})
if err != nil {
return errors.Errorf("Could not find pod %s in namespace %s", cmd.pod, cmd.namespace)
}
if !isMeshedPod(*pod) {
shashankram marked this conversation as resolved.
Show resolved Hide resolved
return errors.Errorf("Pod %s in namespace %s is not a part of a mesh", cmd.pod, cmd.namespace)
}

portForwarder, err := NewPortForwarder(cmd.config, cmd.clientSet, cmd.pod, cmd.namespace, cmd.localPort, constants.EnvoyAdminPort)
if err != nil {
return errors.Errorf("Error setting up port forwarding: %s", err)
}

err = portForwarder.Start(func(pf *PortForwarder) error {
url := fmt.Sprintf("http://localhost:%d/config_dump", cmd.localPort)

// #nosec G107: Potential HTTP request made with variable url
shashankram marked this conversation as resolved.
Show resolved Hide resolved
resp, err := http.Get(url)
if err != nil {
return errors.Errorf("Error fetching url %s: %s", url, err)
}
config, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
return errors.Errorf("Error retrieving proxy config: %s", err)
}
fmt.Fprintf(cmd.out, "%s", config)
shashankram marked this conversation as resolved.
Show resolved Hide resolved
pf.Stop()
return nil
})
if err != nil {
return errors.Errorf("Error retrieving proxy config for pod %s in namespace %s: %s", cmd.pod, cmd.namespace, err)
}

return nil
}

// isMeshedPod returns a boolean indicating if the pod is part of a mesh
func isMeshedPod(pod corev1.Pod) bool {
// osm-controller adds a unique label to each pod that belongs to a mesh
_, proxyLabelSet := pod.Labels[constants.EnvoyUniqueIDLabelName]
return proxyLabelSet
}
48 changes: 48 additions & 0 deletions cmd/cli/proxy_configdump_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package main

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/openservicemesh/osm/pkg/constants"
)

func TestIsMeshedPod(t *testing.T) {
assert := assert.New(t)

type test struct {
pod corev1.Pod
isMeshed bool
}

testCases := []test{
{
pod: corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod-1",
Labels: map[string]string{constants.EnvoyUniqueIDLabelName: "test"},
},
},
isMeshed: true,
},
{
pod: corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod-2",
},
},
isMeshed: false,
},
}

for _, tc := range testCases {
t.Run(fmt.Sprintf("Testing if pod %s is meshed", tc.pod.Name), func(t *testing.T) {
shashankram marked this conversation as resolved.
Show resolved Hide resolved
isMeshed := isMeshedPod(tc.pod)
assert.Equal(isMeshed, tc.isMeshed)
})
}
}