add --since and --since-time
Signed-off-by: Tomas Tulka <tomas.tulka@gmail.com>
This commit is contained in:
@@ -26,12 +26,14 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/client-go/kubernetes"
|
"k8s.io/client-go/kubernetes"
|
||||||
"k8s.io/client-go/rest"
|
"k8s.io/client-go/rest"
|
||||||
|
"k8s.io/kubectl/pkg/util"
|
||||||
|
|
||||||
"github.com/fluxcd/flux2/internal/flags"
|
"github.com/fluxcd/flux2/internal/flags"
|
||||||
"github.com/fluxcd/flux2/internal/utils"
|
"github.com/fluxcd/flux2/internal/utils"
|
||||||
@@ -44,6 +46,9 @@ var logsCmd = &cobra.Command{
|
|||||||
Example: ` # Print the reconciliation logs of all Flux custom resources in your cluster
|
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
|
# Stream logs for a particular log level
|
||||||
flux logs --follow --level=error --all-namespaces
|
flux logs --follow --level=error --all-namespaces
|
||||||
|
|
||||||
@@ -64,6 +69,8 @@ type logsFlags struct {
|
|||||||
name string
|
name string
|
||||||
fluxNamespace string
|
fluxNamespace string
|
||||||
allNamespaces bool
|
allNamespaces bool
|
||||||
|
sinceTime string
|
||||||
|
sinceSeconds time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
var logsArgs = &logsFlags{
|
var logsArgs = &logsFlags{
|
||||||
@@ -78,6 +85,8 @@ func init() {
|
|||||||
logsCmd.Flags().Int64VarP(&logsArgs.tail, "tail", "", logsArgs.tail, "lines of recent log file to display")
|
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().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().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)
|
rootCmd.AddCommand(logsCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +96,6 @@ func logsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), rootArgs.timeout)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
var pods []corev1.Pod
|
|
||||||
cfg, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
|
cfg, err := utils.KubeConfig(rootArgs.kubeconfig, rootArgs.kubecontext)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -102,7 +110,7 @@ func logsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
return fmt.Errorf("no argument required")
|
return fmt.Errorf("no argument required")
|
||||||
}
|
}
|
||||||
|
|
||||||
pods, err = getPods(ctx, clientset, fluxSelector)
|
pods, err := getPods(ctx, clientset, fluxSelector)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -115,6 +123,24 @@ func logsCmdRun(cmd *cobra.Command, args []string) error {
|
|||||||
logOpts.TailLines = &logsArgs.tail
|
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
|
var requests []rest.ResponseWrapper
|
||||||
for _, pod := range pods {
|
for _, pod := range pods {
|
||||||
req := clientset.CoreV1().Pods(logsArgs.fluxNamespace).GetLogs(pod.Name, logOpts)
|
req := clientset.CoreV1().Pods(logsArgs.fluxNamespace).GetLogs(pod.Name, logOpts)
|
||||||
|
|||||||
63
cmd/flux/logs_test.go
Normal file
63
cmd/flux/logs_test.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user