diff --git a/cmd/daemon/daemon_main.go b/cmd/daemon/daemon_main.go index ddb090d..693a3e8 100644 --- a/cmd/daemon/daemon_main.go +++ b/cmd/daemon/daemon_main.go @@ -4,9 +4,10 @@ import ( "expvar" "flag" "log" - "net" "net/http" "os" + + "github.com/instana/envcheck/network" ) var ( @@ -23,7 +24,7 @@ func Exec(address string, info DownwardInfo) error { publish("nodeIP", info.NodeIP) publish("podIP", info.PodIP) - ifs, err := MapInterfaces() + ifs, err := network.MapInterfaces() if err != nil { return err } @@ -37,30 +38,6 @@ func Exec(address string, info DownwardInfo) error { return http.ListenAndServe(address, nil) } -// MapInterfaces provides a map of interfaces to IP addresses. -func MapInterfaces() (map[string][]string, error) { - m := make(map[string][]string) - interfaces, err := net.Interfaces() - if err != nil { - return nil, err - } - - for _, adapter := range interfaces { - addresses, err := adapter.Addrs() - if err != nil { - return nil, err - } - - var ips []string - for _, address := range addresses { - ips = append(ips, address.String()) - } - m[adapter.Name] = ips - } - - return m, nil -} - // PingHandler handles pings from the pinger client. func PingHandler(info DownwardInfo) func(w http.ResponseWriter, r *http.Request) { b := []byte(info.NodeIP) diff --git a/cmd/envcheckctl/envcheckctl_main.go b/cmd/envcheckctl/envcheckctl_main.go index b742063..e0c5ce8 100644 --- a/cmd/envcheckctl/envcheckctl_main.go +++ b/cmd/envcheckctl/envcheckctl_main.go @@ -164,20 +164,75 @@ type EnvcheckConfig struct { Podfile string } +var ( + ErrNoSubcommand = fmt.Errorf("no sub-command specified") + ErrInvalidSubcommand = fmt.Errorf("invalid sub-command specified") +) + +func Parse(args []string, kubepath string, w io.Writer) (*EnvcheckConfig, error) { + var fs []*flag.FlagSet + + var daemonConfig = EnvcheckConfig{ApplyDaemon: true} + daemonFlags := flag.NewFlagSet("daemon", flag.ExitOnError) + daemonFlags.StringVar(&daemonConfig.AgentNamespace, "ns", "instana-agent", "daemon namespace") + daemonFlags.StringVar(&daemonConfig.Kubeconfig, "kubeconfig", kubepath, "absolute path to the kubeconfig file") + daemonFlags.SetOutput(w) + fs = append(fs, daemonFlags) + + var inspectConfig = EnvcheckConfig{} + inspectFlags := flag.NewFlagSet("inspect", flag.ExitOnError) + inspectFlags.BoolVar(&inspectConfig.IsLive, "live", true, "retrieve pods from cluster") + inspectFlags.StringVar(&inspectConfig.AgentNamespace, "agentns", "instana-agent", "Instana agent namespace") + inspectFlags.StringVar(&inspectConfig.Podfile, "podfile", "", "podfile") + inspectFlags.StringVar(&inspectConfig.Kubeconfig, "kubeconfig", kubepath, "absolute path to the kubeconfig file") + inspectFlags.SetOutput(w) + fs = append(fs, inspectFlags) + + var pingConfig = EnvcheckConfig{ApplyPinger: true} + pingFlags := flag.NewFlagSet("ping", flag.ExitOnError) + pingFlags.StringVar(&pingConfig.PingerHost, "host", "", "override IP or DNS name to ping. defaults to nodeIP if blank") + pingFlags.StringVar(&pingConfig.PingerNamespace, "ns", "default", "ping client namespace") + pingFlags.StringVar(&pingConfig.Kubeconfig, "kubeconfig", kubepath, "absolute path to the kubeconfig file") + pingFlags.SetOutput(w) + fs = append(fs, pingFlags) + + if len(args) < 2 { + w.Write([]byte("Usage: " + args[0] + " requires a subcommand\n")) + for _, v := range fs { + v.Usage() + } + return nil, ErrNoSubcommand + } + + cmdArgs := args[2:] + switch args[1] { + case "daemon": + daemonFlags.Parse(cmdArgs) + return &daemonConfig, nil + case "inspect": + inspectFlags.Parse(cmdArgs) + return &inspectConfig, nil + case "ping": + pingFlags.Parse(cmdArgs) + return &pingConfig, nil + } + + w.Write([]byte("Usage: " + args[0] + " requires a subcommand\n")) + for _, v := range fs { + w.Write([]byte("\n")) + v.Usage() + } + return nil, ErrInvalidSubcommand +} + func main() { - var config EnvcheckConfig - - flag.StringVar(&config.AgentNamespace, "agentns", "instana-agent", "Instana agent namespace") - flag.StringVar(&config.Kubeconfig, "kubeconfig", configPath(), "absolute path to the kubeconfig file") - flag.BoolVar(&config.ApplyDaemon, "daemon", false, "deploy daemon to cluster") - flag.BoolVar(&config.ApplyPinger, "ping", false, "deploy ping client to cluster") - flag.BoolVar(&config.IsLive, "live", true, "retrieve pods from cluster") - flag.StringVar(&config.PingerHost, "pinghost", "", "override IP or DNS name to ping. defaults to nodeIP") - flag.StringVar(&config.PingerNamespace, "pingns", "default", "ping client namespace") - flag.StringVar(&config.Podfile, "podfile", "", "podfile") - flag.Parse() - - Exec(config) + kubepath := configPath() + config, err := Parse(os.Args, kubepath, os.Stderr) + if err != nil { + os.Exit(1) + } + + Exec(*config) } func configPath() string { diff --git a/cmd/envcheckctl/envcheckctl_main_test.go b/cmd/envcheckctl/envcheckctl_main_test.go index c0e9fdf..87a95ed 100644 --- a/cmd/envcheckctl/envcheckctl_main_test.go +++ b/cmd/envcheckctl/envcheckctl_main_test.go @@ -1,6 +1,37 @@ -package main_test +package main -import "testing" +import ( + "io/ioutil" + "reflect" + "testing" +) + +func Test_parse_flags(t *testing.T) { + cases := map[string]struct { + args []string + config *EnvcheckConfig + err error + }{ + "no subcommand": {[]string{"envcheckctl"}, nil, ErrNoSubcommand}, + "invalid subcommand": {[]string{"envcheckctl", "foobar"}, nil, ErrNoSubcommand}, + "daemon": {[]string{"envcheckctl", "daemon"}, &EnvcheckConfig{ApplyDaemon: true, AgentNamespace: "instana-agent"}, nil}, + "inspect": {[]string{"envcheckctl", "inspect"}, &EnvcheckConfig{AgentNamespace: "instana-agent", IsLive: true}, nil}, + "ping": {[]string{"envcheckctl", "ping"}, &EnvcheckConfig{ApplyPinger: true, PingerNamespace: "default"}, nil}, + } + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + actual, err := Parse(tc.args, "", ioutil.Discard) + if err != err { + t.Errorf("err=%v, want %v", err, tc.err) + } + + if !reflect.DeepEqual(actual, tc.config) { + t.Errorf("config=%#v, want %#v", actual, tc.config) + } + }) + } + +} func Test_todo(t *testing.T) { t.Skip("Add a test") diff --git a/cmd/pinger/pinger_main.go b/cmd/pinger/pinger_main.go index 2217742..afd8839 100644 --- a/cmd/pinger/pinger_main.go +++ b/cmd/pinger/pinger_main.go @@ -10,6 +10,7 @@ import ( "os" "time" + "github.com/instana/envcheck/network" "github.com/instana/envcheck/ping" "github.com/jackpal/gateway" ) @@ -32,6 +33,15 @@ func Exec(address string, info ping.DownwardInfo, c *http.Client) error { publish("nodeIP", info.NodeIP) publish("podIP", info.PodIP) + ifs, err := network.MapInterfaces() + if err != nil { + return err + } + + for k, v := range ifs { + log.Printf("pod=%s/%s if=%s ips=%v\n", info.Namespace, info.Name, k, v) + } + client := ping.New(c) pingLoop(client, address, info) diff --git a/network/adapters.go b/network/adapters.go new file mode 100644 index 0000000..5579a45 --- /dev/null +++ b/network/adapters.go @@ -0,0 +1,27 @@ +package network + +import "net" + +// MapInterfaces provides a map of interfaces to IP addresses. +func MapInterfaces() (map[string][]string, error) { + m := make(map[string][]string) + interfaces, err := net.Interfaces() + if err != nil { + return nil, err + } + + for _, adapter := range interfaces { + addresses, err := adapter.Addrs() + if err != nil { + return nil, err + } + + var ips []string + for _, address := range addresses { + ips = append(ips, address.String()) + } + m[adapter.Name] = ips + } + + return m, nil +}