Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add logs flags --since and --since-time #1754

Merged
merged 2 commits into from
Aug 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 35 additions & 9 deletions cmd/flux/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@ import (
"os"
"strings"
"sync"
"time"

"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/kubectl/pkg/util"

"github.com/fluxcd/flux2/internal/flags"
"github.com/fluxcd/flux2/internal/utils"
Expand All @@ -42,16 +44,19 @@ var logsCmd = &cobra.Command{
Short: "Display formatted logs for Flux components",
Long: "The logs command displays formatted logs from various Flux components.",
Example: ` # Print the reconciliation logs of all Flux custom resources in your cluster
flux logs --all-namespaces
flux logs --all-namespaces

# Print all logs of all Flux custom resources newer than 2 minutes
flux logs --all-namespaces --since=2m

# Stream logs for a particular log level
flux logs --follow --level=error --all-namespaces
# Stream logs for a particular log level
flux logs --follow --level=error --all-namespaces

# Filter logs by kind, name and namespace
flux logs --kind=Kustomization --name=podinfo --namespace=default
# Filter logs by kind, name and namespace
flux logs --kind=Kustomization --name=podinfo --namespace=default

# Print logs when Flux is installed in a different namespace than flux-system
flux logs --flux-namespace=my-namespace
# Print logs when Flux is installed in a different namespace than flux-system
flux logs --flux-namespace=my-namespace
`,
RunE: logsCmdRun,
}
Expand All @@ -64,6 +69,8 @@ type logsFlags struct {
name string
fluxNamespace string
allNamespaces bool
sinceTime string
sinceSeconds time.Duration
}

var logsArgs = &logsFlags{
Expand All @@ -78,6 +85,8 @@ func init() {
logsCmd.Flags().Int64VarP(&logsArgs.tail, "tail", "", logsArgs.tail, "lines of recent log file to display")
logsCmd.Flags().StringVarP(&logsArgs.fluxNamespace, "flux-namespace", "", rootArgs.defaults.Namespace, "the namespace where the Flux components are running")
logsCmd.Flags().BoolVarP(&logsArgs.allNamespaces, "all-namespaces", "A", false, "displays logs for objects across all namespaces")
logsCmd.Flags().DurationVar(&logsArgs.sinceSeconds, "since", logsArgs.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.")
logsCmd.Flags().StringVar(&logsArgs.sinceTime, "since-time", logsArgs.sinceTime, "Only return logs after a specific date (RFC3339). Defaults to all logs. Only one of since-time / since may be used.")
rootCmd.AddCommand(logsCmd)
}

Expand All @@ -87,7 +96,6 @@ func logsCmdRun(cmd *cobra.Command, args []string) error {
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
defer cancel()

var pods []corev1.Pod
cfg, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
if err != nil {
return err
Expand All @@ -102,7 +110,7 @@ func logsCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf("no argument required")
}

pods, err = getPods(ctx, clientset, fluxSelector)
pods, err := getPods(ctx, clientset, fluxSelector)
if err != nil {
return err
}
Expand All @@ -115,6 +123,24 @@ func logsCmdRun(cmd *cobra.Command, args []string) error {
logOpts.TailLines = &logsArgs.tail
}

if len(logsArgs.sinceTime) > 0 && logsArgs.sinceSeconds != 0 {
return fmt.Errorf("at most one of `sinceTime` or `sinceSeconds` may be specified")
}

if len(logsArgs.sinceTime) > 0 {
t, err := util.ParseRFC3339(logsArgs.sinceTime, metav1.Now)
if err != nil {
return fmt.Errorf("%s is not a valid (RFC3339) time", logsArgs.sinceTime)
}
logOpts.SinceTime = &t
}

if logsArgs.sinceSeconds != 0 {
// round up to the nearest second
sec := int64(logsArgs.sinceSeconds.Round(time.Second).Seconds())
logOpts.SinceSeconds = &sec
}

var requests []rest.ResponseWrapper
for _, pod := range pods {
req := clientset.CoreV1().Pods(logsArgs.fluxNamespace).GetLogs(pod.Name, logOpts)
Expand Down
63 changes: 63 additions & 0 deletions cmd/flux/logs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// +build unit

package main

import (
"testing"
)

func TestLogsNoArgs(t *testing.T) {
cmd := cmdTestCase{
args: "logs",
assert: assertSuccess(),
}
cmd.runTestCmd(t)
}

func TestLogsAllNamespaces(t *testing.T) {
cmd := cmdTestCase{
args: "logs --all-namespaces",
assert: assertSuccess(),
}
cmd.runTestCmd(t)
}

func TestLogsSince(t *testing.T) {
cmd := cmdTestCase{
args: "logs --since=2m",
assert: assertSuccess(),
}
cmd.runTestCmd(t)
}

func TestLogsSinceInvalid(t *testing.T) {
cmd := cmdTestCase{
args: "logs --since=XXX",
assert: assertError(`invalid argument "XXX" for "--since" flag: time: invalid duration "XXX"`),
}
cmd.runTestCmd(t)
}

func TestLogsSinceTime(t *testing.T) {
cmd := cmdTestCase{
args: "logs --since-time=2021-08-06T14:26:25.546Z",
assert: assertSuccess(),
}
cmd.runTestCmd(t)
}

func TestLogsSinceTimeInvalid(t *testing.T) {
cmd := cmdTestCase{
args: "logs --since-time=XXX",
assert: assertError("XXX is not a valid (RFC3339) time"),
}
cmd.runTestCmd(t)
}

func TestLogsSinceOnlyOneAllowed(t *testing.T) {
cmd := cmdTestCase{
args: "logs --since=2m --since-time=2021-08-06T14:26:25.546Z",
assert: assertError("at most one of `sinceTime` or `sinceSeconds` may be specified"),
}
cmd.runTestCmd(t)
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ require (
k8s.io/apiextensions-apiserver v0.21.3
k8s.io/apimachinery v0.21.3
k8s.io/client-go v0.21.3
k8s.io/kubectl v0.21.1
sigs.k8s.io/cli-utils v0.25.1-0.20210608181808-f3974341173a
sigs.k8s.io/controller-runtime v0.9.5
sigs.k8s.io/kustomize/api v0.8.10
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1235,6 +1235,7 @@ k8s.io/kube-openapi v0.0.0-20200410145947-61e04a5be9a6/go.mod h1:GRQhZsXIAJ1xR0C
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e h1:KLHHjkdQFomZy8+06csTWZ0m1343QqxZhR2LJ1OxCYM=
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
k8s.io/kubectl v0.21.1 h1:ySEusoeSgSDSiSBncDMsNrthSa3OSlXqT4R2rf1VFTw=
k8s.io/kubectl v0.21.1/go.mod h1:PMYR88MqESuysBM/MX+Vu4JbX/50nY4d4kny+SPEI2U=
k8s.io/legacy-cloud-providers v0.18.8/go.mod h1:tgp4xYf6lvjrWnjQwTOPvWQE9IVqSBGPF4on0IyICQE=
k8s.io/metrics v0.21.1/go.mod h1:pyDVLsLe++FIGDBFU80NcW4xMFsuiVTWL8Zfi7+PpNo=
Expand Down