2014-11-03 03:51:05 +00:00
/ *
2015-05-01 16:19:44 +00:00
Copyright 2014 The Kubernetes Authors All rights reserved .
2014-11-03 03:51:05 +00:00
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package cmd
import (
2015-10-07 14:59:08 +00:00
"errors"
2015-02-24 14:50:22 +00:00
"fmt"
2014-11-03 03:51:05 +00:00
"io"
2015-09-10 03:46:11 +00:00
"math"
2015-05-21 20:14:32 +00:00
"os"
2014-12-15 11:07:25 +00:00
"strconv"
2015-08-31 02:44:41 +00:00
"strings"
2015-09-10 03:46:11 +00:00
"time"
2014-12-03 12:15:48 +00:00
2015-08-05 22:05:17 +00:00
"github.com/spf13/cobra"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/api"
2015-09-10 03:46:11 +00:00
"k8s.io/kubernetes/pkg/api/unversioned"
2015-09-02 03:54:10 +00:00
client "k8s.io/kubernetes/pkg/client/unversioned"
2015-08-05 22:03:47 +00:00
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
2014-11-03 03:51:05 +00:00
)
2015-02-20 21:28:43 +00:00
const (
2015-09-10 03:46:11 +00:00
log_example = ` # Return snapshot logs from pod nginx with only one container
$ kubectl logs nginx
2014-12-15 11:07:25 +00:00
2015-09-10 03:46:11 +00:00
# Return snapshot of previous terminated ruby container logs from pod web - 1
$ kubectl logs - p - c ruby web - 1
2015-04-22 22:35:49 +00:00
2015-09-10 03:46:11 +00:00
# Begin streaming the logs of the ruby container in pod web - 1
$ kubectl logs - f - c ruby web - 1
# Display only the most recent 20 lines of output in pod nginx
$ kubectl logs -- tail = 20 nginx
# Show all logs from pod nginx written in the last hour
$ kubectl logs -- since = 1 h nginx `
2015-02-20 21:28:43 +00:00
)
2015-02-03 17:59:21 +00:00
2015-10-07 14:59:08 +00:00
type LogsOptions struct {
Client * client . Client
PodNamespace string
PodName string
ContainerName string
Follow bool
Timestamps bool
Previous bool
LimitBytes int
Tail int
SinceTime * unversioned . Time
SinceSeconds time . Duration
Out io . Writer
2015-06-30 00:48:00 +00:00
}
2015-04-23 18:11:42 +00:00
// NewCmdLog creates a new pod log command
2015-04-07 18:21:25 +00:00
func NewCmdLog ( f * cmdutil . Factory , out io . Writer ) * cobra . Command {
2015-10-07 14:59:08 +00:00
o := & LogsOptions {
Out : out ,
Tail : - 1 ,
}
2015-02-20 21:28:43 +00:00
cmd := & cobra . Command {
2015-06-30 00:48:00 +00:00
Use : "logs [-f] [-p] POD [-c CONTAINER]" ,
2015-02-20 21:28:43 +00:00
Short : "Print the logs for a container in a pod." ,
Long : "Print the logs for a container in a pod. If the pod has only one container, the container name is optional." ,
Example : log_example ,
2014-11-03 03:51:05 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2015-10-07 14:59:08 +00:00
if len ( os . Args ) > 1 && os . Args [ 1 ] == "log" {
printDeprecationWarning ( "logs" , "log" )
}
cmdutil . CheckErr ( o . Complete ( f , out , cmd , args ) )
if err := o . Validate ( ) ; err != nil {
cmdutil . CheckErr ( cmdutil . UsageError ( cmd , err . Error ( ) ) )
}
cmdutil . CheckErr ( o . RunLog ( ) )
2015-03-09 22:08:16 +00:00
} ,
2015-05-21 20:14:32 +00:00
Aliases : [ ] string { "log" } ,
2015-03-09 22:08:16 +00:00
}
2015-10-07 14:59:08 +00:00
cmd . Flags ( ) . BoolVarP ( & o . Follow , "follow" , "f" , o . Follow , "Specify if the logs should be streamed." )
cmd . Flags ( ) . BoolVar ( & o . Timestamps , "timestamps" , o . Timestamps , "Include timestamps on each line in the log output" )
2015-03-09 22:08:16 +00:00
cmd . Flags ( ) . Bool ( "interactive" , true , "If true, prompt the user for input when required. Default true." )
2015-10-07 14:59:08 +00:00
cmd . Flags ( ) . MarkDeprecated ( "interactive" , "This flag is no longer respected and there is no replacement." )
cmd . Flags ( ) . IntVar ( & o . LimitBytes , "limit-bytes" , o . LimitBytes , "Maximum bytes of logs to return. Defaults to no limit." )
cmd . Flags ( ) . BoolVarP ( & o . Previous , "previous" , "p" , o . Previous , "If true, print the logs for the previous instance of the container in a pod if it exists." )
cmd . Flags ( ) . IntVar ( & o . Tail , "tail" , o . Tail , "Lines of recent log file to display. Defaults to -1, showing all log lines." )
2015-09-10 03:46:11 +00:00
cmd . Flags ( ) . String ( "since-time" , "" , "Only return logs after a specific date (RFC3339). Defaults to all logs. Only one of since-time / since may be used." )
2015-10-07 14:59:08 +00:00
cmd . Flags ( ) . DurationVar ( & o . SinceSeconds , "since" , o . SinceSeconds , "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." )
cmd . Flags ( ) . StringVarP ( & o . ContainerName , "container" , "c" , o . ContainerName , "Container name" )
2015-03-09 22:08:16 +00:00
return cmd
}
2014-11-25 18:10:32 +00:00
2015-10-07 14:59:08 +00:00
func ( o * LogsOptions ) Complete ( f * cmdutil . Factory , out io . Writer , cmd * cobra . Command , args [ ] string ) error {
switch len ( args ) {
case 0 :
2015-04-07 18:21:25 +00:00
return cmdutil . UsageError ( cmd , "POD is required for log" )
2014-12-12 01:40:47 +00:00
2015-10-07 14:59:08 +00:00
case 1 :
o . PodName = args [ 0 ]
case 2 :
if cmd . Flag ( "container" ) . Changed {
return cmdutil . UsageError ( cmd , "only one of -c, [CONTAINER] arg is allowed" )
}
o . PodName = args [ 0 ]
o . ContainerName = args [ 1 ]
2015-03-09 22:08:16 +00:00
2015-10-07 14:59:08 +00:00
default :
return cmdutil . UsageError ( cmd , "log POD [-c CONTAINER]" )
2015-09-10 03:46:11 +00:00
}
2015-10-07 14:59:08 +00:00
var err error
o . PodNamespace , _ , err = f . DefaultNamespace ( )
2015-03-09 22:08:16 +00:00
if err != nil {
return err
}
2015-10-07 14:59:08 +00:00
o . Client , err = f . Client ( )
2015-03-09 22:08:16 +00:00
if err != nil {
return err
}
2014-12-11 18:39:20 +00:00
2015-10-07 14:59:08 +00:00
sinceTime := cmdutil . GetFlagString ( cmd , "since-time" )
if len ( sinceTime ) > 0 {
t , err := api . ParseRFC3339 ( sinceTime , unversioned . Now )
if err != nil {
return err
}
o . SinceTime = & t
}
return nil
}
func ( o * LogsOptions ) Validate ( ) error {
if len ( o . PodName ) == 0 {
return errors . New ( "POD must be specified" )
}
if o . LimitBytes < 0 {
return errors . New ( "--limit-bytes must be greater than or equal to zero" )
}
if o . Tail < - 1 {
return errors . New ( "--tail must be greater than or equal to -1" )
}
if o . SinceTime != nil && o . SinceSeconds > 0 {
return errors . New ( "only one of --since, --since-time may be specified" )
}
return nil
}
2014-12-11 18:39:20 +00:00
2015-10-07 14:59:08 +00:00
// RunLog retrieves a pod log
func ( o * LogsOptions ) RunLog ( ) error {
pod , err := o . Client . Pods ( o . PodNamespace ) . Get ( o . PodName )
2015-03-09 22:08:16 +00:00
if err != nil {
return err
}
2014-11-03 03:51:05 +00:00
2015-08-31 02:44:41 +00:00
// [-c CONTAINER]
2015-10-07 14:59:08 +00:00
container := o . ContainerName
2015-08-31 02:44:41 +00:00
if len ( container ) == 0 {
2015-06-30 00:48:00 +00:00
// [CONTAINER] (container as arg not flag) is supported as legacy behavior. See PR #10519 for more details.
2015-10-07 14:59:08 +00:00
if len ( pod . Spec . Containers ) != 1 {
podContainersNames := [ ] string { }
for _ , container := range pod . Spec . Containers {
podContainersNames = append ( podContainersNames , container . Name )
2015-06-30 00:48:00 +00:00
}
2015-10-07 14:59:08 +00:00
return fmt . Errorf ( "Pod %s has the following containers: %s; please specify the container to print logs for with -c" , pod . ObjectMeta . Name , strings . Join ( podContainersNames , ", " ) )
2015-06-30 00:48:00 +00:00
}
2015-10-07 14:59:08 +00:00
container = pod . Spec . Containers [ 0 ] . Name
2015-03-09 22:08:16 +00:00
}
2014-11-03 03:51:05 +00:00
2015-09-10 03:46:11 +00:00
logOptions := & api . PodLogOptions {
Container : container ,
2015-10-07 14:59:08 +00:00
Follow : o . Follow ,
Previous : o . Previous ,
Timestamps : o . Timestamps ,
2014-11-03 03:51:05 +00:00
}
2015-10-07 14:59:08 +00:00
if o . SinceSeconds > 0 {
2015-09-10 03:46:11 +00:00
// round up to the nearest second
2015-10-07 14:59:08 +00:00
sec := int64 ( math . Ceil ( float64 ( o . SinceSeconds ) / float64 ( time . Second ) ) )
2015-09-10 03:46:11 +00:00
logOptions . SinceSeconds = & sec
}
2015-10-07 14:59:08 +00:00
logOptions . SinceTime = o . SinceTime
if o . LimitBytes != 0 {
i := int64 ( o . LimitBytes )
2015-09-10 03:46:11 +00:00
logOptions . LimitBytes = & i
2015-04-22 22:35:49 +00:00
}
2015-10-07 14:59:08 +00:00
if o . Tail >= 0 {
i := int64 ( o . Tail )
2015-09-10 03:46:11 +00:00
logOptions . TailLines = & i
}
2015-10-07 14:59:08 +00:00
return handleLog ( o . Client , o . PodNamespace , o . PodName , logOptions , o . Out )
2015-09-02 03:54:10 +00:00
}
2015-04-22 22:35:49 +00:00
2015-09-10 03:46:11 +00:00
func handleLog ( client * client . Client , namespace , podID string , logOptions * api . PodLogOptions , out io . Writer ) error {
// TODO: transform this into a PodLogOptions call
req := client . RESTClient . Get ( ) .
2015-04-23 18:11:42 +00:00
Namespace ( namespace ) .
Name ( podID ) .
Resource ( "pods" ) .
SubResource ( "log" ) .
2015-09-10 03:46:11 +00:00
Param ( "follow" , strconv . FormatBool ( logOptions . Follow ) ) .
Param ( "container" , logOptions . Container ) .
Param ( "previous" , strconv . FormatBool ( logOptions . Previous ) ) .
Param ( "timestamps" , strconv . FormatBool ( logOptions . Timestamps ) )
if logOptions . SinceSeconds != nil {
req . Param ( "sinceSeconds" , strconv . FormatInt ( * logOptions . SinceSeconds , 10 ) )
}
if logOptions . SinceTime != nil {
req . Param ( "sinceTime" , logOptions . SinceTime . Format ( time . RFC3339 ) )
}
if logOptions . LimitBytes != nil {
req . Param ( "limitBytes" , strconv . FormatInt ( * logOptions . LimitBytes , 10 ) )
}
if logOptions . TailLines != nil {
req . Param ( "tailLines" , strconv . FormatInt ( * logOptions . TailLines , 10 ) )
}
readCloser , err := req . Stream ( )
2015-03-09 22:08:16 +00:00
if err != nil {
return err
}
defer readCloser . Close ( )
_ , err = io . Copy ( out , readCloser )
return err
2014-11-03 03:51:05 +00:00
}