8000 feat(api,sdk): Generate call stack for sdk error and preserve stack t… · ovh/cds@dc10dfc · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Commit dc10dfc

Browse files
richardltbnjjj
authored andcommitted
feat(api,sdk): Generate call stack for sdk error and preserve stack trace (#3310)
1 parent b4e192e commit dc10dfc

File tree

21 files changed

+475
-155
lines changed

21 files changed

+475
-155
lines changed

cli/cdsctl/admin.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ func admin() *cobra.Command {
2222
adminPlatformModels,
2323
adminPlugins,
2424
adminBroadcasts,
25+
adminErrors,
2526
usr,
2627
group,
2728
worker,
@@ -35,5 +36,6 @@ func admin() *cobra.Command {
3536
adminPlatformModels,
3637
adminPlugins,
3738
adminBroadcasts,
39+
adminErrors,
3840
})
3941
}

cli/cdsctl/admin_errors.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/ovh/cds/cli"
7+
"github.com/spf13/cobra"
8+
)
9+
10+
var (
11+
adminErrorsCmd = cli.Command{
12+
Name: "errors",
13+
Short: "Manage CDS errors",
14+
}
15+
16+
adminErrors = cli.NewCommand(adminErrorsCmd, nil,
17+
[]*cobra.Command{
18+
cli.NewCommand(adminErrorsGetCmd, adminErrorsGetFunc, nil),
19+
})
20+
)
21+
22+
var adminErrorsGetCmd = cli.Command{
23+
Name: "get",
24+
Short: "Get CDS error",
25+
Args: []cli.Arg{
26+
{Name: "uuid"},
27+
},
28+
}
29+
30+
func adminErrorsGetFunc(v cli.Values) error {
31+
res, err := client.MonErrorsGet(v.GetString("uuid"))
32+
if err != nil {
33+
return err
34+
}
35+
36+
fmt.Printf("Message: %s\n", res.Message)
37+
if res.StackTrace != "" {
38+
fmt.Printf("Stack trace:\n%s", res.StackTrace)
39+
}
40+
41+
return nil
42+
}

cli/cobra.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import (
1616
"github.com/olekukonko/tablewriter"
1717
"github.com/spf13/cobra"
1818
"gopkg.in/yaml.v2"
19+
20+
"github.com/ovh/cds/sdk"
1921
)
2022

2123
// ShellMode will os.Exit if false, display only exit code if true
@@ -27,18 +29,23 @@ func ExitOnError(err error, helpFunc ...func() error) {
2729
return
2830
}
2931

30-
if e, ok := err.(*Error); ok {
32+
code := 50 // default error code
33+
34+
switch e := err.(type) {
35+
case sdk.Error:
36+
fmt.Printf("Error(%s): %s\n", e.UUID, e.Message)
37+
case *Error:
38+
code = e.Code
3139
fmt.Println("Error:", e.Error())
32-
for _, f := range helpFunc {
33-
f()
34-
}
35-
OSExit(e.Code)
40+
default:
41+
fmt.Println("Error:", err.Error())
3642
}
37-
fmt.Println("Error:", err.Error())
43+
3844
for _, f := range helpFunc {
3945
f()
4046
}
41-
OSExit(50)
47+
48+
OSExit(code)
4249
}
4350

4451
// OSExit will os.Exit if ShellMode is false, display only exit code if true

engine/api/api.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ type Configuration struct {
170170
} `toml:"status" comment:"###########################\n CDS Status Settings \n Documentation: https://ovh.github.io/cds/hosting/monitoring/ \n##########################" json:"status"`
171171
DefaultOS string `toml:"defaultOS" default:"linux" comment:"if no model and os/arch is specified in your job's requirements then spawn worker on this operating system (example: freebsd, linux, windows)" json:"defaultOS"`
172172
DefaultArch string `toml:"defaultArch" default:"amd64" comment:"if no model and no os/arch is specified in your job's requirements then spawn worker on this architecture (example: amd64, arm, 386)" json:"defaultArch"`
173+
Graylog struct {
174+
AccessToken string `toml:"accessToken" json:"-"`
175+
URL string `toml:"url" comment:"Example: http://localhost:9000" json:"url"`
176+
} `toml:"graylog" json:"graylog"`
173177
}
174178

175179
// ProviderConfiguration is the piece of configuration for each provider authentication

engine/api/api_routes.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ func (api *API) InitRouter() {
9696
r.Handle("/mon/building/{hash}", r.GET(api.getPipelineBuildingCommitHandler))
9797
r.Handle("/mon/metrics", r.GET(api.getMetricsHandler, Auth(false)))
9898
r.Handle("/mon/stats", r.GET(observability.StatsHandler, Auth(false)))
99+
r.Handle("/mon/errors/{uuid}", r.GET(api.getErrorHandler, NeedAdmin(true)))
99100

100101
r.Handle("/ui/navbar", r.GET(api.getNavbarHandler))
101102
r.Handle("/ui/project/{key}/application/{permApplicationName}/overview", r.GET(api.getApplicationOverviewHandler))

engine/api/application_import.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ func (api *API) postApplicationImportHandler() service.Handler {
7171
myError, ok := globalError.(sdk.Error)
7272
if ok {
7373
log.Warning("postApplicationImportHandler> Unable to import application %s : %v", eapp.Name, myError)
74-
msgTranslated, _ := sdk.ProcessError(myError, r.Header.Get("Accept-Language"))
75-
msgListString = append(msgListString, msgTranslated)
76-
return service.WriteJSON(w, msgListString, myError.Status)
74+
sdkErr := sdk.ExtractHTTPError(myError, r.Header.Get("Accept-Language"))
75+
msgListString = append(msgListString, sdkErr.Message)
76+
return service.WriteJSON(w, msgListString, sdkErr.Status)
7777
}
7878
return sdk.WrapError(globalError, "postApplicationImportHandler> Unable import application %s", eapp.Name)
7979
}

engine/api/ascode_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,9 @@ func writeError(w *http.Response, err error) (*http.Response, error) {
3737
body := new(bytes.Buffer)
3838
enc := json.NewEncoder(body)
3939
w.Body = ioutil.NopCloser(body)
40-
msg, errProcessed := sdk.ProcessError(err, "")
41-
sdkErr := sdk.Error{Message: msg}
40+
sdkErr := sdk.ExtractHTTPError(err, "")
4241
enc.Encode(sdkErr)
43-
w.StatusCode = errProcessed.Status
42+
w.StatusCode = sdkErr.Status
4443
return w, sdkErr
4544
}
4645

engine/api/error.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,76 @@
11
package api
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"io/ioutil"
8+
"net/http"
9+
"time"
10+
11+
"github.com/gorilla/mux"
12+
13+
"github.com/ovh/cds/sdk"
14+
"github.com/ovh/cds/engine/service"
15+
)
16+
17+
type graylogResponse struct {
18+
TotalResult int `json:"total_results"`
19+
Messages []struct {
20+
Message map[string]interface{} `json:"message"`
21+
} `json:"messages"`
22+
}
23+
24+
func (api *API) getErrorHandler() service.Handler {
25+
return func(ctx context.Context, w http.ResponseWriter, r *http.Request) error {
26+
vars := mux.Vars(r)
27+
uuid := vars["uuid"]
28+
29+
if api.Config.Graylog.URL == "" || api.Config.Graylog.AccessToken == "" {
30+
return sdk.ErrNotImplemented
31+
}
32+
33+
req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/search/universal/absolute", api.Config.Graylog.URL), nil)
34+
if err != nil {
35+
return sdk.WrapError(err, "invalid given Graylog url")
36+
}
37+
38+
q := req.URL.Query()
39+
q.Add("query", fmt.Sprintf("error_uuid:%s", uuid))
40+
q.Add("from", "1970-01-01 00:00:00.000")
41+
q.Add("to", time.Now().Format("2006-01-02 15:04:05"))
42+
q.Add("limit", "1")
43+
req.URL.RawQuery = q.Encode()
44+
45+
req.SetBasicAuth(api.Config.Graylog.AccessToken, "token")
46+
resp, err := http.DefaultClient.Do(req)
47+
if err != nil {
48+
return sdk.WrapError(err, "cannot send query to Graylog")
49+
}
50+
51+
defer resp.Body.Close()
52+
body, err := ioutil.ReadAll(resp.Body)
53+
if err != nil {
54+
return sdk.WrapError(err, "cannot read response from Graylog")
55+
}
56+
57+
var res graylogResponse
58+
if err := json.Unmarshal(body, &res); err != nil {
59+
return sdk.WrapError(err, "cannot unmarshal response from Graylog")
60+
}
61+
62+
if res.TotalResult < 1 {
63+
return sdk.ErrNotFound
64+
}
65+
66+
e := sdk.Error{
67+
UUID: res.Messages[0].Message["error_uuid"].(string),
68+
Message: res.Messages[0].Message["message"].(string),
69+
}
70+
if st, ok := res.Messages[0].Message["stack_trace"]; ok {
71+
e.StackTrace = st.(string)
72+
}
73+
74+
return service.WriteJSON(w, e, http.StatusOK)
75+
}
76+
}

engine/api/services/dao.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func All(db gorp.SqlExecutor) ([]sdk.Service, error) {
6969
if err == sdk.ErrNotFound {
7070
return nil, nil
7171
}
72-
return nil, sdk.WrapError(err, "All> Unable to find all services")
72+
return nil, sdk.WrapError(err, "Unable to find all services")
7373
}
7474
return services, nil
7575
}
@@ -95,13 +95,13 @@ func findAll(db gorp.SqlExecutor, query string, args ...interface{}) ([]sdk.Serv
9595
if err == sql.ErrNoRows {
9696
return nil, sdk.ErrNotFound
9797
}
98-
return nil, sdk.WrapError(err, "findAll> no service found")
98+
return nil, sdk.WithStack(err)
9999< BC61 /td>
}
100100
ss := make([]sdk.Service, len(sdbs))
101101
for i := 0; i < len(sdbs); i++ {
102102
s := &sdbs[i]
103103
if err := s.PostGet(db); err != nil {
104-
return nil, sdk.WrapError(err, "findAll> postGet on srv id:%d name:%s type:%s lastHeartbeat:%v", s.ID, s.Name, s.Type, s.LastHeartbeat)
104+
return nil, sdk.WrapError(err, "postGet on srv id:%d name:%s type:%s lastHeartbeat:%v", s.ID, s.Name, s.Type, s.LastHeartbeat)
105105
}
106106
ss[i] = sdk.Service(sdbs[i])
107107
}

engine/api/status.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func (api *API) statusHandler() service.Handler {
6161

6262
srvs, err := services.All(api.mustDB())
6363
if err != nil {
64-
return sdk.WrapError(err, "statusHandler> error on q.All()")
64+
return err
6565
}
6666

6767
mStatus := api.computeGlobalStatus(srvs)

0 commit comments

Comments
 (0)
0