Skip to content

Commit 33e6050

Browse files
authored
feat(service): Introduce helper to view instance logs in service (#2199)
Reviewed-by: Cezar Craciunoiu <cezar.craciunoiu@unikraft.io> Approved-by: Cezar Craciunoiu <cezar.craciunoiu@unikraft.io>
2 parents 513f68d + 00ddcdf commit 33e6050

File tree

2 files changed

+140
-0
lines changed

2 files changed

+140
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
// Copyright (c) 2025, Unikraft GmbH and The KraftKit Authors.
3+
// Licensed under the BSD-3-Clause License (the "License").
4+
// You may not use this file except in compliance with the License.
5+
6+
package logs
7+
8+
import (
9+
"context"
10+
"fmt"
11+
12+
"github.com/MakeNowJust/heredoc"
13+
"github.com/spf13/cobra"
14+
15+
kraftcloud "sdk.kraft.cloud"
16+
17+
"kraftkit.sh/cmdfactory"
18+
"kraftkit.sh/config"
19+
"kraftkit.sh/internal/cli/kraft/cloud/instance/logs"
20+
"kraftkit.sh/internal/cli/kraft/cloud/utils"
21+
)
22+
23+
type LogOptions struct {
24+
Auth *config.AuthConfig `noattribute:"true"`
25+
Client kraftcloud.KraftCloud `noattribute:"true"`
26+
Follow bool `local:"true" long:"follow" short:"f" usage:"Follow the logs of the service every half second" default:"false"`
27+
Metro string `noattribute:"true"`
28+
NoPrefix bool `long:"no-prefix" usage:"When logging multiple machines, do not prefix each log line with the name"`
29+
Tail int `local:"true" long:"tail" short:"n" usage:"Show the last given lines from the logs" default:"-1"`
30+
Token string `noattribute:"true"`
31+
}
32+
33+
// Log retrieves the console output from a Unikraft Cloud service.
34+
func Log(ctx context.Context, opts *LogOptions, args ...string) error {
35+
if opts == nil {
36+
opts = &LogOptions{}
37+
}
38+
39+
return opts.Run(ctx, args)
40+
}
41+
42+
func NewCmd() *cobra.Command {
43+
cmd, err := cmdfactory.New(&LogOptions{}, cobra.Command{
44+
Short: "Get console output for services",
45+
Use: "logs [FLAG] UUID|NAME",
46+
Args: cobra.MinimumNArgs(1),
47+
Aliases: []string{"log"},
48+
Example: heredoc.Doc(`
49+
# Get all console output of a service by UUID
50+
$ kraft cloud service logs 77d0316a-fbbe-488d-8618-5bf7a612477a
51+
52+
# Get all console output of a service by name
53+
$ kraft cloud service logs my-service-431342
54+
55+
# Get the last 20 lines of a service by name
56+
$ kraft cloud service logs my-service-431342 --tail 20
57+
58+
# Get the last lines of a service by name continuously
59+
$ kraft cloud service logs my-service-431342 --follow
60+
61+
# Get the last 10 lines of a service by name continuously
62+
$ kraft cloud service logs my-service-431342 --follow --tail 10
63+
`),
64+
Long: heredoc.Doc(`
65+
Get console output of an service.
66+
`),
67+
Annotations: map[string]string{
68+
cmdfactory.AnnotationHelpGroup: "kraftcloud-service",
69+
},
70+
})
71+
if err != nil {
72+
panic(err)
73+
}
74+
75+
return cmd
76+
}
77+
78+
func (opts *LogOptions) Pre(cmd *cobra.Command, _ []string) error {
79+
err := utils.PopulateMetroToken(cmd, &opts.Metro, &opts.Token)
80+
if err != nil {
81+
return fmt.Errorf("could not populate metro and token: %w", err)
82+
}
83+
84+
if opts.Tail < -1 {
85+
return fmt.Errorf("invalid value for --tail: %d, should be -1 for all logs, or positive for length of truncated logs", opts.Tail)
86+
}
87+
88+
return nil
89+
}
90+
91+
func (opts *LogOptions) Run(ctx context.Context, args []string) error {
92+
return Logs(ctx, opts, args...)
93+
}
94+
95+
func Logs(ctx context.Context, opts *LogOptions, args ...string) error {
96+
var err error
97+
98+
if opts.Auth == nil {
99+
opts.Auth, err = config.GetKraftCloudAuthConfig(ctx, opts.Token)
100+
if err != nil {
101+
return fmt.Errorf("could not retrieve credentials: %w", err)
102+
}
103+
}
104+
105+
if opts.Client == nil {
106+
opts.Client = kraftcloud.NewClient(
107+
kraftcloud.WithToken(config.GetKraftCloudTokenAuthConfig(*opts.Auth)),
108+
)
109+
}
110+
111+
var instances []string
112+
113+
for _, service := range args {
114+
resp, err := opts.Client.Services().WithMetro(opts.Metro).Get(ctx, service)
115+
if err != nil {
116+
return err
117+
}
118+
119+
item, err := resp.FirstOrErr()
120+
if err != nil {
121+
return err
122+
}
123+
124+
for _, instance := range item.Instances {
125+
instances = append(instances, instance.Name)
126+
}
127+
}
128+
129+
return logs.Logs(ctx, &logs.LogOptions{
130+
Auth: opts.Auth,
131+
Client: opts.Client,
132+
Follow: opts.Follow,
133+
Metro: opts.Metro,
134+
NoPrefix: opts.NoPrefix,
135+
Tail: opts.Tail,
136+
Token: opts.Token,
137+
}, instances...)
138+
}

internal/cli/kraft/cloud/service/service.go

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"kraftkit.sh/internal/cli/kraft/cloud/service/create"
1616
"kraftkit.sh/internal/cli/kraft/cloud/service/get"
1717
"kraftkit.sh/internal/cli/kraft/cloud/service/list"
18+
"kraftkit.sh/internal/cli/kraft/cloud/service/logs"
1819
"kraftkit.sh/internal/cli/kraft/cloud/service/remove"
1920

2021
"kraftkit.sh/cmdfactory"
@@ -44,6 +45,7 @@ func NewCmd() *cobra.Command {
4445
cmd.AddCommand(create.NewCmd())
4546
cmd.AddCommand(list.NewCmd())
4647
cmd.AddCommand(get.NewCmd())
48+
cmd.AddCommand(logs.NewCmd())
4749
cmd.AddCommand(remove.NewCmd())
4850

4951
return cmd

0 commit comments

Comments
 (0)