mirror of https://github.com/databricks/cli.git
Show error trace on pipeline run failure
This commit is contained in:
parent
f0c35a2b27
commit
3f6f643407
|
@ -4,15 +4,73 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/databricks/bricks/bundle"
|
"github.com/databricks/bricks/bundle"
|
||||||
"github.com/databricks/bricks/bundle/config/resources"
|
"github.com/databricks/bricks/bundle/config/resources"
|
||||||
|
"github.com/databricks/databricks-sdk-go/client"
|
||||||
"github.com/databricks/databricks-sdk-go/service/pipelines"
|
"github.com/databricks/databricks-sdk-go/service/pipelines"
|
||||||
|
"github.com/fatih/color"
|
||||||
flag "github.com/spf13/pflag"
|
flag "github.com/spf13/pflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type pipelineEventErrorException struct {
|
||||||
|
ClassName string `json:"class_name"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type pipelineEventError struct {
|
||||||
|
Exceptions []pipelineEventErrorException `json:"exceptions"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type pipelineEvent struct {
|
||||||
|
Error *pipelineEventError `json:"error"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type pipelineEventsResponse struct {
|
||||||
|
Events []pipelineEvent `json:"events"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: make ticket to replace this on sdk update
|
||||||
|
func (r *pipelineRunner) logErrorEvent(ctx context.Context, pipelineId string) error {
|
||||||
|
apiClient, err := client.New(r.bundle.WorkspaceClient().Config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
filter := url.QueryEscape(`level='ERROR'`)
|
||||||
|
apiPath := fmt.Sprintf("/api/2.0/pipelines/%s/events?filter=%s", pipelineId, filter)
|
||||||
|
res := pipelineEventsResponse{}
|
||||||
|
err = apiClient.Do(ctx, http.MethodGet, apiPath, nil, &res)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(res.Events) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
red := color.New(color.FgRed).SprintFunc()
|
||||||
|
errorPrefix := fmt.Sprintf("%s [%s]", red("[ERROR]"), r.Key())
|
||||||
|
latestEvent := res.Events[0]
|
||||||
|
logString := errorPrefix
|
||||||
|
if latestEvent.Message != "" {
|
||||||
|
logString += fmt.Sprintf(" %s\n", latestEvent.Message)
|
||||||
|
}
|
||||||
|
if latestEvent.Error != nil && len(latestEvent.Error.Exceptions) > 0 {
|
||||||
|
logString += "trace: \n"
|
||||||
|
for i := 0; i < len(latestEvent.Error.Exceptions); i++ {
|
||||||
|
logString += fmt.Sprintf("%s\n", latestEvent.Error.Exceptions[i].Message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if logString != errorPrefix {
|
||||||
|
log.Printf(logString)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// PipelineOptions defines options for running a pipeline update.
|
// PipelineOptions defines options for running a pipeline update.
|
||||||
type PipelineOptions struct {
|
type PipelineOptions struct {
|
||||||
// Perform a full graph update.
|
// Perform a full graph update.
|
||||||
|
@ -102,8 +160,8 @@ func (r *pipelineRunner) Run(ctx context.Context, opts *Options) error {
|
||||||
updateID := res.UpdateId
|
updateID := res.UpdateId
|
||||||
|
|
||||||
// Log the pipeline update URL as soon as it is available.
|
// Log the pipeline update URL as soon as it is available.
|
||||||
url := fmt.Sprintf("%s/#joblist/pipelines/%s/updates/%s", w.Config.Host, pipelineID, updateID)
|
updateUrl := fmt.Sprintf("%s/#joblist/pipelines/%s/updates/%s", w.Config.Host, pipelineID, updateID)
|
||||||
log.Printf("%s Update available at %s", prefix, url)
|
log.Printf("%s Update available at %s", prefix, updateUrl)
|
||||||
|
|
||||||
// Poll update for completion and post status.
|
// Poll update for completion and post status.
|
||||||
// Note: there is no "StartUpdateAndWait" wrapper for this API.
|
// Note: there is no "StartUpdateAndWait" wrapper for this API.
|
||||||
|
@ -127,6 +185,7 @@ func (r *pipelineRunner) Run(ctx context.Context, opts *Options) error {
|
||||||
}
|
}
|
||||||
if state == pipelines.UpdateInfoStateFailed {
|
if state == pipelines.UpdateInfoStateFailed {
|
||||||
log.Printf("%s Update has failed!", prefix)
|
log.Printf("%s Update has failed!", prefix)
|
||||||
|
r.logErrorEvent(ctx, pipelineID)
|
||||||
return fmt.Errorf("update failed")
|
return fmt.Errorf("update failed")
|
||||||
}
|
}
|
||||||
if state == pipelines.UpdateInfoStateCompleted {
|
if state == pipelines.UpdateInfoStateCompleted {
|
||||||
|
|
Loading…
Reference in New Issue