Skip to content

Commit 5e73b30

Browse files
committed
feat(cmd): finish integration of attach,delete and get command
Signed-off-by: Lorenzo Fontana <[email protected]>
1 parent 465de92 commit 5e73b30

File tree

5 files changed

+116
-51
lines changed

5 files changed

+116
-51
lines changed

pkg/cmd/delete.go

+29-14
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,34 @@ var (
2727
%[1]s trace delete kubectl-trace-1bb3ae39-efe8-11e8-9f29-8c164500a77e
2828
2929
# Delete all bpftrace programs in a specific namespace
30-
%[1]s trace delete -n myns --all`
30+
%[1]s trace delete -n myns --all
31+
32+
# Delete all bpftrace programs in all the namespaces
33+
%[1]s trace delete --all-namespaces`
3134
)
3235

3336
// DeleteOptions ...
3437
type DeleteOptions struct {
3538
genericclioptions.IOStreams
36-
traceID *types.UID
37-
traceName *string
38-
namespace string
39-
clientConfig *rest.Config
40-
all bool
39+
ResourceBuilderFlags *genericclioptions.ResourceBuilderFlags
40+
traceID *types.UID
41+
traceName *string
42+
namespace string
43+
clientConfig *rest.Config
44+
all bool
45+
allNamespaces bool
4146
}
4247

4348
// NewDeleteOptions provides an instance of DeleteOptions with default values.
4449
func NewDeleteOptions(streams genericclioptions.IOStreams) *DeleteOptions {
50+
rbFlags := &genericclioptions.ResourceBuilderFlags{}
51+
rbFlags.WithAllNamespaces(false)
52+
rbFlags.WithAll(false)
53+
4554
return &DeleteOptions{
46-
IOStreams: streams,
47-
all: false,
55+
ResourceBuilderFlags: rbFlags,
56+
IOStreams: streams,
57+
all: false,
4858
}
4959
}
5060

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

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

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

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

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

113+
if cmd.Flag("all-namespaces").Changed {
114+
o.allNamespaces = *o.ResourceBuilderFlags.AllNamespaces
115+
o.namespace = ""
116+
}
117+
118+
if cmd.Flag("all").Changed {
119+
o.all = *o.ResourceBuilderFlags.All
120+
}
121+
107122
//// Prepare client
108123
o.clientConfig, err = factory.ToRESTConfig()
109124
if err != nil {

pkg/cmd/get.go

+78-29
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,18 @@ package cmd
22

33
import (
44
"fmt"
5+
"io"
6+
"text/tabwriter"
57

6-
"github.com/davecgh/go-spew/spew"
78
"github.com/fntlnz/kubectl-trace/pkg/factory"
9+
"github.com/fntlnz/kubectl-trace/pkg/meta"
10+
"github.com/fntlnz/kubectl-trace/pkg/tracejob"
811
"github.com/spf13/cobra"
12+
"k8s.io/apimachinery/pkg/types"
913
"k8s.io/cli-runtime/pkg/genericclioptions"
14+
batchv1client "k8s.io/client-go/kubernetes/typed/batch/v1"
15+
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
16+
"k8s.io/client-go/rest"
1017
)
1118

1219
var (
@@ -38,12 +45,14 @@ type GetOptions struct {
3845
genericclioptions.IOStreams
3946
ResourceBuilderFlags *genericclioptions.ResourceBuilderFlags
4047

41-
namespace string
42-
explicitNamespace bool
48+
namespace string
4349

4450
// Local to this command
4551
allNamespaces bool
4652
traceArg string
53+
clientConfig *rest.Config
54+
traceID *types.UID
55+
traceName *string
4756
}
4857

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

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

90-
// Validate validates the arguments and flags populating GetOptions accordingly.
9199
func (o *GetOptions) Validate(cmd *cobra.Command, args []string) error {
92100
switch len(args) {
93-
case 0:
94-
break
95101
case 1:
96-
o.traceArg = args[0]
102+
if meta.IsObjectName(args[0]) {
103+
o.traceName = &args[0]
104+
} else {
105+
tid := types.UID(args[0])
106+
o.traceID = &tid
107+
}
97108
break
98-
default:
99-
return fmt.Errorf(argumentsErr)
100109
}
101110

102111
return nil
103112
}
104113

105-
// Complete completes the setup of the command.
106114
func (o *GetOptions) Complete(factory factory.Factory, cmd *cobra.Command, args []string) error {
107115
// Prepare namespace
108116
var err error
109-
o.namespace, o.explicitNamespace, _ = factory.ToRawKubeConfigLoader().Namespace()
117+
o.namespace, _, err = factory.ToRawKubeConfigLoader().Namespace()
110118
if err != nil {
111119
return err
112120
}
113121

114122
// All namespaces, when present, overrides namespace flag
115123
if cmd.Flag("all-namespaces").Changed {
116124
o.allNamespaces = *o.ResourceBuilderFlags.AllNamespaces
117-
o.explicitNamespace = false
118125
o.namespace = ""
119126
}
120-
// Need either a namespace, a trace ID, or all namespaces
121-
if o.traceArg == "" && !o.allNamespaces && !o.explicitNamespace {
122-
return fmt.Errorf(missingTargetErr)
123-
}
124127

125-
// todo > init printers (need o.PrintFlags)
126-
127-
// todo > setup printer
128-
// printer, err := o.PrintFlags.ToPrinter()
129-
// if err != nil {
130-
// return err
131-
// }
132-
// o.print = func(obj runtime.Object) error {
133-
// return printer.PrintObj(obj, o.Out)
134-
// }
128+
//// Prepare client
129+
o.clientConfig, err = factory.ToRESTConfig()
130+
if err != nil {
131+
return err
132+
}
135133

136134
return nil
137135
}
138136

139-
// Run executes the get command.
140137
func (o *GetOptions) Run() error {
141-
spew.Dump(o)
138+
jobsClient, err := batchv1client.NewForConfig(o.clientConfig)
139+
if err != nil {
140+
return err
141+
}
142+
143+
coreClient, err := corev1client.NewForConfig(o.clientConfig)
144+
if err != nil {
145+
return err
146+
}
147+
148+
tc := &tracejob.TraceJobClient{
149+
JobClient: jobsClient.Jobs(o.namespace),
150+
ConfigClient: coreClient.ConfigMaps(o.namespace),
151+
}
152+
153+
tc.WithOutStream(o.Out)
154+
155+
tf := tracejob.TraceJobFilter{
156+
Name: o.traceName,
157+
ID: o.traceID,
158+
}
159+
160+
jobs, err := tc.GetJob(tf)
161+
162+
if err != nil {
163+
return err
164+
}
165+
166+
// 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
167+
jobsTablePrint(o.Out, jobs)
142168
return nil
143169
}
170+
171+
// TODO(fntlnz): This needs better printing, perhaps we could use the humanreadable table from k8s itself
172+
// to be consistent with the main project.
173+
func jobsTablePrint(o io.Writer, jobs []tracejob.TraceJob) {
174+
format := "%s\t%s\t%s\t%s\t%s\t"
175+
if len(jobs) == 0 {
176+
fmt.Println("No resources found.")
177+
return
178+
}
179+
// initialize tabwriter
180+
w := new(tabwriter.Writer)
181+
// minwidth, tabwidth, padding, padchar, flags
182+
w.Init(o, 8, 8, 0, '\t', 0)
183+
defer w.Flush()
184+
185+
// TODO(fntlnz): Do the status and age fields, we don't have a way to get them now, so reporting
186+
// them as missing.
187+
fmt.Fprintf(w, format, "NAMESPACE", "NODE", "NAME", "STATUS", "AGE")
188+
for _, j := range jobs {
189+
fmt.Fprintf(w, "\n"+format, j.Namespace, j.Hostname, j.Name, "<missing>", "<missing>")
190+
}
191+
fmt.Fprintf(w, "\n")
192+
}

pkg/cmd/run.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ var (
3030
%[1]s trace run node/kubernetes-node-emt8.c.myproject.internal -e 'kprobe:do_sys_open { printf("%s: %s\n", comm, str(arg1)) }'
3131
3232
# Execute a bpftrace program from file on a specific node
33-
%[1]s trace run node/kubernetes-node-emt8.c.myproject.internal -p read.bt
33+
%[1]s trace run node/kubernetes-node-emt8.c.myproject.internal -f read.bt
3434
3535
# Run an bpftrace inline program on a pod container
3636
%[1]s trace run pod/nginx -c nginx -e "tracepoint:syscalls:sys_enter_* { @[probe] = count(); }"
@@ -100,7 +100,7 @@ func NewRunCommand(factory factory.Factory, streams genericclioptions.IOStreams)
100100
cmd.Flags().StringVarP(&o.container, "container", "c", o.container, "Specify the container")
101101
cmd.Flags().BoolVarP(&o.attach, "attach", "a", o.attach, "Wheter or not to attach to the trace program once it is created")
102102
cmd.Flags().StringVarP(&o.eval, "eval", "e", "", "Literal string to be evaluated as a bpftrace program")
103-
cmd.Flags().StringVarP(&o.program, "program", "p", "", "File containing a bpftrace program")
103+
cmd.Flags().StringVarP(&o.program, "filename", "f", "", "File containing a bpftrace program")
104104

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

127-
if !cmd.Flag("eval").Changed && !cmd.Flag("program").Changed {
127+
if !cmd.Flag("eval").Changed && !cmd.Flag("filename").Changed {
128128
return fmt.Errorf(bpftraceMissingErrString)
129129
}
130-
if cmd.Flag("eval").Changed == cmd.Flag("program").Changed {
130+
if cmd.Flag("eval").Changed == cmd.Flag("filename").Changed {
131131
return fmt.Errorf(bpftraceDoubleErrString)
132132
}
133-
if (cmd.Flag("eval").Changed && len(o.eval) == 0) || (cmd.Flag("program").Changed && len(o.program) == 0) {
133+
if (cmd.Flag("eval").Changed && len(o.eval) == 0) || (cmd.Flag("filename").Changed && len(o.program) == 0) {
134134
return fmt.Errorf(bpftraceEmptyErrString)
135135
}
136136

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

169169
obj, err := x.Object()

pkg/cmd/trace.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ These commands help you trace existing application resources.
1616
`
1717
traceExamples = `
1818
# Execute a bpftrace program from file on a specific node
19-
%[1]s trace run kubernetes-node-emt8.c.myproject.internal -p read.bt
19+
%[1]s trace run kubernetes-node-emt8.c.myproject.internal -f read.bt
2020
2121
# Get all bpftrace programs in all namespaces
2222
%[1]s trace get --all-namespaces

pkg/tracejob/job.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ func (t *TraceJobClient) DeleteJobs(nf TraceJobFilter) error {
132132
dp := metav1.DeletePropagationForeground
133133
for _, j := range jl {
134134
err := t.JobClient.Delete(j.Name, &metav1.DeleteOptions{
135-
PropagationPolicy: &dp,
135+
GracePeriodSeconds: int64Ptr(0),
136+
PropagationPolicy: &dp,
136137
})
137138
if err != nil {
138139
return err

0 commit comments

Comments
 (0)