diff --git a/cmd/root.go b/cmd/root.go index ec85af8..ba37128 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -17,6 +17,7 @@ func init() { rootCommand.AddCommand(installCmd) rootCommand.AddCommand(makeProviderCmd()) rootCommand.AddCommand(collectCmd) + rootCommand.AddCommand(makeServiceCmd()) } func RootCommand() *cobra.Command { diff --git a/cmd/service.go b/cmd/service.go new file mode 100644 index 0000000..fcfd55d --- /dev/null +++ b/cmd/service.go @@ -0,0 +1,22 @@ +package cmd + +import "github.com/spf13/cobra" + +func makeServiceCmd() *cobra.Command { + var command = &cobra.Command{ + Use: "service", + Short: "Manage services", + Long: `Manage services created by faasd from the docker-compose.yml file`, + } + + command.RunE = runServiceE + + command.AddCommand(makeServiceLogsCmd()) + return command +} + +func runServiceE(cmd *cobra.Command, args []string) error { + + return cmd.Help() + +} diff --git a/cmd/service_logs.go b/cmd/service_logs.go new file mode 100644 index 0000000..1a1f3a4 --- /dev/null +++ b/cmd/service_logs.go @@ -0,0 +1,89 @@ +package cmd + +import ( + "context" + "errors" + "fmt" + "os" + "time" + + goexecute "github.com/alexellis/go-execute/v2" + "github.com/spf13/cobra" +) + +func makeServiceLogsCmd() *cobra.Command { + var command = &cobra.Command{ + Use: "logs", + Short: "View logs for a service", + Long: `View logs for a service created by faasd from the docker-compose.yml file.`, + Example: ` ## View logs for the gateway for the last hour + faasd service logs gateway --since 1h + + ## View logs for the cron-connector, and tail them + faasd service logs cron-connector -f +`, + } + + command.Flags().Duration("since", 10*time.Minute, "How far back in time to include logs") + command.Flags().BoolP("follow", "f", false, "Follow the logs") + + command.RunE = runServiceLogsE + command.PreRunE = preRunServiceLogsE + + return command +} + +func runServiceLogsE(cmd *cobra.Command, args []string) error { + name := args[0] + + namespace, _ := cmd.Flags().GetString("namespace") + follow, _ := cmd.Flags().GetBool("follow") + since, _ := cmd.Flags().GetDuration("since") + + journalTask := goexecute.ExecTask{ + Command: "journalctl", + Args: []string{"-o", "cat", "-t", fmt.Sprintf("%s:%s", namespace, name)}, + StreamStdio: true, + } + + if follow { + journalTask.Args = append(journalTask.Args, "-f") + } + + if since != 0 { + // Calculate the timestamp that is 'age' duration ago + sinceTime := time.Now().Add(-since) + // Format according to journalctl's expected format: "2012-10-30 18:17:16" + formattedTime := sinceTime.Format("2006-01-02 15:04:05") + journalTask.Args = append(journalTask.Args, fmt.Sprintf("--since=%s", formattedTime)) + } + + res, err := journalTask.Execute(context.Background()) + if err != nil { + return err + } + + if res.ExitCode != 0 { + return fmt.Errorf("failed to get logs for service %s: %s", name, res.Stderr) + } + + return nil +} + +func preRunServiceLogsE(cmd *cobra.Command, args []string) error { + + if os.Geteuid() != 0 { + return errors.New("this command must be run as root") + } + + if len(args) == 0 { + return errors.New("service name is required as an argument") + } + + namespace, _ := cmd.Flags().GetString("namespace") + if namespace == "" { + return errors.New("namespace is required") + } + + return nil +}