mirror of https://github.com/linkerd/linkerd2.git
Remove package-scoped vars in cmd package (#975)
* Remove package-scoped vars in cmd package * Run gofmt on all cmd package files * Re-add missing Args setting on check command Signed-off-by: Kevin Lingerfelt <kl@buoyant.io>
This commit is contained in:
parent
d256377eea
commit
2baeaacbc8
|
|
@ -23,44 +23,61 @@ const (
|
|||
versionCheckURL = "https://versioncheck.conduit.io/version.json"
|
||||
)
|
||||
|
||||
var versionOverride string
|
||||
type checkOptions struct {
|
||||
versionOverride string
|
||||
}
|
||||
|
||||
var checkCmd = &cobra.Command{
|
||||
Use: "check",
|
||||
Short: "Check your Conduit installation for potential problems.",
|
||||
Long: `Check your Conduit installation for potential problems. The check command will perform various checks of your
|
||||
func newCheckOptions() *checkOptions {
|
||||
return &checkOptions{
|
||||
versionOverride: "",
|
||||
}
|
||||
}
|
||||
|
||||
func newCmdCheck() *cobra.Command {
|
||||
options := newCheckOptions()
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "check",
|
||||
Short: "Check your Conduit installation for potential problems.",
|
||||
Long: `Check your Conduit installation for potential problems. The check command will perform various checks of your
|
||||
local system, the Conduit control plane, and connectivity between those. The process will exit with non-zero check if
|
||||
problems were found.`,
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Args: cobra.NoArgs,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
kubeApi, err := k8s.NewAPI(kubeconfigPath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error with Kubernetes API: %s\n", err.Error())
|
||||
statusCheckResultWasError(os.Stdout)
|
||||
os.Exit(2)
|
||||
}
|
||||
kubeApi, err := k8s.NewAPI(kubeconfigPath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error with Kubernetes API: %s\n", err.Error())
|
||||
statusCheckResultWasError(os.Stdout)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
var conduitApi pb.ApiClient
|
||||
if apiAddr != "" {
|
||||
conduitApi, err = public.NewInternalClient(apiAddr)
|
||||
} else {
|
||||
conduitApi, err = public.NewExternalClient(controlPlaneNamespace, kubeApi)
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error with Conduit API: %s\n", err.Error())
|
||||
statusCheckResultWasError(os.Stdout)
|
||||
os.Exit(2)
|
||||
}
|
||||
var conduitApi pb.ApiClient
|
||||
if apiAddr != "" {
|
||||
conduitApi, err = public.NewInternalClient(apiAddr)
|
||||
} else {
|
||||
conduitApi, err = public.NewExternalClient(controlPlaneNamespace, kubeApi)
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error with Conduit API: %s\n", err.Error())
|
||||
statusCheckResultWasError(os.Stdout)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
grpcStatusChecker := healthcheck.NewGrpcStatusChecker(public.ConduitApiSubsystemName, conduitApi)
|
||||
versionStatusChecker := version.NewVersionStatusChecker(versionCheckURL, versionOverride, conduitApi)
|
||||
grpcStatusChecker := healthcheck.NewGrpcStatusChecker(public.ConduitApiSubsystemName, conduitApi)
|
||||
versionStatusChecker := version.NewVersionStatusChecker(versionCheckURL, options.versionOverride, conduitApi)
|
||||
|
||||
err = checkStatus(os.Stdout, kubeApi, grpcStatusChecker, versionStatusChecker)
|
||||
if err != nil {
|
||||
os.Exit(2)
|
||||
}
|
||||
},
|
||||
err = checkStatus(os.Stdout, kubeApi, grpcStatusChecker, versionStatusChecker)
|
||||
if err != nil {
|
||||
os.Exit(2)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Args = cobra.NoArgs
|
||||
cmd.PersistentFlags().StringVar(&options.versionOverride, "expected-version", options.versionOverride, "Overrides the version used when checking if Conduit is running the latest version (mostly for testing)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func checkStatus(w io.Writer, checkers ...healthcheck.StatusChecker) error {
|
||||
|
|
@ -119,9 +136,3 @@ func statusCheckResultWasError(w io.Writer) error {
|
|||
fmt.Fprintln(w, "Status check results are [ERROR]")
|
||||
return errors.New("error during status check")
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(checkCmd)
|
||||
checkCmd.Args = cobra.NoArgs
|
||||
checkCmd.PersistentFlags().StringVar(&versionOverride, "expected-version", "", "Overrides the version used when checking if Conduit is running the latest version (mostly for testing)")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var example = ` # bash <= 3.2
|
||||
func newCmdCompletion() *cobra.Command {
|
||||
example := ` # bash <= 3.2
|
||||
source /dev/stdin <<< "$(conduit completion bash)"
|
||||
|
||||
# bash >= 4.0
|
||||
|
|
@ -28,37 +29,36 @@ var example = ` # bash <= 3.2
|
|||
# zsh on osx / oh-my-zsh
|
||||
conduit completion zsh > "${fpath[1]}/_conduit"`
|
||||
|
||||
var completionCmd = &cobra.Command{
|
||||
Use: "completion [bash|zsh]",
|
||||
Short: "Shell completion",
|
||||
Long: "Output completion code for the specified shell (bash or zsh).",
|
||||
Example: example,
|
||||
Args: cobra.ExactArgs(1),
|
||||
ValidArgs: []string{"bash", "zsh"},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
out, err := getCompletion(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "completion [bash|zsh]",
|
||||
Short: "Shell completion",
|
||||
Long: "Output completion code for the specified shell (bash or zsh).",
|
||||
Example: example,
|
||||
Args: cobra.ExactArgs(1),
|
||||
ValidArgs: []string{"bash", "zsh"},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
out, err := getCompletion(args[0], cmd.Parent())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Print(out)
|
||||
return nil
|
||||
},
|
||||
fmt.Print(out)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(completionCmd)
|
||||
}
|
||||
|
||||
func getCompletion(sh string) (string, error) {
|
||||
func getCompletion(sh string, parent *cobra.Command) (string, error) {
|
||||
var err error
|
||||
var buf bytes.Buffer
|
||||
|
||||
switch sh {
|
||||
case "bash":
|
||||
err = RootCmd.GenBashCompletion(&buf)
|
||||
err = parent.GenBashCompletion(&buf)
|
||||
case "zsh":
|
||||
err = RootCmd.GenZshCompletion(&buf)
|
||||
err = parent.GenZshCompletion(&buf)
|
||||
default:
|
||||
err = errors.New("unsupported shell type (must be bash or zsh): " + sh)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@ import (
|
|||
func TestCompletion(t *testing.T) {
|
||||
t.Run("Returns completion code", func(t *testing.T) {
|
||||
|
||||
bash, err := getCompletion("bash")
|
||||
bash, err := getCompletion("bash", RootCmd)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
}
|
||||
|
||||
zsh, err := getCompletion("zsh")
|
||||
zsh, err := getCompletion("zsh", RootCmd)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %+v", err)
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ func TestCompletion(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("Fails with invalid shell type", func(t *testing.T) {
|
||||
out, err := getCompletion("foo")
|
||||
out, err := getCompletion("foo", RootCmd)
|
||||
if err == nil {
|
||||
t.Fatalf("Unexpected success for invalid shell type: %+v", out)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,89 +25,110 @@ const (
|
|||
showURL = "url"
|
||||
)
|
||||
|
||||
var dashboardProxyPort int
|
||||
var dashboardShow string
|
||||
type dashboardOptions struct {
|
||||
dashboardProxyPort int
|
||||
dashboardShow string
|
||||
}
|
||||
|
||||
var dashboardCmd = &cobra.Command{
|
||||
Use: "dashboard [flags]",
|
||||
Short: "Open the Conduit dashboard in a web browser",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if dashboardProxyPort < 0 {
|
||||
return fmt.Errorf("port must be greater than or equal to zero, was %d", dashboardProxyPort)
|
||||
}
|
||||
func newDashboardOptions() *dashboardOptions {
|
||||
return &dashboardOptions{
|
||||
dashboardProxyPort: 0,
|
||||
dashboardShow: "conduit",
|
||||
}
|
||||
}
|
||||
|
||||
if dashboardShow != showConduit && dashboardShow != showGrafana && dashboardShow != showURL {
|
||||
return fmt.Errorf("unknown value for 'show' param, was: %s, must be one of: %s, %s, %s", dashboardShow, showConduit, showGrafana, showURL)
|
||||
}
|
||||
func newCmdDashboard() *cobra.Command {
|
||||
options := newDashboardOptions()
|
||||
|
||||
kubernetesProxy, err := k8s.NewProxy(kubeconfigPath, dashboardProxyPort)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to initialize proxy: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "dashboard [flags]",
|
||||
Short: "Open the Conduit dashboard in a web browser",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if options.dashboardProxyPort < 0 {
|
||||
return fmt.Errorf("port must be greater than or equal to zero, was %d", options.dashboardProxyPort)
|
||||
}
|
||||
|
||||
url, err := kubernetesProxy.URLFor(controlPlaneNamespace, "/services/web:http/proxy/")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to generate URL for dashboard: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if options.dashboardShow != showConduit && options.dashboardShow != showGrafana && options.dashboardShow != showURL {
|
||||
return fmt.Errorf("unknown value for 'show' param, was: %s, must be one of: %s, %s, %s",
|
||||
options.dashboardShow, showConduit, showGrafana, showURL)
|
||||
}
|
||||
|
||||
grafanaUrl, err := kubernetesProxy.URLFor(controlPlaneNamespace, "/services/grafana:http/proxy/")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to generate URL for Grafana: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
client, err := newPublicAPIClient()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to initialize Conduit API client: %+v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
dashboardAvailable, err := isDashboardAvailable(client)
|
||||
if err != nil {
|
||||
log.Debugf("Error checking dashboard availability: %s", err)
|
||||
}
|
||||
|
||||
if err != nil || !dashboardAvailable {
|
||||
fmt.Fprintf(os.Stderr, "Conduit is not running in the \"%s\" namespace\n", controlPlaneNamespace)
|
||||
fmt.Fprintf(os.Stderr, "Install with: conduit install --conduit-namespace %s | kubectl apply -f -\n", controlPlaneNamespace)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Conduit dashboard available at:\n%s\n", url.String())
|
||||
fmt.Printf("Grafana dashboard available at:\n%s\n", grafanaUrl.String())
|
||||
|
||||
switch dashboardShow {
|
||||
case showConduit:
|
||||
fmt.Println("Opening Conduit dashboard in the default browser")
|
||||
|
||||
err = browser.OpenURL(url.String())
|
||||
kubernetesProxy, err := k8s.NewProxy(kubeconfigPath, options.dashboardProxyPort)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to open Conduit URL %s in the default browser: %s", url, err)
|
||||
fmt.Fprintf(os.Stderr, "Failed to initialize proxy: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
case showGrafana:
|
||||
fmt.Println("Opening Grafana dashboard in the default browser")
|
||||
|
||||
err = browser.OpenURL(grafanaUrl.String())
|
||||
url, err := kubernetesProxy.URLFor(controlPlaneNamespace, "/services/web:http/proxy/")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to open Grafana URL %s in the default browser: %s", grafanaUrl, err)
|
||||
fmt.Fprintf(os.Stderr, "Failed to generate URL for dashboard: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
case showURL:
|
||||
// no-op, we already printed the URLs
|
||||
}
|
||||
|
||||
// blocks until killed
|
||||
err = kubernetesProxy.Run()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error running proxy: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
grafanaUrl, err := kubernetesProxy.URLFor(controlPlaneNamespace, "/services/grafana:http/proxy/")
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to generate URL for Grafana: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
client, err := newPublicAPIClient()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to initialize Conduit API client: %+v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
dashboardAvailable, err := isDashboardAvailable(client)
|
||||
if err != nil {
|
||||
log.Debugf("Error checking dashboard availability: %s", err)
|
||||
}
|
||||
|
||||
if err != nil || !dashboardAvailable {
|
||||
fmt.Fprintf(os.Stderr, "Conduit is not running in the \"%s\" namespace\n", controlPlaneNamespace)
|
||||
fmt.Fprintf(os.Stderr, "Install with: conduit install --conduit-namespace %s | kubectl apply -f -\n", controlPlaneNamespace)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("Conduit dashboard available at:\n%s\n", url.String())
|
||||
fmt.Printf("Grafana dashboard available at:\n%s\n", grafanaUrl.String())
|
||||
|
||||
switch options.dashboardShow {
|
||||
case showConduit:
|
||||
fmt.Println("Opening Conduit dashboard in the default browser")
|
||||
|
||||
err = browser.OpenURL(url.String())
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to open Conduit URL %s in the default browser: %s", url, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
case showGrafana:
|
||||
fmt.Println("Opening Grafana dashboard in the default browser")
|
||||
|
||||
err = browser.OpenURL(grafanaUrl.String())
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to open Grafana URL %s in the default browser: %s", grafanaUrl, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
case showURL:
|
||||
// no-op, we already printed the URLs
|
||||
}
|
||||
|
||||
// blocks until killed
|
||||
err = kubernetesProxy.Run()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error running proxy: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Args = cobra.NoArgs
|
||||
// This is identical to what `kubectl proxy --help` reports, `--port 0` indicates a random port.
|
||||
cmd.PersistentFlags().IntVarP(&options.dashboardProxyPort, "port", "p", options.dashboardProxyPort, "The port on which to run the proxy (when set to 0, a random port will be used)")
|
||||
cmd.PersistentFlags().StringVar(&options.dashboardShow, "show", options.dashboardShow, "Open a dashboard in a browser or show URLs in the CLI (one of: conduit, grafana, url)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func isDashboardAvailable(client pb.ApiClient) (bool, error) {
|
||||
|
|
@ -123,13 +144,3 @@ func isDashboardAvailable(client pb.ApiClient) (bool, error) {
|
|||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(dashboardCmd)
|
||||
dashboardCmd.Args = cobra.NoArgs
|
||||
|
||||
// This is identical to what `kubectl proxy --help` reports, `--port 0`
|
||||
// indicates a random port.
|
||||
dashboardCmd.PersistentFlags().IntVarP(&dashboardProxyPort, "port", "p", 0, "The port on which to run the proxy (when set to 0, a random port will be used)")
|
||||
dashboardCmd.PersistentFlags().StringVar(&dashboardShow, "show", "conduit", "Open a dashboard in a browser or show URLs in the CLI (one of: conduit, grafana, url)")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,49 +10,49 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var getCmd = &cobra.Command{
|
||||
Use: "get [flags] pods",
|
||||
Short: "Display one or many mesh resources",
|
||||
Long: `Display one or many mesh resources.
|
||||
func newCmdGet() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "get [flags] pods",
|
||||
Short: "Display one or many mesh resources",
|
||||
Long: `Display one or many mesh resources.
|
||||
|
||||
Only pod resources (aka pods, po) are supported.`,
|
||||
Example: ` # get all pods
|
||||
Example: ` # get all pods
|
||||
conduit get pods`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return errors.New("please specify a resource type")
|
||||
}
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return errors.New("please specify a resource type")
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
return errors.New("please specify only one resource type")
|
||||
}
|
||||
if len(args) > 1 {
|
||||
return errors.New("please specify only one resource type")
|
||||
}
|
||||
|
||||
friendlyName := args[0]
|
||||
resourceType, err := k8s.CanonicalKubernetesNameFromFriendlyName(friendlyName)
|
||||
friendlyName := args[0]
|
||||
resourceType, err := k8s.CanonicalKubernetesNameFromFriendlyName(friendlyName)
|
||||
|
||||
if err != nil || resourceType != k8s.Pods {
|
||||
return fmt.Errorf("invalid resource type %s, only %s are allowed as resource types", friendlyName, k8s.Pods)
|
||||
}
|
||||
client, err := newPublicAPIClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err != nil || resourceType != k8s.Pods {
|
||||
return fmt.Errorf("invalid resource type %s, only %s are allowed as resource types", friendlyName, k8s.Pods)
|
||||
}
|
||||
client, err := newPublicAPIClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
podNames, err := getPods(client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
podNames, err := getPods(client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, podName := range podNames {
|
||||
fmt.Println(podName)
|
||||
}
|
||||
for _, podName := range podNames {
|
||||
fmt.Println(podName)
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(getCmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func getPods(apiClient pb.ApiClient) ([]string, error) {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import (
|
|||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/runconduit/conduit/pkg/k8s"
|
||||
"github.com/runconduit/conduit/pkg/version"
|
||||
"github.com/spf13/cobra"
|
||||
appsV1 "k8s.io/api/apps/v1"
|
||||
batchV1 "k8s.io/api/batch/v1"
|
||||
|
|
@ -29,54 +28,73 @@ const (
|
|||
ControlPlanePodName = "controller"
|
||||
)
|
||||
|
||||
var (
|
||||
type injectOptions struct {
|
||||
initImage string
|
||||
proxyImage string
|
||||
proxyUID int64
|
||||
inboundPort uint
|
||||
outboundPort uint
|
||||
ignoreInboundPorts []uint
|
||||
ignoreOutboundPorts []uint
|
||||
proxyControlPort uint
|
||||
proxyMetricsPort uint
|
||||
proxyAPIPort uint
|
||||
proxyLogLevel string
|
||||
)
|
||||
*proxyConfigOptions
|
||||
}
|
||||
|
||||
var injectCmd = &cobra.Command{
|
||||
Use: "inject [flags] CONFIG-FILE",
|
||||
Short: "Add the Conduit proxy to a Kubernetes config",
|
||||
Long: `Add the Conduit proxy to a Kubernetes config.
|
||||
func newInjectOptions() *injectOptions {
|
||||
return &injectOptions{
|
||||
initImage: "gcr.io/runconduit/proxy-init",
|
||||
inboundPort: 4143,
|
||||
outboundPort: 4140,
|
||||
ignoreInboundPorts: nil,
|
||||
ignoreOutboundPorts: nil,
|
||||
proxyConfigOptions: newProxyConfigOptions(),
|
||||
}
|
||||
}
|
||||
|
||||
func newCmdInject() *cobra.Command {
|
||||
options := newInjectOptions()
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "inject [flags] CONFIG-FILE",
|
||||
Short: "Add the Conduit proxy to a Kubernetes config",
|
||||
Long: `Add the Conduit proxy to a Kubernetes config.
|
||||
|
||||
You can use a config file from stdin by using the '-' argument
|
||||
with 'conduit inject'. e.g. curl http://url.to/yml | conduit inject -
|
||||
`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("please specify a kubernetes resource file")
|
||||
}
|
||||
|
||||
var in io.Reader
|
||||
var err error
|
||||
|
||||
if args[0] == "-" {
|
||||
in = os.Stdin
|
||||
} else {
|
||||
if in, err = os.Open(args[0]); err != nil {
|
||||
return err
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("please specify a kubernetes resource file")
|
||||
}
|
||||
}
|
||||
exitCode := runInjectCmd(in, os.Stderr, os.Stdout, conduitVersion)
|
||||
os.Exit(exitCode)
|
||||
return nil
|
||||
},
|
||||
|
||||
var in io.Reader
|
||||
var err error
|
||||
|
||||
if args[0] == "-" {
|
||||
in = os.Stdin
|
||||
} else {
|
||||
if in, err = os.Open(args[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
exitCode := runInjectCmd(in, os.Stderr, os.Stdout, options)
|
||||
os.Exit(exitCode)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
addProxyConfigFlags(cmd, options.proxyConfigOptions)
|
||||
cmd.PersistentFlags().StringVar(&options.initImage, "init-image", options.initImage, "Conduit init container image name")
|
||||
cmd.PersistentFlags().UintVar(&options.inboundPort, "inbound-port", options.inboundPort, "Proxy port to use for inbound traffic")
|
||||
cmd.PersistentFlags().UintVar(&options.outboundPort, "outbound-port", options.outboundPort, "Proxy port to use for outbound traffic")
|
||||
cmd.PersistentFlags().UintSliceVar(&options.ignoreInboundPorts, "skip-inbound-ports", options.ignoreInboundPorts, "Ports that should skip the proxy and send directly to the application")
|
||||
cmd.PersistentFlags().UintSliceVar(&options.ignoreOutboundPorts, "skip-outbound-ports", options.ignoreOutboundPorts, "Outbound ports that should skip the proxy")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// Returns the integer representation of os.Exit code; 0 on success and 1 on failure.
|
||||
func runInjectCmd(input io.Reader, errWriter, outWriter io.Writer, version string) int {
|
||||
func runInjectCmd(input io.Reader, errWriter, outWriter io.Writer, options *injectOptions) int {
|
||||
postInjectBuf := &bytes.Buffer{}
|
||||
err := InjectYAML(input, postInjectBuf, version)
|
||||
err := InjectYAML(input, postInjectBuf, options)
|
||||
if err != nil {
|
||||
fmt.Fprintf(errWriter, "Error injecting conduit proxy: %v\n", err)
|
||||
return 1
|
||||
|
|
@ -93,7 +111,7 @@ func runInjectCmd(input io.Reader, errWriter, outWriter io.Writer, version strin
|
|||
* and init-container injected. If the pod is unsuitable for having them
|
||||
* injected, return null.
|
||||
*/
|
||||
func injectPodTemplateSpec(t *v1.PodTemplateSpec, controlPlaneDNSNameOverride, version string, k8sLabels map[string]string) bool {
|
||||
func injectPodTemplateSpec(t *v1.PodTemplateSpec, controlPlaneDNSNameOverride string, k8sLabels map[string]string, options *injectOptions) bool {
|
||||
// Pods with `hostNetwork=true` share a network namespace with the host. The
|
||||
// init-container would destroy the iptables configuration on the host, so
|
||||
// skip the injection in this case.
|
||||
|
|
@ -102,21 +120,21 @@ func injectPodTemplateSpec(t *v1.PodTemplateSpec, controlPlaneDNSNameOverride, v
|
|||
}
|
||||
|
||||
f := false
|
||||
inboundSkipPorts := append(ignoreInboundPorts, proxyControlPort, proxyMetricsPort)
|
||||
inboundSkipPorts := append(options.ignoreInboundPorts, options.proxyControlPort, options.proxyMetricsPort)
|
||||
inboundSkipPortsStr := make([]string, len(inboundSkipPorts))
|
||||
for i, p := range inboundSkipPorts {
|
||||
inboundSkipPortsStr[i] = strconv.Itoa(int(p))
|
||||
}
|
||||
|
||||
outboundSkipPortsStr := make([]string, len(ignoreOutboundPorts))
|
||||
for i, p := range ignoreOutboundPorts {
|
||||
outboundSkipPortsStr := make([]string, len(options.ignoreOutboundPorts))
|
||||
for i, p := range options.ignoreOutboundPorts {
|
||||
outboundSkipPortsStr[i] = strconv.Itoa(int(p))
|
||||
}
|
||||
|
||||
initArgs := []string{
|
||||
"--incoming-proxy-port", fmt.Sprintf("%d", inboundPort),
|
||||
"--outgoing-proxy-port", fmt.Sprintf("%d", outboundPort),
|
||||
"--proxy-uid", fmt.Sprintf("%d", proxyUID),
|
||||
"--incoming-proxy-port", fmt.Sprintf("%d", options.inboundPort),
|
||||
"--outgoing-proxy-port", fmt.Sprintf("%d", options.outboundPort),
|
||||
"--proxy-uid", fmt.Sprintf("%d", options.proxyUID),
|
||||
}
|
||||
|
||||
if len(inboundSkipPortsStr) > 0 {
|
||||
|
|
@ -131,8 +149,8 @@ func injectPodTemplateSpec(t *v1.PodTemplateSpec, controlPlaneDNSNameOverride, v
|
|||
|
||||
initContainer := v1.Container{
|
||||
Name: "conduit-init",
|
||||
Image: fmt.Sprintf("%s:%s", initImage, version),
|
||||
ImagePullPolicy: v1.PullPolicy(imagePullPolicy),
|
||||
Image: fmt.Sprintf("%s:%s", options.initImage, options.conduitVersion),
|
||||
ImagePullPolicy: v1.PullPolicy(options.imagePullPolicy),
|
||||
Args: initArgs,
|
||||
SecurityContext: &v1.SecurityContext{
|
||||
Capabilities: &v1.Capabilities{
|
||||
|
|
@ -148,31 +166,31 @@ func injectPodTemplateSpec(t *v1.PodTemplateSpec, controlPlaneDNSNameOverride, v
|
|||
|
||||
sidecar := v1.Container{
|
||||
Name: "conduit-proxy",
|
||||
Image: fmt.Sprintf("%s:%s", proxyImage, version),
|
||||
ImagePullPolicy: v1.PullPolicy(imagePullPolicy),
|
||||
Image: fmt.Sprintf("%s:%s", options.proxyImage, options.conduitVersion),
|
||||
ImagePullPolicy: v1.PullPolicy(options.imagePullPolicy),
|
||||
SecurityContext: &v1.SecurityContext{
|
||||
RunAsUser: &proxyUID,
|
||||
RunAsUser: &options.proxyUID,
|
||||
},
|
||||
Ports: []v1.ContainerPort{
|
||||
{
|
||||
Name: "conduit-proxy",
|
||||
ContainerPort: int32(inboundPort),
|
||||
ContainerPort: int32(options.inboundPort),
|
||||
},
|
||||
{
|
||||
Name: "conduit-metrics",
|
||||
ContainerPort: int32(proxyMetricsPort),
|
||||
ContainerPort: int32(options.proxyMetricsPort),
|
||||
},
|
||||
},
|
||||
Env: []v1.EnvVar{
|
||||
{Name: "CONDUIT_PROXY_LOG", Value: proxyLogLevel},
|
||||
{Name: "CONDUIT_PROXY_LOG", Value: options.proxyLogLevel},
|
||||
{
|
||||
Name: "CONDUIT_PROXY_CONTROL_URL",
|
||||
Value: fmt.Sprintf("tcp://%s:%d", controlPlaneDNS, proxyAPIPort),
|
||||
Value: fmt.Sprintf("tcp://%s:%d", controlPlaneDNS, options.proxyAPIPort),
|
||||
},
|
||||
{Name: "CONDUIT_PROXY_CONTROL_LISTENER", Value: fmt.Sprintf("tcp://0.0.0.0:%d", proxyControlPort)},
|
||||
{Name: "CONDUIT_PROXY_METRICS_LISTENER", Value: fmt.Sprintf("tcp://0.0.0.0:%d", proxyMetricsPort)},
|
||||
{Name: "CONDUIT_PROXY_PRIVATE_LISTENER", Value: fmt.Sprintf("tcp://127.0.0.1:%d", outboundPort)},
|
||||
{Name: "CONDUIT_PROXY_PUBLIC_LISTENER", Value: fmt.Sprintf("tcp://0.0.0.0:%d", inboundPort)},
|
||||
{Name: "CONDUIT_PROXY_CONTROL_LISTENER", Value: fmt.Sprintf("tcp://0.0.0.0:%d", options.proxyControlPort)},
|
||||
{Name: "CONDUIT_PROXY_METRICS_LISTENER", Value: fmt.Sprintf("tcp://0.0.0.0:%d", options.proxyMetricsPort)},
|
||||
{Name: "CONDUIT_PROXY_PRIVATE_LISTENER", Value: fmt.Sprintf("tcp://127.0.0.1:%d", options.outboundPort)},
|
||||
{Name: "CONDUIT_PROXY_PUBLIC_LISTENER", Value: fmt.Sprintf("tcp://0.0.0.0:%d", options.inboundPort)},
|
||||
{
|
||||
Name: "CONDUIT_PROXY_POD_NAMESPACE",
|
||||
ValueFrom: &v1.EnvVarSource{FieldRef: &v1.ObjectFieldSelector{FieldPath: "metadata.namespace"}},
|
||||
|
|
@ -184,7 +202,7 @@ func injectPodTemplateSpec(t *v1.PodTemplateSpec, controlPlaneDNSNameOverride, v
|
|||
t.Annotations = make(map[string]string)
|
||||
}
|
||||
t.Annotations[k8s.CreatedByAnnotation] = k8s.CreatedByAnnotationValue()
|
||||
t.Annotations[k8s.ProxyVersionAnnotation] = version
|
||||
t.Annotations[k8s.ProxyVersionAnnotation] = options.conduitVersion
|
||||
|
||||
if t.Labels == nil {
|
||||
t.Labels = make(map[string]string)
|
||||
|
|
@ -201,7 +219,7 @@ func injectPodTemplateSpec(t *v1.PodTemplateSpec, controlPlaneDNSNameOverride, v
|
|||
}
|
||||
|
||||
// InjectYAML takes an input stream of YAML, outputting injected YAML to out.
|
||||
func InjectYAML(in io.Reader, out io.Writer, version string) error {
|
||||
func InjectYAML(in io.Reader, out io.Writer, options *injectOptions) error {
|
||||
reader := yamlDecoder.NewYAMLReader(bufio.NewReaderSize(in, 4096))
|
||||
|
||||
// Iterate over all YAML objects in the input
|
||||
|
|
@ -215,7 +233,7 @@ func InjectYAML(in io.Reader, out io.Writer, version string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
result, err := injectResource(bytes, version)
|
||||
result, err := injectResource(bytes, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -227,7 +245,7 @@ func InjectYAML(in io.Reader, out io.Writer, version string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func injectList(b []byte, version string) ([]byte, error) {
|
||||
func injectList(b []byte, options *injectOptions) ([]byte, error) {
|
||||
var sourceList v1.List
|
||||
if err := yaml.Unmarshal(b, &sourceList); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -236,7 +254,7 @@ func injectList(b []byte, version string) ([]byte, error) {
|
|||
items := []runtime.RawExtension{}
|
||||
|
||||
for _, item := range sourceList.Items {
|
||||
result, err := injectResource(item.Raw, version)
|
||||
result, err := injectResource(item.Raw, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -256,7 +274,7 @@ func injectList(b []byte, version string) ([]byte, error) {
|
|||
return yaml.Marshal(sourceList)
|
||||
}
|
||||
|
||||
func injectResource(bytes []byte, version string) ([]byte, error) {
|
||||
func injectResource(bytes []byte, options *injectOptions) ([]byte, error) {
|
||||
// The Kuberentes API is versioned and each version has an API modeled
|
||||
// with its own distinct Go types. If we tell `yaml.Unmarshal()` which
|
||||
// version we support then it will provide a representation of that
|
||||
|
|
@ -356,14 +374,14 @@ func injectResource(bytes []byte, version string) ([]byte, error) {
|
|||
// Lists are a little different than the other types. There's no immediate
|
||||
// pod template. Because of this, we do a recursive call for each element
|
||||
// in the list (instead of just marshaling the injected pod template).
|
||||
return injectList(bytes, version)
|
||||
return injectList(bytes, options)
|
||||
}
|
||||
|
||||
// If we don't inject anything into the pod template then output the
|
||||
// original serialization of the original object. Otherwise, output the
|
||||
// serialization of the modified object.
|
||||
output := bytes
|
||||
if podTemplateSpec != nil && injectPodTemplateSpec(podTemplateSpec, DNSNameOverride, version, k8sLabels) {
|
||||
if podTemplateSpec != nil && injectPodTemplateSpec(podTemplateSpec, DNSNameOverride, k8sLabels, options) {
|
||||
var err error
|
||||
output, err = yaml.Marshal(obj)
|
||||
if err != nil {
|
||||
|
|
@ -373,24 +391,3 @@ func injectResource(bytes []byte, version string) ([]byte, error) {
|
|||
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(injectCmd)
|
||||
addProxyConfigFlags(injectCmd)
|
||||
injectCmd.PersistentFlags().StringVar(&initImage, "init-image", "gcr.io/runconduit/proxy-init", "Conduit init container image name")
|
||||
injectCmd.PersistentFlags().UintVar(&inboundPort, "inbound-port", 4143, "Proxy port to use for inbound traffic")
|
||||
injectCmd.PersistentFlags().UintVar(&outboundPort, "outbound-port", 4140, "Proxy port to use for outbound traffic")
|
||||
injectCmd.PersistentFlags().UintSliceVar(&ignoreInboundPorts, "skip-inbound-ports", nil, "Ports that should skip the proxy and send directly to the application")
|
||||
injectCmd.PersistentFlags().UintSliceVar(&ignoreOutboundPorts, "skip-outbound-ports", nil, "Outbound ports that should skip the proxy")
|
||||
}
|
||||
|
||||
func addProxyConfigFlags(cmd *cobra.Command) {
|
||||
cmd.PersistentFlags().StringVarP(&conduitVersion, "conduit-version", "v", version.Version, "Tag to be used for Conduit images")
|
||||
cmd.PersistentFlags().StringVar(&proxyImage, "proxy-image", "gcr.io/runconduit/proxy", "Conduit proxy container image name")
|
||||
cmd.PersistentFlags().StringVar(&imagePullPolicy, "image-pull-policy", "IfNotPresent", "Docker image pull policy")
|
||||
cmd.PersistentFlags().Int64Var(&proxyUID, "proxy-uid", 2102, "Run the proxy under this user ID")
|
||||
cmd.PersistentFlags().StringVar(&proxyLogLevel, "proxy-log-level", "warn,conduit_proxy=info", "Log level for the proxy")
|
||||
cmd.PersistentFlags().UintVar(&proxyAPIPort, "api-port", 8086, "Port where the Conduit controller is running")
|
||||
cmd.PersistentFlags().UintVar(&proxyControlPort, "control-port", 4190, "Proxy port to use for control")
|
||||
cmd.PersistentFlags().UintVar(&proxyMetricsPort, "metrics-port", 4191, "Proxy port to serve metrics on")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ import (
|
|||
)
|
||||
|
||||
func TestInjectYAML(t *testing.T) {
|
||||
testInjectVersion := "testinjectversion"
|
||||
testInjectOptions := newInjectOptions()
|
||||
testInjectOptions.conduitVersion = "testinjectversion"
|
||||
testCases := []struct {
|
||||
inputFileName string
|
||||
goldenFileName string
|
||||
|
|
@ -34,7 +35,7 @@ func TestInjectYAML(t *testing.T) {
|
|||
|
||||
output := new(bytes.Buffer)
|
||||
|
||||
err = InjectYAML(read, output, testInjectVersion)
|
||||
err = InjectYAML(read, output, testInjectOptions)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error injecting YAML: %v\n", err)
|
||||
}
|
||||
|
|
@ -52,7 +53,8 @@ func TestInjectYAML(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRunInjectCmd(t *testing.T) {
|
||||
testInjectVersion := "testinjectversion"
|
||||
testInjectOptions := newInjectOptions()
|
||||
testInjectOptions.conduitVersion = "testinjectversion"
|
||||
testCases := []struct {
|
||||
inputFileName string
|
||||
stdErrGoldenFileName string
|
||||
|
|
@ -81,7 +83,7 @@ func TestRunInjectCmd(t *testing.T) {
|
|||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
exitCode := runInjectCmd(in, errBuffer, outBuffer, testInjectVersion)
|
||||
exitCode := runInjectCmd(in, errBuffer, outBuffer, testInjectOptions)
|
||||
if exitCode != tc.exitCode {
|
||||
t.Fatalf("Expected exit code to be %d but got: %d", tc.exitCode, exitCode)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,52 +32,75 @@ type installConfig struct {
|
|||
CreatedByAnnotation string
|
||||
}
|
||||
|
||||
var (
|
||||
conduitVersion string
|
||||
type installOptions struct {
|
||||
dockerRegistry string
|
||||
controllerReplicas uint
|
||||
webReplicas uint
|
||||
prometheusReplicas uint
|
||||
imagePullPolicy string
|
||||
controllerLogLevel string
|
||||
)
|
||||
|
||||
var installCmd = &cobra.Command{
|
||||
Use: "install [flags]",
|
||||
Short: "Output Kubernetes configs to install Conduit",
|
||||
Long: "Output Kubernetes configs to install Conduit.",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
config, err := validateAndBuildConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return render(*config, os.Stdout)
|
||||
},
|
||||
*proxyConfigOptions
|
||||
}
|
||||
|
||||
func validateAndBuildConfig() (*installConfig, error) {
|
||||
if err := validate(); err != nil {
|
||||
func newInstallOptions() *installOptions {
|
||||
return &installOptions{
|
||||
dockerRegistry: "gcr.io/runconduit",
|
||||
controllerReplicas: 1,
|
||||
webReplicas: 1,
|
||||
prometheusReplicas: 1,
|
||||
controllerLogLevel: "info",
|
||||
proxyConfigOptions: newProxyConfigOptions(),
|
||||
}
|
||||
}
|
||||
|
||||
func newCmdInstall() *cobra.Command {
|
||||
options := newInstallOptions()
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "install [flags]",
|
||||
Short: "Output Kubernetes configs to install Conduit",
|
||||
Long: "Output Kubernetes configs to install Conduit.",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
config, err := validateAndBuildConfig(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return render(*config, os.Stdout, options)
|
||||
},
|
||||
}
|
||||
|
||||
addProxyConfigFlags(cmd, options.proxyConfigOptions)
|
||||
cmd.PersistentFlags().StringVar(&options.dockerRegistry, "registry", options.dockerRegistry, "Docker registry to pull images from")
|
||||
cmd.PersistentFlags().UintVar(&options.controllerReplicas, "controller-replicas", options.controllerReplicas, "Replicas of the controller to deploy")
|
||||
cmd.PersistentFlags().UintVar(&options.webReplicas, "web-replicas", options.webReplicas, "Replicas of the web server to deploy")
|
||||
cmd.PersistentFlags().UintVar(&options.prometheusReplicas, "prometheus-replicas", options.prometheusReplicas, "Replicas of prometheus to deploy")
|
||||
cmd.PersistentFlags().StringVar(&options.controllerLogLevel, "controller-log-level", options.controllerLogLevel, "Log level for the controller and web components")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func validateAndBuildConfig(options *installOptions) (*installConfig, error) {
|
||||
if err := validate(options); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &installConfig{
|
||||
Namespace: controlPlaneNamespace,
|
||||
ControllerImage: fmt.Sprintf("%s/controller:%s", dockerRegistry, conduitVersion),
|
||||
WebImage: fmt.Sprintf("%s/web:%s", dockerRegistry, conduitVersion),
|
||||
ControllerImage: fmt.Sprintf("%s/controller:%s", options.dockerRegistry, options.conduitVersion),
|
||||
WebImage: fmt.Sprintf("%s/web:%s", options.dockerRegistry, options.conduitVersion),
|
||||
PrometheusImage: "prom/prometheus:v2.2.1",
|
||||
GrafanaImage: fmt.Sprintf("%s/grafana:%s", dockerRegistry, conduitVersion),
|
||||
ControllerReplicas: controllerReplicas,
|
||||
WebReplicas: webReplicas,
|
||||
PrometheusReplicas: prometheusReplicas,
|
||||
ImagePullPolicy: imagePullPolicy,
|
||||
GrafanaImage: fmt.Sprintf("%s/grafana:%s", options.dockerRegistry, options.conduitVersion),
|
||||
ControllerReplicas: options.controllerReplicas,
|
||||
WebReplicas: options.webReplicas,
|
||||
PrometheusReplicas: options.prometheusReplicas,
|
||||
ImagePullPolicy: options.imagePullPolicy,
|
||||
UUID: uuid.NewV4().String(),
|
||||
CliVersion: k8s.CreatedByAnnotationValue(),
|
||||
ControllerLogLevel: controllerLogLevel,
|
||||
ControllerLogLevel: options.controllerLogLevel,
|
||||
ControllerComponentLabel: k8s.ControllerComponentLabel,
|
||||
CreatedByAnnotation: k8s.CreatedByAnnotation,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func render(config installConfig, w io.Writer) error {
|
||||
func render(config installConfig, w io.Writer, options *installOptions) error {
|
||||
template, err := template.New("conduit").Parse(install.Template)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -87,40 +110,32 @@ func render(config installConfig, w io.Writer) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return InjectYAML(buf, w, conduitVersion)
|
||||
injectOptions := newInjectOptions()
|
||||
injectOptions.proxyConfigOptions = options.proxyConfigOptions
|
||||
return InjectYAML(buf, w, injectOptions)
|
||||
}
|
||||
|
||||
var alphaNumDash = regexp.MustCompile("^[a-zA-Z0-9-]+$")
|
||||
var alphaNumDashDot = regexp.MustCompile("^[\\.a-zA-Z0-9-]+$")
|
||||
var alphaNumDashDotSlash = regexp.MustCompile("^[\\./a-zA-Z0-9-]+$")
|
||||
|
||||
func validate() error {
|
||||
func validate(options *installOptions) error {
|
||||
// These regexs are not as strict as they could be, but are a quick and dirty
|
||||
// sanity check against illegal characters.
|
||||
if !alphaNumDash.MatchString(controlPlaneNamespace) {
|
||||
return fmt.Errorf("%s is not a valid namespace", controlPlaneNamespace)
|
||||
}
|
||||
if !alphaNumDashDot.MatchString(conduitVersion) {
|
||||
return fmt.Errorf("%s is not a valid version", conduitVersion)
|
||||
if !alphaNumDashDot.MatchString(options.conduitVersion) {
|
||||
return fmt.Errorf("%s is not a valid version", options.conduitVersion)
|
||||
}
|
||||
if !alphaNumDashDotSlash.MatchString(dockerRegistry) {
|
||||
return fmt.Errorf("%s is not a valid Docker registry", dockerRegistry)
|
||||
if !alphaNumDashDotSlash.MatchString(options.dockerRegistry) {
|
||||
return fmt.Errorf("%s is not a valid Docker registry", options.dockerRegistry)
|
||||
}
|
||||
if imagePullPolicy != "Always" && imagePullPolicy != "IfNotPresent" && imagePullPolicy != "Never" {
|
||||
if options.imagePullPolicy != "Always" && options.imagePullPolicy != "IfNotPresent" && options.imagePullPolicy != "Never" {
|
||||
return fmt.Errorf("--image-pull-policy must be one of: Always, IfNotPresent, Never")
|
||||
}
|
||||
if _, err := log.ParseLevel(controllerLogLevel); err != nil {
|
||||
if _, err := log.ParseLevel(options.controllerLogLevel); err != nil {
|
||||
return fmt.Errorf("--controller-log-level must be one of: panic, fatal, error, warn, info, debug")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(installCmd)
|
||||
addProxyConfigFlags(installCmd)
|
||||
installCmd.PersistentFlags().StringVar(&dockerRegistry, "registry", "gcr.io/runconduit", "Docker registry to pull images from")
|
||||
installCmd.PersistentFlags().UintVar(&controllerReplicas, "controller-replicas", 1, "Replicas of the controller to deploy")
|
||||
installCmd.PersistentFlags().UintVar(&webReplicas, "web-replicas", 1, "Replicas of the web server to deploy")
|
||||
installCmd.PersistentFlags().UintVar(&prometheusReplicas, "prometheus-replicas", 1, "Replicas of prometheus to deploy")
|
||||
installCmd.PersistentFlags().StringVar(&controllerLogLevel, "controller-log-level", "info", "Log level for the controller and web components")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package cmd
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
|
@ -12,7 +11,8 @@ func TestRender(t *testing.T) {
|
|||
// The default configuration, with the random UUID overridden with a fixed
|
||||
// value to facilitate testing.
|
||||
defaultControlPlaneNamespace := controlPlaneNamespace
|
||||
defaultConfig, err := validateAndBuildConfig()
|
||||
defaultOptions := newInstallOptions()
|
||||
defaultConfig, err := validateAndBuildConfig(defaultOptions)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error from validateAndBuildConfig(): %v", err)
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ func TestRender(t *testing.T) {
|
|||
controlPlaneNamespace = tc.controlPlaneNamespace
|
||||
|
||||
var buf bytes.Buffer
|
||||
err := render(tc.config, &buf)
|
||||
err := render(tc.config, &buf, defaultOptions)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"github.com/runconduit/conduit/controller/api/public"
|
||||
pb "github.com/runconduit/conduit/controller/gen/public"
|
||||
"github.com/runconduit/conduit/pkg/k8s"
|
||||
"github.com/runconduit/conduit/pkg/version"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
|
@ -32,6 +33,16 @@ func init() {
|
|||
RootCmd.PersistentFlags().StringVar(&kubeconfigPath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests")
|
||||
RootCmd.PersistentFlags().StringVar(&apiAddr, "api-addr", "", "Override kubeconfig and communicate directly with the control plane at host:port (mostly for testing)")
|
||||
RootCmd.PersistentFlags().BoolVar(&verbose, "verbose", false, "Turn on debug logging")
|
||||
|
||||
RootCmd.AddCommand(newCmdCheck())
|
||||
RootCmd.AddCommand(newCmdCompletion())
|
||||
RootCmd.AddCommand(newCmdDashboard())
|
||||
RootCmd.AddCommand(newCmdGet())
|
||||
RootCmd.AddCommand(newCmdInject())
|
||||
RootCmd.AddCommand(newCmdInstall())
|
||||
RootCmd.AddCommand(newCmdStat())
|
||||
RootCmd.AddCommand(newCmdTap())
|
||||
RootCmd.AddCommand(newCmdVersion())
|
||||
}
|
||||
|
||||
func newPublicAPIClient() (pb.ApiClient, error) {
|
||||
|
|
@ -44,3 +55,38 @@ func newPublicAPIClient() (pb.ApiClient, error) {
|
|||
}
|
||||
return public.NewExternalClient(controlPlaneNamespace, kubeAPI)
|
||||
}
|
||||
|
||||
type proxyConfigOptions struct {
|
||||
conduitVersion string
|
||||
proxyImage string
|
||||
imagePullPolicy string
|
||||
proxyUID int64
|
||||
proxyLogLevel string
|
||||
proxyAPIPort uint
|
||||
proxyControlPort uint
|
||||
proxyMetricsPort uint
|
||||
}
|
||||
|
||||
func newProxyConfigOptions() *proxyConfigOptions {
|
||||
return &proxyConfigOptions{
|
||||
conduitVersion: version.Version,
|
||||
proxyImage: "gcr.io/runconduit/proxy",
|
||||
imagePullPolicy: "IfNotPresent",
|
||||
proxyUID: 2102,
|
||||
proxyLogLevel: "warn,conduit_proxy=info",
|
||||
proxyAPIPort: 8086,
|
||||
proxyControlPort: 4190,
|
||||
proxyMetricsPort: 4191,
|
||||
}
|
||||
}
|
||||
|
||||
func addProxyConfigFlags(cmd *cobra.Command, options *proxyConfigOptions) {
|
||||
cmd.PersistentFlags().StringVarP(&options.conduitVersion, "conduit-version", "v", options.conduitVersion, "Tag to be used for Conduit images")
|
||||
cmd.PersistentFlags().StringVar(&options.proxyImage, "proxy-image", options.proxyImage, "Conduit proxy container image name")
|
||||
cmd.PersistentFlags().StringVar(&options.imagePullPolicy, "image-pull-policy", options.imagePullPolicy, "Docker image pull policy")
|
||||
cmd.PersistentFlags().Int64Var(&options.proxyUID, "proxy-uid", options.proxyUID, "Run the proxy under this user ID")
|
||||
cmd.PersistentFlags().StringVar(&options.proxyLogLevel, "proxy-log-level", options.proxyLogLevel, "Log level for the proxy")
|
||||
cmd.PersistentFlags().UintVar(&options.proxyAPIPort, "api-port", options.proxyAPIPort, "Port where the Conduit controller is running")
|
||||
cmd.PersistentFlags().UintVar(&options.proxyControlPort, "control-port", options.proxyControlPort, "Proxy port to use for control")
|
||||
cmd.PersistentFlags().UintVar(&options.proxyMetricsPort, "metrics-port", options.proxyMetricsPort, "Proxy port to serve metrics on")
|
||||
}
|
||||
|
|
|
|||
149
cli/cmd/stat.go
149
cli/cmd/stat.go
|
|
@ -18,18 +18,35 @@ import (
|
|||
"k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
var (
|
||||
timeWindow string
|
||||
namespace string
|
||||
toNamespace, toResource string
|
||||
fromNamespace, fromResource string
|
||||
allNamespaces bool
|
||||
)
|
||||
type statOptions struct {
|
||||
namespace string
|
||||
timeWindow string
|
||||
toNamespace string
|
||||
toResource string
|
||||
fromNamespace string
|
||||
fromResource string
|
||||
allNamespaces bool
|
||||
}
|
||||
|
||||
var statCmd = &cobra.Command{
|
||||
Use: "stat [flags] (RESOURCE)",
|
||||
Short: "Display traffic stats about one or many resources",
|
||||
Long: `Display traffic stats about one or many resources.
|
||||
func newStatOptions() *statOptions {
|
||||
return &statOptions{
|
||||
namespace: "default",
|
||||
timeWindow: "1m",
|
||||
toNamespace: "",
|
||||
toResource: "",
|
||||
fromNamespace: "",
|
||||
fromResource: "",
|
||||
allNamespaces: false,
|
||||
}
|
||||
}
|
||||
|
||||
func newCmdStat() *cobra.Command {
|
||||
options := newStatOptions()
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "stat [flags] (RESOURCE)",
|
||||
Short: "Display traffic stats about one or many resources",
|
||||
Long: `Display traffic stats about one or many resources.
|
||||
|
||||
The RESOURCE argument specifies the target resource(s) to aggregate stats over:
|
||||
(TYPE [NAME] | TYPE/NAME)
|
||||
|
|
@ -52,7 +69,7 @@ Valid resource types include:
|
|||
|
||||
This command will hide resources that have completed, such as pods that are in the Succeeded or Failed phases.
|
||||
If no resource name is specified, displays stats about all resources of the specified RESOURCETYPE`,
|
||||
Example: ` # Get all deployments in the test namespace.
|
||||
Example: ` # Get all deployments in the test namespace.
|
||||
conduit stat deployments -n test
|
||||
|
||||
# Get the hello1 replication controller in the test namespace.
|
||||
|
|
@ -72,47 +89,42 @@ If no resource name is specified, displays stats about all resources of the spec
|
|||
|
||||
# Get all services in all namespaces that receive calls from hello1 deployment in the test namesapce.
|
||||
conduit stat services --from deploy/hello1 --from-namespace test --all-namespaces`,
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
ValidArgs: util.ValidTargets,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := newPublicAPIClient()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating api client while making stats request: %v", err)
|
||||
}
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
ValidArgs: util.ValidTargets,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
client, err := newPublicAPIClient()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating api client while making stats request: %v", err)
|
||||
}
|
||||
|
||||
req, err := buildStatSummaryRequest(
|
||||
timeWindow, allNamespaces,
|
||||
args, namespace,
|
||||
toResource, toNamespace,
|
||||
fromResource, fromNamespace,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating metrics request while making stats request: %v", err)
|
||||
}
|
||||
req, err := buildStatSummaryRequest(args, options)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating metrics request while making stats request: %v", err)
|
||||
}
|
||||
|
||||
output, err := requestStatsFromAPI(client, req, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = fmt.Print(output)
|
||||
|
||||
output, err := requestStatsFromAPI(client, req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_, err = fmt.Print(output)
|
||||
cmd.PersistentFlags().StringVarP(&options.namespace, "namespace", "n", options.namespace, "Namespace of the specified resource")
|
||||
cmd.PersistentFlags().StringVarP(&options.timeWindow, "time-window", "t", options.timeWindow, "Stat window (for example: \"10s\", \"1m\", \"10m\", \"1h\")")
|
||||
cmd.PersistentFlags().StringVar(&options.toResource, "to", options.toResource, "If present, restricts outbound stats to the specified resource name")
|
||||
cmd.PersistentFlags().StringVar(&options.toNamespace, "to-namespace", options.toNamespace, "Sets the namespace used to lookup the \"--to\" resource; by default the current \"--namespace\" is used")
|
||||
cmd.PersistentFlags().StringVar(&options.fromResource, "from", options.fromResource, "If present, restricts outbound stats from the specified resource name")
|
||||
cmd.PersistentFlags().StringVar(&options.fromNamespace, "from-namespace", options.fromNamespace, "Sets the namespace used from lookup the \"--from\" resource; by default the current \"--namespace\" is used")
|
||||
cmd.PersistentFlags().BoolVar(&options.allNamespaces, "all-namespaces", options.allNamespaces, "If present, returns stats across all namespaces, ignoring the \"--namespace\" flag")
|
||||
|
||||
return err
|
||||
},
|
||||
return cmd
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(statCmd)
|
||||
statCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", "default", "Namespace of the specified resource")
|
||||
statCmd.PersistentFlags().StringVarP(&timeWindow, "time-window", "t", "1m", "Stat window (for example: \"10s\", \"1m\", \"10m\", \"1h\")")
|
||||
statCmd.PersistentFlags().StringVar(&toResource, "to", "", "If present, restricts outbound stats to the specified resource name")
|
||||
statCmd.PersistentFlags().StringVar(&toNamespace, "to-namespace", "", "Sets the namespace used to lookup the \"--to\" resource; by default the current \"--namespace\" is used")
|
||||
statCmd.PersistentFlags().StringVar(&fromResource, "from", "", "If present, restricts outbound stats from the specified resource name")
|
||||
statCmd.PersistentFlags().StringVar(&fromNamespace, "from-namespace", "", "Sets the namespace used from lookup the \"--from\" resource; by default the current \"--namespace\" is used")
|
||||
statCmd.PersistentFlags().BoolVar(&allNamespaces, "all-namespaces", false, "If present, returns stats across all namespaces, ignoring the \"--namespace\" flag")
|
||||
}
|
||||
|
||||
func requestStatsFromAPI(client pb.ApiClient, req *pb.StatSummaryRequest) (string, error) {
|
||||
func requestStatsFromAPI(client pb.ApiClient, req *pb.StatSummaryRequest, options *statOptions) (string, error) {
|
||||
resp, err := client.StatSummary(context.Background(), req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("StatSummary API error: %v", err)
|
||||
|
|
@ -121,13 +133,13 @@ func requestStatsFromAPI(client pb.ApiClient, req *pb.StatSummaryRequest) (strin
|
|||
return "", fmt.Errorf("StatSummary API response error: %v", e.Error)
|
||||
}
|
||||
|
||||
return renderStats(resp, req.Selector.Resource.Type), nil
|
||||
return renderStats(resp, req.Selector.Resource.Type, options), nil
|
||||
}
|
||||
|
||||
func renderStats(resp *pb.StatSummaryResponse, resourceType string) string {
|
||||
func renderStats(resp *pb.StatSummaryResponse, resourceType string, options *statOptions) string {
|
||||
var buffer bytes.Buffer
|
||||
w := tabwriter.NewWriter(&buffer, 0, 0, padding, ' ', tabwriter.AlignRight)
|
||||
writeStatsToBuffer(resp, resourceType, w)
|
||||
writeStatsToBuffer(resp, resourceType, w, options)
|
||||
w.Flush()
|
||||
|
||||
// strip left padding on the first column
|
||||
|
|
@ -157,7 +169,7 @@ var (
|
|||
namespaceHeader = "NAMESPACE"
|
||||
)
|
||||
|
||||
func writeStatsToBuffer(resp *pb.StatSummaryResponse, reqResourceType string, w *tabwriter.Writer) {
|
||||
func writeStatsToBuffer(resp *pb.StatSummaryResponse, reqResourceType string, w *tabwriter.Writer, options *statOptions) {
|
||||
maxNameLength := len(nameHeader)
|
||||
maxNamespaceLength := len(namespaceHeader)
|
||||
statTables := make(map[string]map[string]*row)
|
||||
|
|
@ -216,16 +228,16 @@ func writeStatsToBuffer(resp *pb.StatSummaryResponse, reqResourceType string, w
|
|||
}
|
||||
lastDisplayedStat = false
|
||||
if reqResourceType == k8s.All {
|
||||
printStatTable(stats, resourceType, w, maxNameLength, maxNamespaceLength)
|
||||
printStatTable(stats, resourceType, w, maxNameLength, maxNamespaceLength, options)
|
||||
} else {
|
||||
printStatTable(stats, "", w, maxNameLength, maxNamespaceLength)
|
||||
printStatTable(stats, "", w, maxNameLength, maxNamespaceLength, options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func printStatTable(stats map[string]*row, resourceType string, w *tabwriter.Writer, maxNameLength int, maxNamespaceLength int) {
|
||||
func printStatTable(stats map[string]*row, resourceType string, w *tabwriter.Writer, maxNameLength int, maxNamespaceLength int, options *statOptions) {
|
||||
headers := make([]string, 0)
|
||||
if allNamespaces {
|
||||
if options.allNamespaces {
|
||||
headers = append(headers,
|
||||
namespaceHeader+strings.Repeat(" ", maxNamespaceLength-len(namespaceHeader)))
|
||||
}
|
||||
|
|
@ -252,7 +264,7 @@ func printStatTable(stats map[string]*row, resourceType string, w *tabwriter.Wri
|
|||
templateString := "%s\t%s\t%.2f%%\t%.1frps\t%dms\t%dms\t%dms\t\n"
|
||||
templateStringEmpty := "%s\t%s\t-\t-\t-\t-\t-\t\n"
|
||||
|
||||
if allNamespaces {
|
||||
if options.allNamespaces {
|
||||
values = append(values,
|
||||
namespace+strings.Repeat(" ", maxNamespaceLength-len(namespace)))
|
||||
templateString = "%s\t" + templateString
|
||||
|
|
@ -287,16 +299,11 @@ func getNamePrefix(resourceType string) string {
|
|||
}
|
||||
}
|
||||
|
||||
func buildStatSummaryRequest(
|
||||
timeWindow string, allNamespaces bool,
|
||||
resource []string, namespace string,
|
||||
toResource, toNamespace string,
|
||||
fromResource, fromNamespace string,
|
||||
) (*pb.StatSummaryRequest, error) {
|
||||
targetNamespace := namespace
|
||||
if allNamespaces {
|
||||
func buildStatSummaryRequest(resource []string, options *statOptions) (*pb.StatSummaryRequest, error) {
|
||||
targetNamespace := options.namespace
|
||||
if options.allNamespaces {
|
||||
targetNamespace = ""
|
||||
} else if namespace == "" {
|
||||
} else if options.namespace == "" {
|
||||
targetNamespace = v1.NamespaceDefault
|
||||
}
|
||||
|
||||
|
|
@ -306,30 +313,30 @@ func buildStatSummaryRequest(
|
|||
}
|
||||
|
||||
var toRes, fromRes pb.Resource
|
||||
if toResource != "" {
|
||||
toRes, err = util.BuildResource(toNamespace, toResource)
|
||||
if options.toResource != "" {
|
||||
toRes, err = util.BuildResource(options.toNamespace, options.toResource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if fromResource != "" {
|
||||
fromRes, err = util.BuildResource(fromNamespace, fromResource)
|
||||
if options.fromResource != "" {
|
||||
fromRes, err = util.BuildResource(options.fromNamespace, options.fromResource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
requestParams := util.StatSummaryRequestParams{
|
||||
TimeWindow: timeWindow,
|
||||
TimeWindow: options.timeWindow,
|
||||
ResourceName: target.Name,
|
||||
ResourceType: target.Type,
|
||||
Namespace: targetNamespace,
|
||||
ToName: toRes.Name,
|
||||
ToType: toRes.Type,
|
||||
ToNamespace: toNamespace,
|
||||
ToNamespace: options.toNamespace,
|
||||
FromName: fromRes.Name,
|
||||
FromType: fromRes.Type,
|
||||
FromNamespace: fromNamespace,
|
||||
FromNamespace: options.fromNamespace,
|
||||
}
|
||||
|
||||
return util.BuildStatSummaryRequest(requestParams)
|
||||
|
|
|
|||
126
cli/cmd/tap.go
126
cli/cmd/tap.go
|
|
@ -16,18 +16,37 @@ import (
|
|||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
var (
|
||||
maxRps float32
|
||||
scheme string
|
||||
method string
|
||||
authority string
|
||||
path string
|
||||
)
|
||||
type tapOptions struct {
|
||||
namespace string
|
||||
toResource string
|
||||
toNamespace string
|
||||
maxRps float32
|
||||
scheme string
|
||||
method string
|
||||
authority string
|
||||
path string
|
||||
}
|
||||
|
||||
var tapCmd = &cobra.Command{
|
||||
Use: "tap [flags] (RESOURCE)",
|
||||
Short: "Listen to a traffic stream",
|
||||
Long: `Listen to a traffic stream.
|
||||
func newTapOptions() *tapOptions {
|
||||
return &tapOptions{
|
||||
namespace: "default",
|
||||
toResource: "",
|
||||
toNamespace: "",
|
||||
maxRps: 1.0,
|
||||
scheme: "",
|
||||
method: "",
|
||||
authority: "",
|
||||
path: "",
|
||||
}
|
||||
}
|
||||
|
||||
func newCmdTap() *cobra.Command {
|
||||
options := newTapOptions()
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "tap [flags] (RESOURCE)",
|
||||
Short: "Listen to a traffic stream",
|
||||
Long: `Listen to a traffic stream.
|
||||
|
||||
The RESOURCE argument specifies the target resource(s) to tap:
|
||||
(TYPE [NAME] | TYPE/NAME)
|
||||
|
|
@ -45,7 +64,7 @@ var tapCmd = &cobra.Command{
|
|||
* pods
|
||||
* replicationcontrollers
|
||||
* services (only supported as a "--to" resource)`,
|
||||
Example: ` # tap the web deployment in the default namespace
|
||||
Example: ` # tap the web deployment in the default namespace
|
||||
conduit tap deploy/web
|
||||
|
||||
# tap the web-dlbvj pod in the default namespace
|
||||
|
|
@ -53,56 +72,49 @@ var tapCmd = &cobra.Command{
|
|||
|
||||
# tap the test namespace, filter by request to prod namespace
|
||||
conduit tap ns/test --to ns/prod`,
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
ValidArgs: apiUtil.ValidTargets,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
req, err := buildTapByResourceRequest(
|
||||
args, namespace,
|
||||
toResource, toNamespace,
|
||||
maxRps,
|
||||
scheme, method, authority, path,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
Args: cobra.RangeArgs(1, 2),
|
||||
ValidArgs: apiUtil.ValidTargets,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
req, err := buildTapByResourceRequest(args, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := newPublicAPIClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := newPublicAPIClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return requestTapByResourceFromAPI(os.Stdout, client, req)
|
||||
},
|
||||
}
|
||||
return requestTapByResourceFromAPI(os.Stdout, client, req)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(tapCmd)
|
||||
tapCmd.PersistentFlags().StringVarP(&namespace, "namespace", "n", "default",
|
||||
cmd.PersistentFlags().StringVarP(&options.namespace, "namespace", "n", options.namespace,
|
||||
"Namespace of the specified resource")
|
||||
tapCmd.PersistentFlags().StringVar(&toResource, "to", "",
|
||||
cmd.PersistentFlags().StringVar(&options.toResource, "to", options.toResource,
|
||||
"Display requests to this resource")
|
||||
tapCmd.PersistentFlags().StringVar(&toNamespace, "to-namespace", "",
|
||||
cmd.PersistentFlags().StringVar(&options.toNamespace, "to-namespace", options.toNamespace,
|
||||
"Sets the namespace used to lookup the \"--to\" resource; by default the current \"--namespace\" is used")
|
||||
tapCmd.PersistentFlags().Float32Var(&maxRps, "max-rps", 1.0,
|
||||
cmd.PersistentFlags().Float32Var(&options.maxRps, "max-rps", options.maxRps,
|
||||
"Maximum requests per second to tap.")
|
||||
tapCmd.PersistentFlags().StringVar(&scheme, "scheme", "",
|
||||
cmd.PersistentFlags().StringVar(&options.scheme, "scheme", options.scheme,
|
||||
"Display requests with this scheme")
|
||||
tapCmd.PersistentFlags().StringVar(&method, "method", "",
|
||||
cmd.PersistentFlags().StringVar(&options.method, "method", options.method,
|
||||
"Display requests with this HTTP method")
|
||||
tapCmd.PersistentFlags().StringVar(&authority, "authority", "",
|
||||
cmd.PersistentFlags().StringVar(&options.authority, "authority", options.authority,
|
||||
"Display requests with this :authority")
|
||||
tapCmd.PersistentFlags().StringVar(&path, "path", "",
|
||||
cmd.PersistentFlags().StringVar(&options.path, "path", options.path,
|
||||
"Display requests with paths that start with this prefix")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func buildTapByResourceRequest(
|
||||
resource []string, namespace string,
|
||||
toResource, toNamespace string,
|
||||
maxRps float32,
|
||||
scheme, method, authority, path string,
|
||||
resource []string,
|
||||
options *tapOptions,
|
||||
) (*pb.TapByResourceRequest, error) {
|
||||
|
||||
target, err := apiUtil.BuildResource(namespace, resource...)
|
||||
target, err := apiUtil.BuildResource(options.namespace, resource...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("target resource invalid: %s", err)
|
||||
}
|
||||
|
|
@ -112,8 +124,8 @@ func buildTapByResourceRequest(
|
|||
|
||||
matches := []*pb.TapByResourceRequest_Match{}
|
||||
|
||||
if toResource != "" {
|
||||
destination, err := apiUtil.BuildResource(toNamespace, toResource)
|
||||
if options.toResource != "" {
|
||||
destination, err := apiUtil.BuildResource(options.toNamespace, options.toResource)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("destination resource invalid: %s", err)
|
||||
}
|
||||
|
|
@ -131,27 +143,27 @@ func buildTapByResourceRequest(
|
|||
matches = append(matches, &match)
|
||||
}
|
||||
|
||||
if scheme != "" {
|
||||
if options.scheme != "" {
|
||||
match := buildMatchHTTP(&pb.TapByResourceRequest_Match_Http{
|
||||
Match: &pb.TapByResourceRequest_Match_Http_Scheme{Scheme: scheme},
|
||||
Match: &pb.TapByResourceRequest_Match_Http_Scheme{Scheme: options.scheme},
|
||||
})
|
||||
matches = append(matches, &match)
|
||||
}
|
||||
if method != "" {
|
||||
if options.method != "" {
|
||||
match := buildMatchHTTP(&pb.TapByResourceRequest_Match_Http{
|
||||
Match: &pb.TapByResourceRequest_Match_Http_Method{Method: method},
|
||||
Match: &pb.TapByResourceRequest_Match_Http_Method{Method: options.method},
|
||||
})
|
||||
matches = append(matches, &match)
|
||||
}
|
||||
if authority != "" {
|
||||
if options.authority != "" {
|
||||
match := buildMatchHTTP(&pb.TapByResourceRequest_Match_Http{
|
||||
Match: &pb.TapByResourceRequest_Match_Http_Authority{Authority: authority},
|
||||
Match: &pb.TapByResourceRequest_Match_Http_Authority{Authority: options.authority},
|
||||
})
|
||||
matches = append(matches, &match)
|
||||
}
|
||||
if path != "" {
|
||||
if options.path != "" {
|
||||
match := buildMatchHTTP(&pb.TapByResourceRequest_Match_Http{
|
||||
Match: &pb.TapByResourceRequest_Match_Http_Path{Path: path},
|
||||
Match: &pb.TapByResourceRequest_Match_Http_Path{Path: options.path},
|
||||
})
|
||||
matches = append(matches, &match)
|
||||
}
|
||||
|
|
@ -160,7 +172,7 @@ func buildTapByResourceRequest(
|
|||
Target: &pb.ResourceSelection{
|
||||
Resource: &target,
|
||||
},
|
||||
MaxRps: maxRps,
|
||||
MaxRps: options.maxRps,
|
||||
Match: &pb.TapByResourceRequest_Match{
|
||||
Match: &pb.TapByResourceRequest_Match_All{
|
||||
All: &pb.TapByResourceRequest_Match_Seq{
|
||||
|
|
|
|||
|
|
@ -19,15 +19,16 @@ func TestRequestTapByResourceFromAPI(t *testing.T) {
|
|||
t.Run("Should render busy response if everything went well", func(t *testing.T) {
|
||||
resourceType := k8s.Pods
|
||||
targetName := "pod-666"
|
||||
scheme := "https"
|
||||
method := "GET"
|
||||
authority := "localhost"
|
||||
path := "/some/path"
|
||||
options := &tapOptions{
|
||||
scheme: "https",
|
||||
method: "GET",
|
||||
authority: "localhost",
|
||||
path: "/some/path",
|
||||
}
|
||||
|
||||
req, err := buildTapByResourceRequest(
|
||||
[]string{resourceType, targetName},
|
||||
"", "", "", 0,
|
||||
scheme, method, authority, path,
|
||||
options,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
|
|
@ -40,8 +41,8 @@ func TestRequestTapByResourceFromAPI(t *testing.T) {
|
|||
Id: &common.TapEvent_Http_StreamId{
|
||||
Base: 1,
|
||||
},
|
||||
Authority: authority,
|
||||
Path: path,
|
||||
Authority: options.authority,
|
||||
Path: options.path,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -94,15 +95,16 @@ func TestRequestTapByResourceFromAPI(t *testing.T) {
|
|||
t.Run("Should render empty response if no events returned", func(t *testing.T) {
|
||||
resourceType := k8s.Pods
|
||||
targetName := "pod-666"
|
||||
scheme := "https"
|
||||
method := "GET"
|
||||
authority := "localhost"
|
||||
path := "/some/path"
|
||||
options := &tapOptions{
|
||||
scheme: "https",
|
||||
method: "GET",
|
||||
authority: "localhost",
|
||||
path: "/some/path",
|
||||
}
|
||||
|
||||
req, err := buildTapByResourceRequest(
|
||||
[]string{resourceType, targetName},
|
||||
"", "", "", 0,
|
||||
scheme, method, authority, path,
|
||||
options,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
|
|
@ -134,15 +136,16 @@ func TestRequestTapByResourceFromAPI(t *testing.T) {
|
|||
t.SkipNow()
|
||||
resourceType := k8s.Pods
|
||||
targetName := "pod-666"
|
||||
scheme := "https"
|
||||
method := "GET"
|
||||
authority := "localhost"
|
||||
path := "/some/path"
|
||||
options := &tapOptions{
|
||||
scheme: "https",
|
||||
method: "GET",
|
||||
authority: "localhost",
|
||||
path: "/some/path",
|
||||
}
|
||||
|
||||
req, err := buildTapByResourceRequest(
|
||||
[]string{resourceType, targetName},
|
||||
"", "", "", 0,
|
||||
scheme, method, authority, path,
|
||||
options,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
|
|
|
|||
|
|
@ -12,42 +12,54 @@ import (
|
|||
|
||||
const DefaultVersionString = "unavailable"
|
||||
|
||||
var shortVersion bool
|
||||
var onlyClientVersion bool
|
||||
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the client and server version information",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
clientVersion := version.Version
|
||||
if shortVersion {
|
||||
fmt.Println(clientVersion)
|
||||
} else {
|
||||
fmt.Printf("Client version: %s\n", clientVersion)
|
||||
}
|
||||
|
||||
conduitApiClient, err := newPublicAPIClient()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error connecting to server: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !onlyClientVersion {
|
||||
serverVersion := getServerVersion(conduitApiClient)
|
||||
if shortVersion {
|
||||
fmt.Println(serverVersion)
|
||||
} else {
|
||||
fmt.Printf("Server version: %s\n", serverVersion)
|
||||
}
|
||||
}
|
||||
},
|
||||
type versionOptions struct {
|
||||
shortVersion bool
|
||||
onlyClientVersion bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(versionCmd)
|
||||
versionCmd.Args = cobra.NoArgs
|
||||
versionCmd.PersistentFlags().BoolVar(&shortVersion, "short", false, "Print the version number(s) only, with no additional output")
|
||||
versionCmd.PersistentFlags().BoolVar(&onlyClientVersion, "client", false, "Print the client version only")
|
||||
func newVersionOptions() *versionOptions {
|
||||
return &versionOptions{
|
||||
shortVersion: false,
|
||||
onlyClientVersion: false,
|
||||
}
|
||||
}
|
||||
|
||||
func newCmdVersion() *cobra.Command {
|
||||
options := newVersionOptions()
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the client and server version information",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
clientVersion := version.Version
|
||||
if options.shortVersion {
|
||||
fmt.Println(clientVersion)
|
||||
} else {
|
||||
fmt.Printf("Client version: %s\n", clientVersion)
|
||||
}
|
||||
|
||||
conduitApiClient, err := newPublicAPIClient()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error connecting to server: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !options.onlyClientVersion {
|
||||
serverVersion := getServerVersion(conduitApiClient)
|
||||
if options.shortVersion {
|
||||
fmt.Println(serverVersion)
|
||||
} else {
|
||||
fmt.Printf("Server version: %s\n", serverVersion)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Args = cobra.NoArgs
|
||||
cmd.PersistentFlags().BoolVar(&options.shortVersion, "short", options.shortVersion, "Print the version number(s) only, with no additional output")
|
||||
cmd.PersistentFlags().BoolVar(&options.onlyClientVersion, "client", options.onlyClientVersion, "Print the client version only")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func getServerVersion(client pb.ApiClient) string {
|
||||
|
|
|
|||
Loading…
Reference in New Issue