Skip to content

Commit

Permalink
feat(cmd): finish integration of attach,delete and get command
Browse files Browse the repository at this point in the history
Signed-off-by: Lorenzo Fontana <[email protected]>
  • Loading branch information
fntlnz committed Nov 25, 2018
1 parent 465de92 commit 5e73b30
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 51 deletions.
43 changes: 29 additions & 14 deletions pkg/cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,34 @@ var (
%[1]s trace delete kubectl-trace-1bb3ae39-efe8-11e8-9f29-8c164500a77e
# Delete all bpftrace programs in a specific namespace
%[1]s trace delete -n myns --all`
%[1]s trace delete -n myns --all
# Delete all bpftrace programs in all the namespaces
%[1]s trace delete --all-namespaces`
)

// DeleteOptions ...
type DeleteOptions struct {
genericclioptions.IOStreams
traceID *types.UID
traceName *string
namespace string
clientConfig *rest.Config
all bool
ResourceBuilderFlags *genericclioptions.ResourceBuilderFlags
traceID *types.UID
traceName *string
namespace string
clientConfig *rest.Config
all bool
allNamespaces bool
}

// NewDeleteOptions provides an instance of DeleteOptions with default values.
func NewDeleteOptions(streams genericclioptions.IOStreams) *DeleteOptions {
rbFlags := &genericclioptions.ResourceBuilderFlags{}
rbFlags.WithAllNamespaces(false)
rbFlags.WithAll(false)

return &DeleteOptions{
IOStreams: streams,
all: false,
ResourceBuilderFlags: rbFlags,
IOStreams: streams,
all: false,
}
}

Expand All @@ -53,7 +63,7 @@ func NewDeleteCommand(factory factory.Factory, streams genericclioptions.IOStrea
o := NewDeleteOptions(streams)

cmd := &cobra.Command{
Use: "delete [TRACE_ID] [--all]",
Use: "delete (TRACE_ID | TRACE_NAME)",
Short: deleteShort,
Long: deleteLong, // Wrap with templates.LongDesc()
Example: fmt.Sprintf(deleteExamples, "kubectl"), // Wrap with templates.Examples()
Expand All @@ -72,7 +82,7 @@ func NewDeleteCommand(factory factory.Factory, streams genericclioptions.IOStrea
},
}

cmd.Flags().BoolVar(&o.all, "all", o.all, "Delete all trace jobs in the provided namespace")
o.ResourceBuilderFlags.AddFlags(cmd.Flags())

return cmd
}
Expand All @@ -87,10 +97,6 @@ func (o *DeleteOptions) Validate(cmd *cobra.Command, args []string) error {
o.traceID = &tid
}
break
default:
if o.all == false {
return fmt.Errorf("--all=true must be specified to delete all the trace programs in a namespace\n%s", requiredArgErrString)
}
}

return nil
Expand All @@ -104,6 +110,15 @@ func (o *DeleteOptions) Complete(factory factory.Factory, cmd *cobra.Command, ar
return err
}

if cmd.Flag("all-namespaces").Changed {
o.allNamespaces = *o.ResourceBuilderFlags.AllNamespaces
o.namespace = ""
}

if cmd.Flag("all").Changed {
o.all = *o.ResourceBuilderFlags.All
}

//// Prepare client
o.clientConfig, err = factory.ToRESTConfig()
if err != nil {
Expand Down
107 changes: 78 additions & 29 deletions pkg/cmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@ package cmd

import (
"fmt"
"io"
"text/tabwriter"

"github.com/davecgh/go-spew/spew"
"github.com/fntlnz/kubectl-trace/pkg/factory"
"github.com/fntlnz/kubectl-trace/pkg/meta"
"github.com/fntlnz/kubectl-trace/pkg/tracejob"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/types"
"k8s.io/cli-runtime/pkg/genericclioptions"
batchv1client "k8s.io/client-go/kubernetes/typed/batch/v1"
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
)

var (
Expand Down Expand Up @@ -38,12 +45,14 @@ type GetOptions struct {
genericclioptions.IOStreams
ResourceBuilderFlags *genericclioptions.ResourceBuilderFlags

namespace string
explicitNamespace bool
namespace string

// Local to this command
allNamespaces bool
traceArg string
clientConfig *rest.Config
traceID *types.UID
traceName *string
}

// NewGetOptions provides an instance of GetOptions with default values.
Expand All @@ -62,7 +71,7 @@ func NewGetCommand(factory factory.Factory, streams genericclioptions.IOStreams)
o := NewGetOptions(streams)

cmd := &cobra.Command{
Use: fmt.Sprintf("%s [TRACE_ID]", getCommand),
Use: fmt.Sprintf("%s (TRACE_ID | TRACE_NAME)", getCommand),
Short: getShort,
Long: getLong, // wrap with templates.LongDesc()
Example: fmt.Sprintf(getExamples, "kubectl"), // wrap with templates.Examples()
Expand All @@ -87,57 +96,97 @@ func NewGetCommand(factory factory.Factory, streams genericclioptions.IOStreams)
return cmd
}

// Validate validates the arguments and flags populating GetOptions accordingly.
func (o *GetOptions) Validate(cmd *cobra.Command, args []string) error {
switch len(args) {
case 0:
break
case 1:
o.traceArg = args[0]
if meta.IsObjectName(args[0]) {
o.traceName = &args[0]
} else {
tid := types.UID(args[0])
o.traceID = &tid
}
break
default:
return fmt.Errorf(argumentsErr)
}

return nil
}

// Complete completes the setup of the command.
func (o *GetOptions) Complete(factory factory.Factory, cmd *cobra.Command, args []string) error {
// Prepare namespace
var err error
o.namespace, o.explicitNamespace, _ = factory.ToRawKubeConfigLoader().Namespace()
o.namespace, _, err = factory.ToRawKubeConfigLoader().Namespace()
if err != nil {
return err
}

// All namespaces, when present, overrides namespace flag
if cmd.Flag("all-namespaces").Changed {
o.allNamespaces = *o.ResourceBuilderFlags.AllNamespaces
o.explicitNamespace = false
o.namespace = ""
}
// Need either a namespace, a trace ID, or all namespaces
if o.traceArg == "" && !o.allNamespaces && !o.explicitNamespace {
return fmt.Errorf(missingTargetErr)
}

// todo > init printers (need o.PrintFlags)

// todo > setup printer
// printer, err := o.PrintFlags.ToPrinter()
// if err != nil {
// return err
// }
// o.print = func(obj runtime.Object) error {
// return printer.PrintObj(obj, o.Out)
// }
//// Prepare client
o.clientConfig, err = factory.ToRESTConfig()
if err != nil {
return err
}

return nil
}

// Run executes the get command.
func (o *GetOptions) Run() error {
spew.Dump(o)
jobsClient, err := batchv1client.NewForConfig(o.clientConfig)
if err != nil {
return err
}

coreClient, err := corev1client.NewForConfig(o.clientConfig)
if err != nil {
return err
}

tc := &tracejob.TraceJobClient{
JobClient: jobsClient.Jobs(o.namespace),
ConfigClient: coreClient.ConfigMaps(o.namespace),
}

tc.WithOutStream(o.Out)

tf := tracejob.TraceJobFilter{
Name: o.traceName,
ID: o.traceID,
}

jobs, err := tc.GetJob(tf)

if err != nil {
return err
}

// TODO: support other output formats via the o flag, like json, yaml. Not sure if a good idea, trace is not a resource in k8s
jobsTablePrint(o.Out, jobs)
return nil
}

// TODO(fntlnz): This needs better printing, perhaps we could use the humanreadable table from k8s itself
// to be consistent with the main project.
func jobsTablePrint(o io.Writer, jobs []tracejob.TraceJob) {
format := "%s\t%s\t%s\t%s\t%s\t"
if len(jobs) == 0 {
fmt.Println("No resources found.")
return
}
// initialize tabwriter
w := new(tabwriter.Writer)
// minwidth, tabwidth, padding, padchar, flags
w.Init(o, 8, 8, 0, '\t', 0)
defer w.Flush()

// TODO(fntlnz): Do the status and age fields, we don't have a way to get them now, so reporting
// them as missing.
fmt.Fprintf(w, format, "NAMESPACE", "NODE", "NAME", "STATUS", "AGE")
for _, j := range jobs {
fmt.Fprintf(w, "\n"+format, j.Namespace, j.Hostname, j.Name, "<missing>", "<missing>")
}
fmt.Fprintf(w, "\n")
}
12 changes: 6 additions & 6 deletions pkg/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ var (
%[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
%[1]s trace run node/kubernetes-node-emt8.c.myproject.internal -f 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(); }"
Expand Down Expand Up @@ -100,7 +100,7 @@ func NewRunCommand(factory factory.Factory, streams genericclioptions.IOStreams)
cmd.Flags().StringVarP(&o.container, "container", "c", o.container, "Specify the container")
cmd.Flags().BoolVarP(&o.attach, "attach", "a", o.attach, "Wheter or not to attach to the trace program once it is created")
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")
cmd.Flags().StringVarP(&o.program, "filename", "f", "", "File containing a bpftrace program")

return cmd
}
Expand All @@ -124,13 +124,13 @@ func (o *RunOptions) Validate(cmd *cobra.Command, args []string) error {
return fmt.Errorf(requiredArgErrString)
}

if !cmd.Flag("eval").Changed && !cmd.Flag("program").Changed {
if !cmd.Flag("eval").Changed && !cmd.Flag("filename").Changed {
return fmt.Errorf(bpftraceMissingErrString)
}
if cmd.Flag("eval").Changed == cmd.Flag("program").Changed {
if cmd.Flag("eval").Changed == cmd.Flag("filename").Changed {
return fmt.Errorf(bpftraceDoubleErrString)
}
if (cmd.Flag("eval").Changed && len(o.eval) == 0) || (cmd.Flag("program").Changed && len(o.program) == 0) {
if (cmd.Flag("eval").Changed && len(o.eval) == 0) || (cmd.Flag("filename").Changed && len(o.program) == 0) {
return fmt.Errorf(bpftraceEmptyErrString)
}

Expand Down Expand Up @@ -163,7 +163,7 @@ func (o *RunOptions) Complete(factory factory.Factory, cmd *cobra.Command, args
WithScheme(scheme.Scheme, scheme.Scheme.PrioritizedVersionsAllGroups()...).
NamespaceParam(o.namespace).
SingleResourceType().
ResourceNames("pods", o.resourceArg). // Search pods by default
ResourceNames("nodes", o.resourceArg). // Search nodes by default
Do()

obj, err := x.Object()
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ These commands help you trace existing application resources.
`
traceExamples = `
# Execute a bpftrace program from file on a specific node
%[1]s trace run kubernetes-node-emt8.c.myproject.internal -p read.bt
%[1]s trace run kubernetes-node-emt8.c.myproject.internal -f read.bt
# Get all bpftrace programs in all namespaces
%[1]s trace get --all-namespaces
Expand Down
3 changes: 2 additions & 1 deletion pkg/tracejob/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ func (t *TraceJobClient) DeleteJobs(nf TraceJobFilter) error {
dp := metav1.DeletePropagationForeground
for _, j := range jl {
err := t.JobClient.Delete(j.Name, &metav1.DeleteOptions{
PropagationPolicy: &dp,
GracePeriodSeconds: int64Ptr(0),
PropagationPolicy: &dp,
})
if err != nil {
return err
Expand Down

0 comments on commit 5e73b30

Please sign in to comment.