mirror of https://github.com/helm/helm
parent
5672d5d3ba
commit
ed5e59c4ff
@ -0,0 +1,186 @@
|
||||
package kube
|
||||
|
||||
import (
|
||||
"time"
|
||||
"k8s.io/kubernetes/pkg/kubectl/cmd"
|
||||
"io"
|
||||
"math"
|
||||
"errors"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||
"k8s.io/kubernetes/pkg/api/validation"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// Mimics kubectl logs functionality
|
||||
|
||||
var (
|
||||
selectorTail int64 = 10
|
||||
)
|
||||
|
||||
// All options that can be passed to kubectl logs
|
||||
type LogOptions struct {
|
||||
// Specify if the logs should be streamed.
|
||||
Follow bool
|
||||
// Include timestamps on each line in the log output
|
||||
Timestamps bool
|
||||
// Maximum bytes of logs to return. Defaults to no limit.
|
||||
LimitBytes int64
|
||||
// If true, print the logs for the previous instance of the container in a pod if it exists.
|
||||
Previous bool
|
||||
// Lines of recent log file to display. Defaults to -1 with no selector, showing all log lines otherwise 10, if a selector is provided.
|
||||
Tail int64
|
||||
// Only return logs after a specific date (RFC3339). Defaults to all logs. Only one of since-time / since may be used.
|
||||
SinceTime time.Time
|
||||
// Only return logs newer than a relative duration like 5s, 2m, or 3h. Defaults to all logs. Only one of since-time / since may be used.
|
||||
Since time.Duration
|
||||
// Print the logs of this container
|
||||
Container string
|
||||
// Selector (label query) to filter on.
|
||||
Selector string
|
||||
// Namespace to query for logs
|
||||
Namespace string
|
||||
// Resource to query for logs
|
||||
Resource string
|
||||
}
|
||||
|
||||
func NewOptions() *LogOptions {
|
||||
return &LogOptions{
|
||||
Follow: false,
|
||||
Timestamps: false,
|
||||
LimitBytes: 0,
|
||||
Previous: false,
|
||||
Tail: -1,
|
||||
SinceTime: nil,
|
||||
Since: nil,
|
||||
Container: "",
|
||||
Selector: "",
|
||||
Namespace: "",
|
||||
Resource: "",
|
||||
}
|
||||
}
|
||||
|
||||
func (o *LogOptions) ExecuteLogRequest(out io.Writer) {
|
||||
f := cmdutil.NewFactory(nil)
|
||||
Complete(o, f, out)
|
||||
}
|
||||
|
||||
func Complete(opts *LogOptions, f cmdutil.Factory, out io.Writer) (*cmd.LogsOptions, error) {
|
||||
o := &cmd.LogsOptions{}
|
||||
containerName := opts.Container
|
||||
selector := opts.Selector
|
||||
if len(opts.Resource) != 0 && len(opts.Selector) != 0 {
|
||||
return nil, errors.New("Specify either a selector or a resource, not both")
|
||||
}
|
||||
o.Namespace = opts.Namespace
|
||||
if o.Namespace == "" {
|
||||
return nil, errors.New("Namespace is required")
|
||||
}
|
||||
|
||||
logOptions := &api.PodLogOptions{
|
||||
Container: containerName,
|
||||
Follow: opts.Follow,
|
||||
Previous: opts.Previous,
|
||||
Timestamps: opts.Timestamps,
|
||||
}
|
||||
if opts.SinceTime {
|
||||
t := metav1.NewTime(opts.SinceTime)
|
||||
logOptions.SinceTime = &t
|
||||
}
|
||||
if opts.LimitBytes != 0 {
|
||||
logOptions.LimitBytes = &opts.LimitBytes
|
||||
}
|
||||
if opts.Tail != -1 {
|
||||
logOptions.TailLines = &opts.Tail
|
||||
}
|
||||
if opts.Since {
|
||||
// round up to the nearest second
|
||||
sec := int64(math.Ceil(opts.Since.Seconds()))
|
||||
logOptions.SinceSeconds = &sec
|
||||
}
|
||||
o.Options = logOptions
|
||||
o.LogsForObject = f.LogsForObject
|
||||
o.ClientMapper = resource.ClientMapperFunc(f.ClientForMapping)
|
||||
o.Out = out
|
||||
|
||||
if len(selector) != 0 {
|
||||
if logOptions.Follow {
|
||||
return nil, errors.New("only one of follow (-f) or selector (-l) is allowed")
|
||||
}
|
||||
if len(logOptions.Container) != 0 {
|
||||
return nil, errors.New( "a container cannot be specified when using a selector (-l)")
|
||||
}
|
||||
if logOptions.TailLines == nil {
|
||||
logOptions.TailLines = &selectorTail
|
||||
}
|
||||
}
|
||||
|
||||
mapper, typer := f.Object()
|
||||
decoder := f.Decoder(true)
|
||||
if o.Object == nil {
|
||||
builder := resource.NewBuilder(mapper, typer, o.ClientMapper, decoder).
|
||||
NamespaceParam(o.Namespace).DefaultNamespace().
|
||||
SingleResourceType()
|
||||
if o.ResourceArg != "" {
|
||||
builder.ResourceNames("pods", o.ResourceArg)
|
||||
}
|
||||
if selector != "" {
|
||||
builder.ResourceTypes("pods").SelectorParam(selector)
|
||||
}
|
||||
infos, err := builder.Do().Infos()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if selector == "" && len(infos) != 1 {
|
||||
return nil, errors.New("expected a resource")
|
||||
}
|
||||
o.Object = infos[0].Object
|
||||
}
|
||||
|
||||
return o, nil
|
||||
}
|
||||
|
||||
func Validate(o *cmd.LogsOptions) error {
|
||||
logsOptions, ok := o.Options.(*api.PodLogOptions)
|
||||
if !ok {
|
||||
return errors.New("unexpected logs options object")
|
||||
}
|
||||
if errs := validation.ValidatePodLogOptions(logsOptions); len(errs) > 0 {
|
||||
return errs.ToAggregate()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunLogs retrieves a pod log
|
||||
func RunLogs(o *cmd.LogsOptions) error {
|
||||
switch t := o.Object.(type) {
|
||||
case *api.PodList:
|
||||
for _, p := range t.Items {
|
||||
if err := getLogs(o, &p); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return getLogs(o, o.Object)
|
||||
}
|
||||
}
|
||||
|
||||
func getLogs(o *cmd.LogsOptions, obj runtime.Object) error {
|
||||
req, err := o.LogsForObject(obj, o.Options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
readCloser, err := req.Stream()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer readCloser.Close()
|
||||
|
||||
_, err = io.Copy(o.Out, readCloser)
|
||||
return err
|
||||
}
|
Loading…
Reference in new issue