Skip to content

Commit

Permalink
feat: add listener/cluster/route configdump support
Browse files Browse the repository at this point in the history
Signed-off-by: bitliu <bitliu@tencent.com>
  • Loading branch information
Xunzhuo committed Feb 28, 2023
1 parent 7a373cc commit a98e065
Show file tree
Hide file tree
Showing 31 changed files with 2,106 additions and 2,720 deletions.
315 changes: 309 additions & 6 deletions internal/cmd/egctl/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ import (
"net/http"

"github.com/spf13/cobra"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/reflect/protoreflect"
"k8s.io/apimachinery/pkg/types"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"sigs.k8s.io/yaml"

adminv3 "github.com/envoyproxy/go-control-plane/envoy/admin/v3"

"github.com/envoyproxy/gateway/internal/cmd/options"
kube "github.com/envoyproxy/gateway/internal/kubernetes"
)
Expand Down Expand Up @@ -44,7 +48,7 @@ func NewConfigCommand() *cobra.Command {
options.AddKubeConfigFlags(flags)

cfgCommand.PersistentFlags().StringVarP(&output, "output", "o", "json", "One of 'yaml' or 'json'")
cfgCommand.PersistentFlags().StringVarP(&podNamespace, "namespace", "n", "envoy-gateway", "Namespace where envoy proxy pod are installed.")
cfgCommand.PersistentFlags().StringVarP(&podNamespace, "namespace", "n", "envoy-gateway-system", "Namespace where envoy proxy pod are installed.")

return cfgCommand
}
Expand All @@ -57,13 +61,17 @@ func proxyCommand() *cobra.Command {
}

c.AddCommand(allConfigCmd())
c.AddCommand(bootstrapConfigCmd())
c.AddCommand(clusterConfigCmd())
c.AddCommand(listenerConfigCmd())
c.AddCommand(routeConfigCmd())

return c
}

func allConfigCmd() *cobra.Command {

allConfigCmd := &cobra.Command{
configCmd := &cobra.Command{
Use: "all <pod-name>",
Short: "Retrieves all Envoy xDS resources from the specified pod",
Long: `Retrieves information about all Envoy xDS resources from the Envoy instance in the specified pod.`,
Expand All @@ -81,7 +89,7 @@ func allConfigCmd() *cobra.Command {
},
}

return allConfigCmd
return configCmd
}

func runAllConfig(c *cobra.Command, args []string) error {
Expand Down Expand Up @@ -111,7 +119,288 @@ func runAllConfig(c *cobra.Command, args []string) error {
}
defer fw.Stop()

out, err := extractConfigDump(fw, output)
configDump, err := extractConfigDump(fw)
if err != nil {
return err
}

out, err := marshalEnvoyProxyConfig(configDump, output)
if err != nil {
return err
}

_, err = fmt.Fprintln(c.OutOrStdout(), string(out))
return err
}

func bootstrapConfigCmd() *cobra.Command {

configCmd := &cobra.Command{
Use: "bootstrap <pod-name>",
Short: "Retrieves bootstrap Envoy xDS resources from the specified pod",
Long: `Retrieves information about bootstrap Envoy xDS resources from the Envoy instance in the specified pod.`,
Example: ` # Retrieve summary about bootstrap configuration for a given pod from Envoy.
egctl config envoy-proxy bootstrap <pod-name> -n <pod-namespace>
# Retrieve full configuration dump as YAML
egctl config envoy-proxy bootstrap <pod-name> -n <pod-namespace> -o yaml
# Retrieve full configuration dump with short syntax
egctl c proxy bootstrap <pod-name> -n <pod-namespace>
`,
Run: func(c *cobra.Command, args []string) {
cmdutil.CheckErr(runBootstrapConfig(c, args))
},
}

return configCmd
}

func runBootstrapConfig(c *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("pod name is required")
}

podName = args[0]

if podName == "" {
return fmt.Errorf("pod name is required")
}

if podNamespace == "" {
return fmt.Errorf("pod namespace is required")
}

fw, err := portForwarder(types.NamespacedName{
Namespace: podNamespace,
Name: podName,
})
if err != nil {
return err
}
if err := fw.Start(); err != nil {
return err
}
defer fw.Stop()

configDump, err := extractConfigDump(fw)
if err != nil {
return err
}

bootstrap, err := findXDSResourceFromConfigDump(BootstrapEnvoyConfigType, configDump)
if err != nil {
return err
}

out, err := marshalEnvoyProxyConfig(bootstrap, output)
if err != nil {
return err
}

_, err = fmt.Fprintln(c.OutOrStdout(), string(out))
return err
}

func clusterConfigCmd() *cobra.Command {

configCmd := &cobra.Command{
Use: "cluster <pod-name>",
Short: "Retrieves cluster Envoy xDS resources from the specified pod",
Long: `Retrieves information about cluster Envoy xDS resources from the Envoy instance in the specified pod.`,
Example: ` # Retrieve summary about cluster configuration for a given pod from Envoy.
egctl config envoy-proxy cluster <pod-name> -n <pod-namespace>
# Retrieve full configuration dump as YAML
egctl config envoy-proxy cluster <pod-name> -n <pod-namespace> -o yaml
# Retrieve full configuration dump with short syntax
egctl c proxy cluster <pod-name> -n <pod-namespace>
`,
Run: func(c *cobra.Command, args []string) {
cmdutil.CheckErr(runClusterConfig(c, args))
},
}

return configCmd
}

func runClusterConfig(c *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("pod name is required")
}

podName = args[0]

if podName == "" {
return fmt.Errorf("pod name is required")
}

if podNamespace == "" {
return fmt.Errorf("pod namespace is required")
}

fw, err := portForwarder(types.NamespacedName{
Namespace: podNamespace,
Name: podName,
})
if err != nil {
return err
}
if err := fw.Start(); err != nil {
return err
}
defer fw.Stop()

configDump, err := extractConfigDump(fw)
if err != nil {
return err
}

cluster, err := findXDSResourceFromConfigDump(ClusterEnvoyConfigType, configDump)
if err != nil {
return err
}

out, err := marshalEnvoyProxyConfig(cluster, output)
if err != nil {
return err
}

_, err = fmt.Fprintln(c.OutOrStdout(), string(out))
return err
}

func listenerConfigCmd() *cobra.Command {

configCmd := &cobra.Command{
Use: "listener <pod-name>",
Short: "Retrieves listener Envoy xDS resources from the specified pod",
Long: `Retrieves information about listener Envoy xDS resources from the Envoy instance in the specified pod.`,
Example: ` # Retrieve summary about listener configuration for a given pod from Envoy.
egctl config envoy-proxy listener <pod-name> -n <pod-namespace>
# Retrieve full configuration dump as YAML
egctl config envoy-proxy listener <pod-name> -n <pod-namespace> -o yaml
# Retrieve full configuration dump with short syntax
egctl c proxy listener <pod-name> -n <pod-namespace>
`,
Run: func(c *cobra.Command, args []string) {
cmdutil.CheckErr(runListenerConfig(c, args))
},
}

return configCmd
}

func runListenerConfig(c *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("pod name is required")
}

podName = args[0]

if podName == "" {
return fmt.Errorf("pod name is required")
}

if podNamespace == "" {
return fmt.Errorf("pod namespace is required")
}

fw, err := portForwarder(types.NamespacedName{
Namespace: podNamespace,
Name: podName,
})
if err != nil {
return err
}
if err := fw.Start(); err != nil {
return err
}
defer fw.Stop()

configDump, err := extractConfigDump(fw)
if err != nil {
return err
}

listener, err := findXDSResourceFromConfigDump(ListenerEnvoyConfigType, configDump)
if err != nil {
return err
}

out, err := marshalEnvoyProxyConfig(listener, output)
if err != nil {
return err
}

_, err = fmt.Fprintln(c.OutOrStdout(), string(out))
return err
}

func routeConfigCmd() *cobra.Command {

configCmd := &cobra.Command{
Use: "route <pod-name>",
Short: "Retrieves route Envoy xDS resources from the specified pod",
Long: `Retrieves information about route Envoy xDS resources from the Envoy instance in the specified pod.`,
Example: ` # Retrieve summary about route configuration for a given pod from Envoy.
egctl config envoy-proxy route <pod-name> -n <pod-namespace>
# Retrieve full configuration dump as YAML
egctl config envoy-proxy route <pod-name> -n <pod-namespace> -o yaml
# Retrieve full configuration dump with short syntax
egctl c proxy route <pod-name> -n <pod-namespace>
`,
Run: func(c *cobra.Command, args []string) {
cmdutil.CheckErr(runRouteConfig(c, args))
},
}

return configCmd
}

func runRouteConfig(c *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("pod name is required")
}

podName = args[0]

if podName == "" {
return fmt.Errorf("pod name is required")
}

if podNamespace == "" {
return fmt.Errorf("pod namespace is required")
}

fw, err := portForwarder(types.NamespacedName{
Namespace: podNamespace,
Name: podName,
})
if err != nil {
return err
}
if err := fw.Start(); err != nil {
return err
}
defer fw.Stop()

configDump, err := extractConfigDump(fw)
if err != nil {
return err
}

route, err := findXDSResourceFromConfigDump(RouteEnvoyConfigType, configDump)
if err != nil {
return err
}

out, err := marshalEnvoyProxyConfig(route, output)
if err != nil {
return err
}
Expand Down Expand Up @@ -142,8 +431,8 @@ func portForwarder(nn types.NamespacedName) (kube.PortForwarder, error) {
return fw, nil
}

func extractConfigDump(fw kube.PortForwarder, output string) ([]byte, error) {
out, err := configDumpRequest(fw.Address())
func marshalEnvoyProxyConfig(configDump protoreflect.ProtoMessage, output string) ([]byte, error) {
out, err := protojson.Marshal(configDump)
if err != nil {
return nil, err
}
Expand All @@ -158,6 +447,20 @@ func extractConfigDump(fw kube.PortForwarder, output string) ([]byte, error) {
return out, nil
}

func extractConfigDump(fw kube.PortForwarder) (*adminv3.ConfigDump, error) {
out, err := configDumpRequest(fw.Address())
if err != nil {
return nil, err
}

configDump := &adminv3.ConfigDump{}
if err := protojson.Unmarshal(out, configDump); err != nil {
return nil, err
}

return configDump, nil
}

func configDumpRequest(address string) ([]byte, error) {
req, err := http.NewRequest("GET", fmt.Sprintf("http://%s/config_dump", address), nil)
if err != nil {
Expand Down
Loading

0 comments on commit a98e065

Please sign in to comment.