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-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"
2015-09-09 17:45:01 +00:00
"k8s.io/kubernetes/pkg/util/sets"
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-02-24 14:50:22 +00:00
func selectContainer ( pod * api . Pod , in io . Reader , out io . Writer ) string {
fmt . Fprintf ( out , "Please select a container:\n" )
2015-09-09 17:45:01 +00:00
options := sets . String { }
2015-02-24 14:50:22 +00:00
for ix := range pod . Spec . Containers {
fmt . Fprintf ( out , "[%d] %s\n" , ix + 1 , pod . Spec . Containers [ ix ] . Name )
options . Insert ( pod . Spec . Containers [ ix ] . Name )
}
for {
var input string
fmt . Fprintf ( out , "> " )
fmt . Fscanln ( in , & input )
if options . Has ( input ) {
return input
}
ix , err := strconv . Atoi ( input )
if err == nil && ix > 0 && ix <= len ( pod . Spec . Containers ) {
return pod . Spec . Containers [ ix - 1 ] . Name
}
fmt . Fprintf ( out , "Invalid input: %s" , input )
}
}
2015-06-30 00:48:00 +00:00
type logParams struct {
containerName string
}
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-06-30 00:48:00 +00:00
params := & logParams { }
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-06-30 00:48:00 +00:00
err := RunLog ( f , out , cmd , args , params )
2015-04-07 18:21:25 +00:00
cmdutil . CheckErr ( err )
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
}
cmd . Flags ( ) . BoolP ( "follow" , "f" , false , "Specify if the logs should be streamed." )
2015-09-10 03:46:11 +00:00
cmd . Flags ( ) . Bool ( "timestamps" , false , "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-04-22 22:35:49 +00:00
cmd . Flags ( ) . BoolP ( "previous" , "p" , false , "If true, print the logs for the previous instance of the container in a pod if it exists." )
2015-09-10 03:46:11 +00:00
cmd . Flags ( ) . Int ( "limit-bytes" , 0 , "Maximum bytes of logs to return. Defaults to no limit." )
cmd . Flags ( ) . Int ( "tail" , - 1 , "Lines of recent log file to display. Defaults to -1, showing all log lines." )
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." )
cmd . Flags ( ) . Duration ( "since" , 0 , "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." )
2015-06-30 00:48:00 +00:00
cmd . Flags ( ) . StringVarP ( & params . containerName , "container" , "c" , "" , "Container name" )
2015-03-09 22:08:16 +00:00
return cmd
}
2014-11-25 18:10:32 +00:00
2015-04-23 18:11:42 +00:00
// RunLog retrieves a pod log
2015-06-30 00:48:00 +00:00
func RunLog ( f * cmdutil . Factory , out io . Writer , cmd * cobra . Command , args [ ] string , p * logParams ) error {
2015-05-21 20:14:32 +00:00
if len ( os . Args ) > 1 && os . Args [ 1 ] == "log" {
printDeprecationWarning ( "logs" , "log" )
}
2015-03-09 22:08:16 +00:00
if len ( args ) == 0 {
2015-04-07 18:21:25 +00:00
return cmdutil . UsageError ( cmd , "POD is required for log" )
2015-03-09 22:08:16 +00:00
}
2014-12-12 01:40:47 +00:00
2015-03-09 22:08:16 +00:00
if len ( args ) > 2 {
2015-04-07 18:21:25 +00:00
return cmdutil . UsageError ( cmd , "log POD [CONTAINER]" )
2015-03-09 22:08:16 +00:00
}
2015-09-10 03:46:11 +00:00
sinceSeconds := cmdutil . GetFlagDuration ( cmd , "since" )
sinceTime := cmdutil . GetFlagString ( cmd , "since-time" )
if len ( sinceTime ) > 0 && sinceSeconds > 0 {
return cmdutil . UsageError ( cmd , "only one of --since, --since-time may be specified" )
}
2015-06-26 20:49:34 +00:00
namespace , _ , err := f . DefaultNamespace ( )
2015-03-09 22:08:16 +00:00
if err != nil {
return err
}
2015-03-14 10:45:18 +00:00
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-03-09 22:08:16 +00:00
podID := args [ 0 ]
2014-12-11 18:39:20 +00:00
2015-03-09 22:08:16 +00:00
pod , err := client . Pods ( namespace ) . Get ( podID )
if err != nil {
return err
}
2014-11-03 03:51:05 +00:00
2015-08-31 02:44:41 +00:00
// [-c CONTAINER]
container := p . containerName
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.
if len ( args ) == 1 {
if len ( pod . Spec . Containers ) != 1 {
2015-08-31 02:44:41 +00:00
podContainersNames := [ ] string { }
for _ , container := range pod . Spec . Containers {
podContainersNames = append ( podContainersNames , container . Name )
}
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
}
container = pod . Spec . Containers [ 0 ] . Name
} else {
container = args [ 1 ]
}
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 ,
Follow : cmdutil . GetFlagBool ( cmd , "follow" ) ,
Previous : cmdutil . GetFlagBool ( cmd , "previous" ) ,
Timestamps : cmdutil . GetFlagBool ( cmd , "timestamps" ) ,
2014-11-03 03:51:05 +00:00
}
2015-09-10 03:46:11 +00:00
if sinceSeconds > 0 {
// round up to the nearest second
sec := int64 ( math . Ceil ( float64 ( sinceSeconds ) / float64 ( time . Second ) ) )
logOptions . SinceSeconds = & sec
}
if t , err := api . ParseRFC3339 ( sinceTime , unversioned . Now ) ; err == nil {
logOptions . SinceTime = & t
}
if limitBytes := cmdutil . GetFlagInt ( cmd , "limit-bytes" ) ; limitBytes != 0 {
i := int64 ( limitBytes )
logOptions . LimitBytes = & i
2015-04-22 22:35:49 +00:00
}
2015-09-10 03:46:11 +00:00
if tail := cmdutil . GetFlagInt ( cmd , "tail" ) ; tail >= 0 {
i := int64 ( tail )
logOptions . TailLines = & i
}
return handleLog ( client , namespace , podID , logOptions , 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
}