Skip to content

Commit

Permalink
improve log displaying and fix nil panic
Browse files Browse the repository at this point in the history
This improves the live tailing of the logs during application builds by updating the authorization token before every log call. Before we were using the token which was created when starting nctl. It furthermore fixes a `nil` pointer access.
  • Loading branch information
thirdeyenick committed Sep 25, 2024
1 parent 763d46a commit ffa014d
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 16 deletions.
2 changes: 1 addition & 1 deletion api/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func New(ctx context.Context, apiClusterContext, project string, opts ...ClientO
// LogClient sets up a log client connected to the provided address.
func LogClient(ctx context.Context, address string, insecure bool) ClientOpt {
return func(c *Client) error {
logClient, err := log.NewClient(address, c.Token(ctx), c.Project, insecure)
logClient, err := log.NewClient(address, func(ctx context.Context) string { return c.Token(ctx) }, c.Project, insecure)
if err != nil {
return fmt.Errorf("unable to create log client: %w", err)
}
Expand Down
46 changes: 35 additions & 11 deletions api/log/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ import (
"k8s.io/client-go/util/retry"
)

type tokenFunc func(ctx context.Context) string

type Client struct {
bearerTokenFunc tokenFunc
logclient.Client
StdOut output.LogOutput
}
Expand All @@ -38,7 +41,7 @@ type Query struct {
}

// NewClient returns a new log API client.
func NewClient(address, token, orgID string, insecure bool) (*Client, error) {
func NewClient(address string, tokenFunc tokenFunc, orgID string, insecure bool) (*Client, error) {
out, err := StdOut("default")
if err != nil {
return nil, err
Expand All @@ -50,12 +53,12 @@ func NewClient(address, token, orgID string, insecure bool) (*Client, error) {
}

return &Client{
StdOut: out,
bearerTokenFunc: tokenFunc,
StdOut: out,
Client: &logclient.DefaultClient{
Address: address,
BearerToken: token,
OrgID: orgID,
TLSConfig: tls,
Address: address,
OrgID: orgID,
TLSConfig: tls,
},
}, nil
}
Expand All @@ -82,8 +85,18 @@ func StdOut(mode string) (output.LogOutput, error) {
return out, nil
}

// QueryRange queries logs within a specific time range.
func (c *Client) refreshToken(ctx context.Context) {
if c.bearerTokenFunc == nil {
return
}
if defaultClient, is := c.Client.(*logclient.DefaultClient); is {
defaultClient.BearerToken = c.bearerTokenFunc(ctx)
}
}

// QueryRange queries logs within a specific time range and prints the result.
func (c *Client) QueryRange(ctx context.Context, out output.LogOutput, q Query) error {
c.refreshToken(ctx)
resp, err := c.Client.QueryRange(q.QueryString, q.Limit, q.Start, q.End, q.Direction, q.Step, q.Interval, q.Quiet)
if err != nil {
return err
Expand All @@ -92,6 +105,12 @@ func (c *Client) QueryRange(ctx context.Context, out output.LogOutput, q Query)
return printResult(resp.Data.Result, out)
}

// QueryRangeResponse queries logs within a specific time range and returns the response.
func (c *Client) QueryRangeResponse(ctx context.Context, q Query) (*loghttp.QueryResponse, error) {
c.refreshToken(ctx)
return c.Client.QueryRange(q.QueryString, q.Limit, q.Start, q.End, q.Direction, q.Step, q.Interval, q.Quiet)
}

// QueryRangeWithRetry queries logs within a specific time range with a retry
// in case of an error or not finding any logs.
func (c *Client) QueryRangeWithRetry(ctx context.Context, out output.LogOutput, q Query) error {
Expand All @@ -108,7 +127,7 @@ func (c *Client) QueryRangeWithRetry(ctx context.Context, out output.LogOutput,
return true
},
func() error {
resp, err := c.Client.QueryRange(q.QueryString, q.Limit, q.Start, q.End, q.Direction, q.Step, q.Interval, q.Quiet)
resp, err := c.QueryRangeResponse(ctx, q)
if err != nil {
return err
}
Expand All @@ -124,6 +143,12 @@ func (c *Client) QueryRangeWithRetry(ctx context.Context, out output.LogOutput,
})
}

// LiveTailQueryConn does a live tailing with a specific query.
func (c *Client) LiveTailQueryConn(ctx context.Context, queryStr string, delayFor time.Duration, limit int, start time.Time, quiet bool) (*websocket.Conn, error) {
c.refreshToken(ctx)
return c.Client.LiveTailQueryConn(queryStr, delayFor, limit, start, quiet)
}

func printResult(value loghttp.ResultValue, out output.LogOutput) error {
switch value.Type() {
case logqlmodel.ValueTypeStreams:
Expand Down Expand Up @@ -151,7 +176,7 @@ func printStream(streams loghttp.Streams, out output.LogOutput) {
// This has been adapted from https://github.com/grafana/loki/blob/v2.8.2/pkg/logcli/query/tail.go#L22
// as it directly prints out messages using builtin log, which we don't want.
func (c *Client) TailQuery(ctx context.Context, delayFor time.Duration, out output.LogOutput, q Query) error {
conn, err := c.LiveTailQueryConn(q.QueryString, delayFor, q.Limit, q.Start, q.Quiet)
conn, err := c.LiveTailQueryConn(ctx, q.QueryString, delayFor, q.Limit, q.Start, q.Quiet)
if err != nil {
return fmt.Errorf("tailing logs failed: %w", err)
}
Expand Down Expand Up @@ -186,11 +211,10 @@ func (c *Client) TailQuery(ctx context.Context, delayFor time.Duration, out outp
})

for backoff.Ongoing() {
conn, err = c.LiveTailQueryConn(q.QueryString, delayFor, q.Limit, lastReceivedTimestamp, q.Quiet)
conn, err = c.LiveTailQueryConn(ctx, q.QueryString, delayFor, q.Limit, lastReceivedTimestamp, q.Quiet)
if err == nil {
break
}

backoff.Wait()
}

Expand Down
13 changes: 9 additions & 4 deletions create/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import (
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
)

const logPrintTimeout = 10 * time.Second

// note: when adding/changing fields here also make sure to carry it over to
// update/application.go.
type applicationCmd struct {
Expand Down Expand Up @@ -188,13 +190,15 @@ func (app *applicationCmd) Run(ctx context.Context, client *api.Client) error {
waitForBuildFinish(appWaitCtx, newApp, client.Log),
waitForRelease(newApp),
); err != nil {
printCtx, cancel := context.WithTimeout(context.Background(), logPrintTimeout)
defer cancel()
if buildErr, ok := err.(buildError); ok {
if err := buildErr.printMessage(appWaitCtx, client); err != nil {
if err := buildErr.printMessage(printCtx, client); err != nil {
return fmt.Errorf("%s: %w", buildErr, err)
}
}
if releaseErr, ok := err.(releaseError); ok {
if err := releaseErr.printMessage(appWaitCtx, client); err != nil {
if err := releaseErr.printMessage(printCtx, client); err != nil {
return fmt.Errorf("%s: %w", releaseErr, err)
}
}
Expand Down Expand Up @@ -437,8 +441,9 @@ type releaseError struct {
}

func (r releaseError) Error() string {
if r.release.Status.AtProvider.DeployJobStatus.Status == "" {
switch r.release.Status.AtProvider.DeployJobStatus.Reason {
deployJobStatus := r.release.Status.AtProvider.DeployJobStatus
if deployJobStatus != nil && deployJobStatus.Status == "" {
switch deployJobStatus.Reason {
case apps.DeployJobProcessReasonBackoff:
return "deploy job has failed after all retries."
case apps.DeployJobProcessReasonTimeout:
Expand Down

0 comments on commit ffa014d

Please sign in to comment.