Permalink
Browse files

Merge branch 'master' into feature/support-init-container-tail-and-co…

…ntainer-state
  • Loading branch information...
shun0309 committed Jun 26, 2018
2 parents 43ac51a + 009d952 commit ae2c992a9b50924bafa0455a2a67f6d387cb75e7
Showing with 244 additions and 18 deletions.
  1. +45 −0 CONTRIBUTING.md
  2. +64 −4 README.md
  3. +50 −1 cmd/cli.go
  4. +1 −0 kubernetes/clientset.go
  5. +2 −0 stern/config.go
  6. +1 −1 stern/main.go
  7. +39 −12 stern/tail.go
  8. +42 −0 vendor/vendor.json
View
@@ -0,0 +1,45 @@
# Contributing
Oracle welcomes contributions to this repository from anyone.
If you want to submit a pull request to fix a bug or enhance an existing
feature, please first open an issue and link to that issue when you
submit your pull request.
If you have any questions about a possible submission, feel free to open
an issue too.
## Contributing to the Wercker repository
Pull requests can be made under
[The Oracle Contributor Agreement](https://www.oracle.com/technetwork/community/oca-486395.html) (OCA).
For pull requests to be accepted, the bottom of your commit message must have
the following line using your name and e-mail address as it appears in the
OCA Signatories list.
```
Signed-off-by: Your Name <you@example.org>
```
This can be automatically added to pull requests by committing with:
```
git commit --signoff
```
Only pull requests from committers that can be verified as having
signed the OCA can be accepted.
### Pull request process
1. Fork this repository
1. Create a branch in your fork to implement the changes. We recommend using
the issue number as part of your branch name, e.g. `1234-fixes`
1. Ensure that any documentation is updated with the changes that are required
by your fix.
1. Ensure that any samples are updated if the base image has been changed.
1. Submit the pull request. *Do not leave the pull request blank*. Explain exactly
what your changes are meant to do and provide simple steps on how to validate
your changes. Ensure that you reference the issue you created as well.
We will assign the pull request to 2-3 people for review before it is merged.
View
@@ -7,7 +7,7 @@ within the pod. Each result is color coded for quicker debugging.
The query is a regular expression so the pod name can easily be filtered and
you don't need to specify the exact id (for instance omitting the deployment
id). If a pod is deleted it gets removed from tail and if a new is added it
id). If a pod is deleted it gets removed from tail and if a new pod is added it
automatically gets tailed.
When a pod contains multiple containers Stern can tail all of them too without
@@ -60,13 +60,45 @@ The `pod` query is a regular expression so you could provide `"web-\w"` to tail
| `--selector` | | Selector (label query) to filter on. If present, default to `.*` for the pod-query. |
| `--tail` | `-1` | The number of lines from the end of the logs to show. Defaults to -1, showing all logs. |
| `--color` | `auto` | Force set color output. `auto`: colorize if tty attached, `always`: always colorize, `never`: never colorize |
| `--output` | `default` | Specify predefined template. Currently support: [default, raw, json] See templates section |
| `template` | | Template to use for log lines, leave empty to use --output flag |
See `stern --help` for details
Stern will use the `$KUBECONFIG` environment variable if set. If both the
environment variable and `--kubeconfig` flag are passed the cli flag will be
used.
### templates
stern supports outputting custom log messages. There are a few predefined
templates which you can use by specifying the `--output` flag:
| output | description |
|-----------|-------------------------------------------------------------------------------------------------------|
| `default` | Displays the namespace, pod and container, and decorates it with color depending on --color |
| `raw` | Only outputs the log message itself, useful when your logs are json and you want to pipe them to `jq` |
| `json` | Marshals the log struct to json. Useful for programmatic purposes |
It accepts a custom template through the `--template` flag, which will be
compiled to a Go template and then used for every log message. This Go template
will receive the following struct:
| property | type | description |
|-----------------|--------|---------------------------|
| `Message` | string | The log message itself |
| `Namespace` | string | The namespace of the pod |
| `PodName` | string | The name of the pod |
| `ContainerName` | string | The name of the container |
The following functions are available within the template (besides the [builtin
functions](https://golang.org/pkg/text/template/#hdr-Functions)):
| func | arguments | description |
|---------|-----------------------|-------------------------------------------------|
| `json` | `object` | Marshal the object and output it as a json text |
| `color` | `color.Color, string` | Wrap the text in color |
## Examples:
Tail the `gateway` container running inside of the `envvars` pod on staging
@@ -99,17 +131,40 @@ Follow the `frontend` pods in canary release
stern frontend --selector release=canary
```
Pipe the log message to jq:
```
stern backend -o json | jq .
```
Only output the log message itself:
```
stern backend -o raw
```
Output using a custom template:
```
stern --template '{{.Message}} ({{.Namespace}}/{{.PodName}}/{{.ContainerName}})' backend
```
## Completion
Stern supports command-line auto completion for bash or zsh. `stern --completion=(bash|zsh)` outputs the shell completion code which work by being evaluated in `.bashrc`, etc for the specified shell. In addition, Stern supports dynamic completion for `--namespace` and `--context`. In order to use that, kubectl must be installed on your environment.
Stern supports command-line auto completion for bash or zsh. `stern
--completion=(bash|zsh)` outputs the shell completion code which work by being
evaluated in `.bashrc`, etc for the specified shell. In addition, Stern
supports dynamic completion for `--namespace` and `--context`. In order to use
that, kubectl must be installed on your environment.
If you use bash, stern bash completion code depends on the [bash-completion](https://github.com/scop/bash-completion). On the macOS, you can install it with homebrew as follows:
If you use bash, stern bash completion code depends on the
[bash-completion](https://github.com/scop/bash-completion). On the macOS, you
can install it with homebrew as follows:
```
$ brew install bash-completion
```
Note that bash-completion must be sourced before sourcing the stern bash completion code in `.bashrc`.
Note that bash-completion must be sourced before sourcing the stern bash
completion code in `.bashrc`.
```sh
source <(brew --prefix)/etc/bash-completion
@@ -121,3 +176,8 @@ If you use zsh, just source the stern zsh completion code in `.zshrc`.
```sh
source <(stern --completion=zsh)
```
## Contributing to this repository
Oracle welcomes contributions to this repository from anyone. Please see
[CONTRIBUTING](CONTRIBUTING.md) for details.
View
@@ -16,11 +16,13 @@ package cmd
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"regexp"
"text/template"
"time"
"k8s.io/apimachinery/pkg/labels"
@@ -33,7 +35,7 @@ import (
"github.com/fatih/color"
)
const version = "1.6.0"
const version = "1.7.0"
type Options struct {
container string
@@ -50,13 +52,17 @@ type Options struct {
color string
version bool
completion string
template string
output string
}
var opts = &Options{
container: ".*",
containerState: "running",
tail: -1,
color: "auto",
template: "",
output: "default",
}
func Run() {
@@ -80,6 +86,8 @@ func Run() {
cmd.Flags().StringVar(&opts.color, "color", opts.color, "Color output. Can be 'always', 'never', or 'auto'")
cmd.Flags().BoolVarP(&opts.version, "version", "v", opts.version, "Print the version and exit")
cmd.Flags().StringVar(&opts.completion, "completion", opts.completion, "Outputs stern command-line completion code for the specified shell. Can be 'bash' or 'zsh'")
cmd.Flags().StringVar(&opts.template, "template", opts.template, "Template to use for log lines, leave empty to use --output flag")
cmd.Flags().StringVarP(&opts.output, "output", "o", opts.output, "Specify predefined template. Currently support: [default, raw, json]")
// Specify custom bash completion function
cmd.BashCompletionFunction = bash_completion_func
@@ -195,6 +203,46 @@ func parseConfig(args []string) (*stern.Config, error) {
return nil, errors.New("color should be one of 'always', 'never', or 'auto'")
}
t := opts.template
if t == "" {
switch opts.output {
case "default":
if color.NoColor {
t = "{{.PodName}} {{.ContainerName}} {{.Message}}"
if opts.allNamespaces {
t = fmt.Sprintf("{{.Namespace}} %s", t)
}
} else {
t = "{{color .PodColor .PodName}} {{color .ContainerColor .ContainerName}} {{.Message}}"
if opts.allNamespaces {
t = fmt.Sprintf("{{color .PodColor .Namespace}} %s", t)
}
}
case "raw":
t = "{{.Message}}"
case "json":
t = "{{json .}}\n"
}
}
funs := map[string]interface{}{
"json": func(in interface{}) (string, error) {
b, err := json.Marshal(in)
if err != nil {
return "", err
}
return string(b), nil
},
"color": func(color color.Color, text string) string {
return color.SprintFunc()(text)
},
}
template, err := template.New("log").Funcs(funs).Parse(t)
if err != nil {
return nil, errors.Wrap(err, "unable to parse template")
}
return &stern.Config{
KubeConfig: kubeConfig,
PodQuery: pod,
@@ -208,6 +256,7 @@ func parseConfig(args []string) (*stern.Config, error) {
AllNamespaces: opts.allNamespaces,
LabelSelector: labelSelector,
TailLines: tailLines,
Template: template,
}, nil
}
View
@@ -22,6 +22,7 @@ import (
"k8s.io/client-go/tools/clientcmd"
// auth providers
_ "k8s.io/client-go/plugin/pkg/client/auth/azure"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
)
View
@@ -16,6 +16,7 @@ package stern
import (
"regexp"
"text/template"
"time"
"k8s.io/apimachinery/pkg/labels"
@@ -35,4 +36,5 @@ type Config struct {
AllNamespaces bool
LabelSelector labels.Selector
TailLines *int64
Template *template.Template
}
View
@@ -57,7 +57,7 @@ func Run(ctx context.Context, config *Config) error {
continue
}
tail := NewTail(p.Namespace, p.Pod, p.Container, &TailOptions{
tail := NewTail(p.Namespace, p.Pod, p.Container, config.Template, &TailOptions{
Timestamps: config.Timestamps,
SinceSeconds: int64(config.Since.Seconds()),
Exclude: config.Exclude,
View
@@ -18,11 +18,12 @@ import (
"bufio"
"context"
"fmt"
"os"
"regexp"
"text/template"
"github.com/fatih/color"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/rest"
@@ -37,6 +38,7 @@ type Tail struct {
closed chan struct{}
podColor *color.Color
containerColor *color.Color
tmpl *template.Template
}
type TailOptions struct {
@@ -48,13 +50,14 @@ type TailOptions struct {
}
// NewTail returns a new tail for a Kubernetes container inside a pod
func NewTail(namespace, podName, containerName string, options *TailOptions) *Tail {
func NewTail(namespace, podName, containerName string, tmpl *template.Template, options *TailOptions) *Tail {
return &Tail{
Namespace: namespace,
PodName: podName,
ContainerName: containerName,
Options: options,
closed: make(chan struct{}),
tmpl: tmpl,
}
}
@@ -87,9 +90,9 @@ func (t *Tail) Start(ctx context.Context, i v1.PodInterface) {
p := t.podColor.SprintFunc()
c := t.containerColor.SprintFunc()
if t.Options.Namespace {
fmt.Printf("%s %s %s%s\n", g("+"), p(t.Namespace), p(t.PodName), c(t.ContainerName))
fmt.Fprintf(os.Stderr, "%s %s %s%s\n", g("+"), p(t.Namespace), p(t.PodName), c(t.ContainerName))
} else {
fmt.Printf("%s %s%s\n", g("+"), p(t.PodName), c(t.ContainerName))
fmt.Fprintf(os.Stderr, "%s %s%s\n", g("+"), p(t.PodName), c(t.ContainerName))
}
req := i.GetLogs(t.PodName, &corev1.PodLogOptions{
@@ -144,20 +147,44 @@ func (t *Tail) Close() {
r := color.New(color.FgHiRed, color.Bold).SprintFunc()
p := t.podColor.SprintFunc()
if t.Options.Namespace {
fmt.Printf("%s %s %s\n", r("-"), p(t.Namespace), p(t.PodName))
fmt.Fprintf(os.Stderr, "%s %s %s\n", r("-"), p(t.Namespace), p(t.PodName))
} else {
fmt.Printf("%s %s\n", r("-"), p(t.PodName))
fmt.Fprintf(os.Stderr, "%s %s\n", r("-"), p(t.PodName))
}
close(t.closed)
}
// Print prints a color coded log message with the pod and container names
func (t *Tail) Print(msg string) {
p := t.podColor.SprintFunc()
c := t.containerColor.SprintFunc()
if t.Options.Namespace {
fmt.Printf("%s %s %s %s", p(t.Namespace), p(t.PodName), c(t.ContainerName), msg)
} else {
fmt.Printf("%s %s %s", p(t.PodName), c(t.ContainerName), msg)
vm := Log{
Message: msg,
Namespace: t.Namespace,
PodName: t.PodName,
ContainerName: t.ContainerName,
PodColor: t.podColor,
ContainerColor: t.containerColor,
}
err := t.tmpl.Execute(os.Stdout, vm)
if err != nil {
os.Stderr.WriteString(fmt.Sprintf("expanding template failed: %s", err))
}
}
// Log is the object which will be used together with the template to generate
// the output.
type Log struct {
// Message is the log message itself
Message string `json:"message"`
// Namespace of the pod
Namespace string `json:"namespace"`
// PodName of the pod
PodName string `json:"podName"`
// ContainerName of the container
ContainerName string `json:"containerName"`
PodColor *color.Color `json:"-"`
ContainerColor *color.Color `json:"-"`
}
Oops, something went wrong.

0 comments on commit ae2c992

Please sign in to comment.