Skip to content

Commit

Permalink
feat(cmd/log): initial implementation of the kubectl trace logs com…
Browse files Browse the repository at this point in the history
…mand

Signed-off-by: Lorenzo Fontana <lo@linux.com>
  • Loading branch information
fntlnz committed Dec 29, 2018
1 parent 10efc55 commit 8a59c03
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 10 deletions.
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ require (
github.com/spf13/pflag v1.0.3
github.com/stevvooe/resumable v0.0.0-20180830230917-22b14a53ba50 // indirect
golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869 // indirect
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a // indirect
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc // indirect
golang.org/x/oauth2 v0.0.0-20181120190819-8f65e3013eba // indirect
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f // indirect
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b // indirect
Expand All @@ -53,7 +53,7 @@ require (
k8s.io/client-go v9.0.0+incompatible
k8s.io/klog v0.1.0 // indirect
k8s.io/kube-openapi v0.0.0-20181114233023-0317810137be // indirect
k8s.io/kubernetes v1.12.2
k8s.io/kubernetes v1.12.3
k8s.io/utils v0.0.0-20181115163542-0d26856f57b3 // indirect
sigs.k8s.io/yaml v1.1.0 // indirect
)
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/oauth2 v0.0.0-20181120190819-8f65e3013eba h1:YDkOrzGLLYybtuP6ZgebnO4OWYEYVMFSniazXsxrFN8=
golang.org/x/oauth2 v0.0.0-20181120190819-8f65e3013eba/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down Expand Up @@ -151,8 +151,8 @@ k8s.io/klog v0.1.0 h1:I5HMfc/DtuVaGR1KPwUrTc476K8NCqNBldC7H4dYEzk=
k8s.io/klog v0.1.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/kube-openapi v0.0.0-20181114233023-0317810137be h1:aWEq4nbj7HRJ0mtKYjNSk/7X28Tl6TI6FeG8gKF+r7Q=
k8s.io/kube-openapi v0.0.0-20181114233023-0317810137be/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
k8s.io/kubernetes v1.12.2 h1:JEj2cxR+5T31U4klP5hI5dxjr6udRIHm+4dzCEPk498=
k8s.io/kubernetes v1.12.2/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
k8s.io/kubernetes v1.12.3 h1:1FH8TXY6pIuOHoNlsq9bB6rmCP0vfvQZH5YN7OXCHXU=
k8s.io/kubernetes v1.12.3/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
k8s.io/utils v0.0.0-20181115163542-0d26856f57b3 h1:S3/Kq185JnolOEemhmDXXd23l2t4bX5hPQPQPADlF1E=
k8s.io/utils v0.0.0-20181115163542-0d26856f57b3/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
Expand Down
150 changes: 150 additions & 0 deletions pkg/cmd/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package cmd

import (
"context"
"fmt"

"github.com/fntlnz/kubectl-trace/pkg/factory"
"github.com/fntlnz/kubectl-trace/pkg/logs"
"github.com/fntlnz/kubectl-trace/pkg/meta"
"github.com/fntlnz/kubectl-trace/pkg/signals"
"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 (
logShort = `` // Wrap with i18n.T()
logLong = logShort + `
...`

logExamples = `
# ...
%[1]s trace log -h
# ...
%[1]s trace log`
)

// LogOptions ...
type LogOptions struct {
genericclioptions.IOStreams
traceID *types.UID
traceName *string
namespace string
clientConfig *rest.Config
}

// NewLogOptions provides an instance of LogOptions with default values.
func NewLogOptions(streams genericclioptions.IOStreams) *LogOptions {
return &LogOptions{
IOStreams: streams,
}
}

// NewLogCommand provides the log command wrapping LogOptions.
func NewLogCommand(factory factory.Factory, streams genericclioptions.IOStreams) *cobra.Command {
o := NewLogOptions(streams)

cmd := &cobra.Command{
Use: "log (TRACE_ID | TRACE_NAME)",
DisableFlagsInUseLine: true,
Short: logShort,
Long: logLong, // Wrap with templates.LongDesc()
Example: fmt.Sprintf(logExamples, "kubectl"), // Wrap with templates.Examples()
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
}
if err := o.Run(); err != nil {
fmt.Fprintln(o.ErrOut, err.Error())
return nil
}
return nil
},
}

return cmd
}

func (o *LogOptions) Validate(cmd *cobra.Command, args []string) error {
switch len(args) {
case 1:
if meta.IsObjectName(args[0]) {
o.traceName = &args[0]
} else {
tid := types.UID(args[0])
o.traceID = &tid
}
break
default:
return fmt.Errorf("(TRACE_ID | TRACE_NAME) is a required argument for the log command")
}

return nil
}

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

// Prepare client
o.clientConfig, err = factory.ToRESTConfig()
if err != nil {
return err
}

return nil
}

func (o *LogOptions) Run() error {
jobsClient, err := batchv1client.NewForConfig(o.clientConfig)
if err != nil {
return err
}

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

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

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

jobs, err := tc.GetJob(tf)

if err != nil {
return err
}

if len(jobs) == 0 {
return fmt.Errorf("no trace found with the provided criterias")
}

job := jobs[0]

ctx := context.Background()
ctx = signals.WithStandardSignals(ctx)
nl := logs.NewLogs(client, o.IOStreams)
nl.WithContext(ctx)
nl.Run(job.ID, job.Namespace)
return nil
}
1 change: 1 addition & 0 deletions pkg/cmd/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ func NewTraceCommand(streams genericclioptions.IOStreams) *cobra.Command {
cmd.AddCommand(NewAttachCommand(f, streams))
cmd.AddCommand(NewDeleteCommand(f, streams))
cmd.AddCommand(NewVersionCommand(streams))
cmd.AddCommand(NewLogCommand(f, streams))

return cmd
}
94 changes: 94 additions & 0 deletions pkg/logs/logs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package logs

import (
"github.com/fntlnz/kubectl-trace/pkg/meta"
tcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/rest"

"fmt"
"io"

"context"

"k8s.io/cli-runtime/pkg/genericclioptions"
)

type Logs struct {
genericclioptions.IOStreams
coreV1Client tcorev1.CoreV1Interface
ctx context.Context
}

func NewLogs(client tcorev1.CoreV1Interface, streams genericclioptions.IOStreams) *Logs {
return &Logs{
coreV1Client: client,
IOStreams: streams,
ctx: context.TODO(),
}
}

const (
podNotFoundError = "no trace found to get logs from with the given selector"
podPhaseNotAcceptedError = "cannot get logs from a completed trace; current phase is %s"
invalidPodContainersSizeError = "unexpected number of containers in trace job pod"
)

func (l *Logs) WithContext(c context.Context) {
l.ctx = c
}

func (l *Logs) Run(jobID types.UID, namespace string) error {
pl, err := l.coreV1Client.Pods(namespace).List(metav1.ListOptions{
LabelSelector: fmt.Sprintf("%s=%s", meta.TraceIDLabelKey, jobID),
})

if err != nil {
return err
}

if len(pl.Items) == 0 {
return fmt.Errorf(podNotFoundError)
}

pod := &pl.Items[0]
if pod.Status.Phase == corev1.PodSucceeded || pod.Status.Phase == corev1.PodFailed {
return fmt.Errorf(podPhaseNotAcceptedError, pod.Status.Phase)
}

if len(pod.Spec.Containers) != 1 {
return fmt.Errorf(invalidPodContainersSizeError)
}

containerName := pod.Spec.Containers[0].Name

// TODO(fntlnz): let the user choose to follow or not
logOptions := &corev1.PodLogOptions{
Container: containerName,
Follow: true,
Previous: false,
Timestamps: false,
}

logsRequest := l.coreV1Client.Pods(namespace).GetLogs(pod.Name, logOptions)

go consumeRequest(logsRequest, l.IOStreams.Out)
<-l.ctx.Done()

return nil
}

func consumeRequest(request *rest.Request, out io.Writer) error {
readCloser, err := request.Stream()
if err != nil {
return err
}
defer readCloser.Close()

_, err = io.Copy(out, readCloser)
return err
}

25 changes: 23 additions & 2 deletions vendor/golang.org/x/net/http2/transport.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions vendor/modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ github.com/spf13/cobra
github.com/spf13/pflag
# golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869
golang.org/x/crypto/ssh/terminal
# golang.org/x/net v0.0.0-20181114220301-adae6a3d119a
# golang.org/x/net v0.0.0-20181201002055-351d144fa1fc
golang.org/x/net/http2
golang.org/x/net/http/httpguts
golang.org/x/net/http2/hpack
Expand Down Expand Up @@ -273,7 +273,7 @@ k8s.io/client-go/tools/clientcmd/api/v1
# k8s.io/kube-openapi v0.0.0-20181114233023-0317810137be
k8s.io/kube-openapi/pkg/util/proto
k8s.io/kube-openapi/pkg/util/proto/validation
# k8s.io/kubernetes v1.12.2
# k8s.io/kubernetes v1.12.3
k8s.io/kubernetes/pkg/kubectl/util/term
k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi
k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi/validation
Expand Down

0 comments on commit 8a59c03

Please sign in to comment.