Skip to content

Commit 827c417

Browse files
committed
feat(cmd/get): basic get command
Signed-off-by: Lorenzo Fontana <[email protected]>
1 parent dea645f commit 827c417

File tree

6 files changed

+255
-28
lines changed

6 files changed

+255
-28
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ Some of them will not yet work because we don't attach with a TTY already, sorry
3939
To consider this project (ready) the goals are:
4040

4141
- [x] basic program run and attach
42-
- [ ] list command to list running traces - command: `kubectl trace ls`
42+
- [x] list command to list running traces - command: `kubectl trace get`
4343
- [x] delete running traces
4444
- [ ] run without attach
4545
- [ ] attach command to attach only - command: `kubectl trace attach <program>`

cmd/kubectl-trace/delete.go

+9-8
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ import (
1010
)
1111

1212
var deleteCmd = &cobra.Command{
13-
Use: "delete TRACEID",
13+
Use: "delete NAME",
1414
Short: "Delete a trace execution from your system",
15-
Long: `Delete all the running pods that are collecting your trace data using bpftrace for a given TRACEID
15+
Long: `Delete all the running pods that are collecting your trace data using bpftrace for a given NAME
1616
1717
Example:
1818
# Delete a specific trace
19-
kubectl trace delete 656ee75a-ee3c-11e8-9e7a-8c164500a77e
19+
kubectl trace delete kubectl-trace-d5314890-ee4f-11e8-9684-8c164500a77e-sm4t2<Paste>
2020
2121
Limitations:
2222
This command does not implement yet a way to bulk delete traces.
@@ -29,9 +29,9 @@ func delete(cmd *cobra.Command, args []string) {
2929
defer log.Sync()
3030

3131
if len(args) == 0 {
32-
log.Fatal("TRACEID not provided")
32+
log.Fatal("NAME not provided")
3333
}
34-
uuid := args[0]
34+
name := args[0]
3535

3636
kubeconfig := viper.GetString("kubeconfig")
3737
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
@@ -45,18 +45,19 @@ func delete(cmd *cobra.Command, args []string) {
4545
log.Fatal("cannot create kubernetes config from provider KUBECONFIG", zap.Error(err))
4646
}
4747

48+
namespace := viper.GetString("namespace")
4849
jobsClient := clientset.BatchV1().Jobs(namespace)
4950

5051
tc := &tracejob.TraceJobClient{
5152
JobClient: jobsClient,
5253
ConfigClient: clientset.CoreV1().ConfigMaps(namespace),
5354
}
5455

55-
tj := tracejob.TraceJob{
56-
ID: uuid,
56+
tf := tracejob.TraceJobFilter{
57+
Name: &name,
5758
}
5859

59-
err = tc.DeleteJob(tj)
60+
err = tc.DeleteJob(tf)
6061

6162
if err != nil {
6263
log.Fatal("error deleting trace execution from cluster", zap.Error(err))

cmd/kubectl-trace/get.go

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
7+
"text/tabwriter"
8+
9+
"github.com/fntlnz/kubectl-trace/pkg/tracejob"
10+
"github.com/spf13/cobra"
11+
"github.com/spf13/viper"
12+
"go.uber.org/zap"
13+
"k8s.io/client-go/kubernetes"
14+
"k8s.io/client-go/tools/clientcmd"
15+
)
16+
17+
var getCmd = &cobra.Command{
18+
Use: "get [TRACEID] [-n NAMESPACE]",
19+
Short: "Get all the running traces in a kubernetes cluster",
20+
Long: `Get all the running traces in a kubernetes cluster
21+
22+
Examples:
23+
# Get all traces in a namespace
24+
kubectl trace get -n mynamespace
25+
26+
# Get only a specific trace
27+
kubectl trace get 656ee75a-ee3c-11e8-9e7a-8c164500a77e
28+
29+
# Get only a specific trace in a specific namespace
30+
kubectl trace get 656ee75a-ee3c-11e8-9e7a-8c164500a77e -n mynamespace
31+
32+
Limitations:
33+
- Currently work only with a single namespace at time
34+
- It does not contain yet status and age for the trace
35+
`,
36+
Run: get,
37+
}
38+
39+
func get(cmd *cobra.Command, args []string) {
40+
log, _ := zap.NewProduction()
41+
defer log.Sync()
42+
43+
kubeconfig := viper.GetString("kubeconfig")
44+
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
45+
46+
if err != nil {
47+
log.Fatal("cannot create kubernetes client from provider KUBECONFIG", zap.Error(err))
48+
}
49+
50+
var uuid *string
51+
if len(args) > 0 {
52+
uuid = &args[0]
53+
}
54+
55+
namespace := viper.GetString("namespace")
56+
57+
clientset, err := kubernetes.NewForConfig(config)
58+
if err != nil {
59+
log.Fatal("cannot create kubernetes config from provider KUBECONFIG", zap.Error(err))
60+
}
61+
62+
jobsClient := clientset.BatchV1().Jobs(namespace)
63+
64+
tc := &tracejob.TraceJobClient{
65+
JobClient: jobsClient,
66+
ConfigClient: clientset.CoreV1().ConfigMaps(namespace),
67+
}
68+
69+
tf := tracejob.TraceJobFilter{
70+
ID: uuid,
71+
}
72+
73+
jobs, err := tc.GetJob(tf)
74+
75+
if err != nil {
76+
log.Fatal("error getting jobs with provided filter", zap.Error(err), zap.Any("filter", tf))
77+
}
78+
79+
jobsTablePrint(jobs)
80+
81+
}
82+
83+
// TODO(fntlnz): This needs better printing, perhaps we could use the humanreadable table from k8s itself
84+
// to be consistent with the main project.
85+
func jobsTablePrint(jobs []tracejob.TraceJob) {
86+
format := "%s\t%s\t%s\t%s\t%s\t"
87+
if len(jobs) == 0 {
88+
fmt.Println("No resources found.")
89+
return
90+
}
91+
// initialize tabwriter
92+
w := new(tabwriter.Writer)
93+
// minwidth, tabwidth, padding, padchar, flags
94+
w.Init(os.Stdout, 8, 8, 0, '\t', 0)
95+
defer w.Flush()
96+
97+
// TODO(fntlnz): Do the status and age fields, we don't have a way to get them now, so reporting
98+
// them as missing.
99+
fmt.Fprintf(w, format, "NAMESPACE", "NAME", "STATUS", "AGE", "HOSTNAME")
100+
for _, j := range jobs {
101+
fmt.Fprintf(w, "\n"+format, j.Namespace, j.Name, "<missing>", "<missing>", j.Hostname)
102+
}
103+
}

cmd/kubectl-trace/root.go

+6-10
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@ import (
77
homedir "github.com/mitchellh/go-homedir"
88
"github.com/spf13/cobra"
99
"github.com/spf13/viper"
10+
apiv1 "k8s.io/api/core/v1"
1011
//"k8s.io/cli-runtime/pkg/genericclioptions"
1112
)
1213

1314
var cfgFile string
1415

15-
//var parentConfigFlags genericclioptions.ConfigFlags
16-
1716
var rootCmd = &cobra.Command{
1817
Use: "trace",
1918
Short: "Execute and manage bpftrace programs on your kubernetes cluster",
@@ -29,21 +28,18 @@ func Execute() {
2928
func init() {
3029
cobra.OnInitialize(initConfig)
3130

32-
// Here you will define your flags and configuration settings.
33-
// Cobra supports persistent flags, which, if defined here,
34-
// will be global for your application.
3531
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.kubectl-trace.yaml)")
3632

37-
// TODO(leodido): figure out how to use the flag from the main kubectl
38-
// instead of having to recreate them like below
39-
//parentConfigFlags = genericclioptions.ConfigFlags{}
40-
//parentConfigFlags.AddFlags(rootCmd.PersistentFlags())
41-
4233
rootCmd.PersistentFlags().String("kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
4334
viper.BindPFlag("kubeconfig", rootCmd.PersistentFlags().Lookup("kubeconfig"))
4435
viper.BindEnv("kubeconfig", "KUBECONFIG")
36+
37+
rootCmd.PersistentFlags().StringP("namespace", "n", apiv1.NamespaceDefault, "If present, the namespace scope for this CLI request")
38+
viper.BindPFlag("namespace", rootCmd.PersistentFlags().Lookup("namespace"))
39+
4540
rootCmd.AddCommand(runCmd)
4641
rootCmd.AddCommand(deleteCmd)
42+
rootCmd.AddCommand(getCmd)
4743
}
4844

4945
// initConfig reads in config file and ENV variables if set.

cmd/kubectl-trace/run.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"go.uber.org/zap"
1414
"k8s.io/apimachinery/pkg/util/uuid"
1515

16-
apiv1 "k8s.io/api/core/v1"
1716
"k8s.io/client-go/kubernetes"
1817
"k8s.io/client-go/tools/clientcmd"
1918
)
@@ -29,18 +28,22 @@ Examples:
2928
3029
# Execute a program from file on a specific node
3130
kubectl trace run kubernetes-node-emt8.c.myproject.internal -f read.bt
31+
32+
Limitations:
33+
1. Right now this command lets you run bpftrace commands only on a specific node in your cluster,
34+
the plan is to have this working for a node, a pod, a deployment, a statefulset.
35+
2. Since there's no TTY attach (yet) it is not possible to run bpftrace commands that need an input
36+
from the user in order to complete, like histograms, working on this is a priority for this project.
3237
`,
3338
Run: run,
3439
}
3540

3641
var program string
3742
var programfile string
38-
var namespace string
3943

4044
func init() {
4145
runCmd.Flags().StringVarP(&program, "program-literal", "e", "", "Literal string containing a bpftrace program")
4246
runCmd.Flags().StringVarP(&programfile, "program-file", "f", "", "File containing a bpftrace program")
43-
runCmd.Flags().StringVarP(&namespace, "namespace", "n", apiv1.NamespaceDefault, "Name of the node where to do the trace")
4447
}
4548

4649
func run(cmd *cobra.Command, args []string) {
@@ -63,6 +66,8 @@ func run(cmd *cobra.Command, args []string) {
6366
}
6467
node := args[0]
6568

69+
namespace := viper.GetString("namespace")
70+
6671
ctx := context.Background()
6772
ctx = signals.WithStandardSignals(ctx)
6873

0 commit comments

Comments
 (0)