-
Notifications
You must be signed in to change notification settings - Fork 165
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
135 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/davecgh/go-spew/spew" | ||
"github.com/fntlnz/kubectl-trace/factory" | ||
"github.com/spf13/cobra" | ||
"k8s.io/cli-runtime/pkg/genericclioptions" | ||
// "k8s.io/kubernetes/pkg/kubectl/util/templates" | ||
) | ||
|
||
var ( | ||
runShort = `Execute a bpftrace program on resources` // Wrap with i18n.T() | ||
|
||
runLong = runShort | ||
|
||
runExamples = ` | ||
# Count system calls using tracepoints on a specific node | ||
%[1]s trace run node/kubernetes-node-emt8.c.myproject.internal -e 'kprobe:do_sys_open { printf("%s: %s\n", comm, str(arg1)) }' | ||
# Execute a bpftrace program from file on a specific node | ||
%[1]s trace run node/kubernetes-node-emt8.c.myproject.internal -p read.bt | ||
# Run an bpftrace inline program on a pod container | ||
%[1]s trace run pod/nginx -c nginx -e "tracepoint:syscalls:sys_enter_* { @[probe] = count(); }" | ||
%[1]s trace run pod/nginx nginx -e "tracepoint:syscalls:sys_enter_* { @[probe] = count(); }"` | ||
|
||
runCommand = "run" | ||
usageString = "(POD | TYPE/NAME)" | ||
requiredArgErrString = fmt.Sprintf("%s is a required argument for the %s command", usageString, runCommand) | ||
containerAsArgOrFlagErrString = "specify container inline as argument or via its flag" | ||
bpftraceMissingErrString = "the bpftrace program is mandatory" | ||
) | ||
|
||
// RunOptions ... | ||
type RunOptions struct { | ||
genericclioptions.IOStreams | ||
|
||
namespace string | ||
|
||
// Local to this command | ||
container string | ||
eval string | ||
program string | ||
resourceArg string | ||
} | ||
|
||
// NewRunOptions provides an instance of RunOptions with default values. | ||
func NewRunOptions(streams genericclioptions.IOStreams) *RunOptions { | ||
return &RunOptions{ | ||
IOStreams: streams, | ||
} | ||
} | ||
|
||
// NewRunCommand provides the run command wrapping RunOptions. | ||
func NewRunCommand(factory factory.Factory, streams genericclioptions.IOStreams) *cobra.Command { | ||
o := NewRunOptions(streams) | ||
|
||
cmd := &cobra.Command{ | ||
Use: fmt.Sprintf("%s %s [-c CONTAINER]", runCommand, usageString), | ||
Short: runShort, | ||
Long: runLong, // Wrap with templates.LongDesc() | ||
Example: fmt.Sprintf(runExamples, "kubectl"), // Wrap with templates.Examples() | ||
SilenceUsage: true, | ||
PreRunE: func(c *cobra.Command, args []string) error { | ||
return o.Validate(c, args) | ||
}, | ||
RunE: func(c *cobra.Command, args []string) error { | ||
if err := o.Complete(factory, c, args); err != nil { | ||
return err | ||
} | ||
return nil | ||
}, | ||
} | ||
|
||
cmd.Flags().StringVarP(&o.container, "container", "c", o.container, "Specify the container") | ||
cmd.Flags().StringVarP(&o.eval, "eval", "e", "", "Literal string to be evaluated as a bpftrace program") | ||
cmd.Flags().StringVarP(&o.program, "program", "p", "", "File containing a bpftrace program") | ||
|
||
return cmd | ||
} | ||
|
||
// Validate validates the arguments and flags populating RunOptions accordingly. | ||
func (o *RunOptions) Validate(cmd *cobra.Command, args []string) error { | ||
containerDefined := cmd.Flag("container").Changed | ||
switch len(args) { | ||
case 1: | ||
o.resourceArg = args[0] | ||
if !containerDefined { | ||
return fmt.Errorf(containerAsArgOrFlagErrString) | ||
} | ||
break | ||
// 2nd argument interpreted as container when provided | ||
case 2: | ||
o.resourceArg = args[0] | ||
o.container = args[1] | ||
if containerDefined { | ||
return fmt.Errorf(containerAsArgOrFlagErrString) | ||
} | ||
break | ||
default: | ||
return fmt.Errorf(requiredArgErrString) | ||
} | ||
|
||
if !cmd.Flag("eval").Changed && !cmd.Flag("program").Changed { | ||
return fmt.Errorf(bpftraceMissingErrString) | ||
} | ||
|
||
// todo > complete validation | ||
// - make errors | ||
// - make validators | ||
if len(o.container) == 0 { | ||
return fmt.Errorf("invalid container") | ||
} | ||
// if len(o.eval) == 0 || file not exist(o.program) || file is empty(o.program) { | ||
// return fmt.Errorf("invalid bpftrace program") | ||
// } | ||
|
||
return nil | ||
} | ||
|
||
// Complete completes the setup of the command. | ||
func (o *RunOptions) Complete(factory factory.Factory, cmd *cobra.Command, args []string) error { | ||
spew.Dump(o) | ||
|
||
o.namespace, _, _ = factory.ToRawKubeConfigLoader().Namespace() | ||
|
||
spew.Dump(o) | ||
|
||
// get resource by pof | type/name | ||
// get container | ||
|
||
return nil | ||
} |