mirror of https://github.com/databricks/cli.git
This commit is contained in:
parent
414a94df3b
commit
d5e03f08d5
|
@ -0,0 +1,26 @@
|
|||
error: Failed to upload telemetry logs: unable to parse response. This is likely a bug in the Databricks SDK for Go or the underlying REST API. Please report this issue with the following debugging information to the SDK issue tracker at https://github.com/databricks/databricks-sdk-go/issues. Request log:
|
||||
```
|
||||
POST /telemetry-ext
|
||||
> * Host:
|
||||
> * Accept: application/json
|
||||
> * Authorization: REDACTED
|
||||
> * Content-Type: application/json
|
||||
> * Traceparent: 00-1997351cf21b2042c96c8bf415bbcfe8-7542779df43bc559-01
|
||||
> * User-Agent: cli/$DEV_VERSION databricks-sdk-go/0.56.1 go/1.23.4 os/darwin auth/pat
|
||||
> {
|
||||
> "items": null,
|
||||
> "protoLogs": [
|
||||
> "{\"frontend_log_event_id\":\"[UUID]\",\"entry\":{\"databricks_cli_log\":{\"execution_context\":{\"cmd_exec_id\":\"[UUID]\",\"version\":\"$DEV_VERSION\",\"command\":\"telemetry_dummy\",\"operating_system\":\"darwin\",\"execution_time_ms\":0,\"exit_code\":0},\"cli_test_event\":{\"name\":\"VALUE1\"}}}}",
|
||||
> "{\"frontend_log_event_id\":\"[UUID]\",\"entry\":{\"databricks_cli_log\":{\"execution_context\":{\"cmd_exec_id\":\"[UUID]\",\"version\":\"$DEV_VERSION\",\"command\":\"telemetry_dummy\",\"operating_system\":\"darwin\",\"execution_time_ms\":0,\"exit_code\":0},\"cli_test_event\":{\"name\":\"VALUE2\"}}}}"
|
||||
> ],
|
||||
> "uploadTime": 1738682716822
|
||||
> }
|
||||
< HTTP/1.1 404 Not Found
|
||||
< * Content-Length: 19
|
||||
< * Content-Type: text/plain; charset=utf-8
|
||||
< * Date: Tue, 04 Feb 2025 15:25:16 GMT
|
||||
< * X-Content-Type-Options: nosniff
|
||||
< 404 page not found
|
||||
<
|
||||
```
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
export DATABRICKS_CLI_TELEMETRY_PID_FILE=./telemetry.pid
|
||||
export DATABRICKS_CLI_TELEMETRY_UPLOAD_LOGS_FILE=./out.upload.txt
|
||||
|
||||
# This test ensures that the main CLI command does not error even if
|
||||
# telemetry upload fails.
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
# TODO: We do not run this on windows because we don't have proper stubbing
|
||||
# for API errors yet. Simply not defining a server stub does not return a 404 on windows.
|
||||
# Run this on windows as well once we can stub API errors.
|
||||
[GOOS]
|
||||
windows = false
|
|
@ -0,0 +1,3 @@
|
|||
Telemetry logs uploaded successfully
|
||||
Response:
|
||||
{"errors":null,"numProtoSuccess":2}
|
|
@ -1,4 +1,5 @@
|
|||
export DATABRICKS_CLI_TELEMETRY_PID_FILE=./telemetry.pid
|
||||
export DATABRICKS_CLI_TELEMETRY_UPLOAD_LOGS_FILE=./out.upload.txt
|
||||
|
||||
trace $CLI telemetry dummy
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
Telemetry logs uploaded successfully
|
||||
Response:
|
||||
{"errors":null,"numProtoSuccess":2}
|
|
@ -1,5 +1,2 @@
|
|||
|
||||
>>> $CLI telemetry upload
|
||||
Successfully uploaded telemetry logs
|
||||
Response:
|
||||
{"errors":null,"numProtoSuccess":2}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
export DATABRICKS_CLI_TELEMETRY_UPLOAD_LOGS_FILE=./out.upload.txt
|
||||
|
||||
# This command / test cannot be run in inprocess / debug mode. This is because
|
||||
# it does not go through the [root.Execute] function.
|
||||
trace $CLI telemetry upload < stdin
|
||||
|
|
|
@ -165,8 +165,6 @@ func inheritEnvVars() []string {
|
|||
return out
|
||||
}
|
||||
|
||||
const telemetryPidFileEnvVar = "DATABRICKS_CLI_TELEMETRY_PID_FILE"
|
||||
|
||||
func uploadTelemetry(ctx context.Context, cmdStr string, start, end time.Time, exitCode int) {
|
||||
// Nothing to upload.
|
||||
if !telemetry.HasLogs(ctx) {
|
||||
|
@ -216,7 +214,7 @@ func uploadTelemetry(ctx context.Context, cmdStr string, start, end time.Time, e
|
|||
return
|
||||
}
|
||||
|
||||
if pidFilePath := env.Get(ctx, telemetryPidFileEnvVar); pidFilePath != "" {
|
||||
if pidFilePath := env.Get(ctx, telemetry.PidFileEnvVar); pidFilePath != "" {
|
||||
err = os.WriteFile(pidFilePath, []byte(strconv.Itoa(telemetryCmd.Process.Pid)), 0o644)
|
||||
if err != nil {
|
||||
log.Debugf(ctx, "failed to write telemetry worker PID file: %s", err)
|
||||
|
|
|
@ -11,13 +11,17 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/databricks/cli/libs/telemetry/protos"
|
||||
"github.com/databricks/databricks-sdk-go/apierr"
|
||||
"github.com/databricks/databricks-sdk-go/client"
|
||||
"github.com/databricks/databricks-sdk-go/config"
|
||||
)
|
||||
|
||||
// File containing debug logs from the upload process.
|
||||
const UploadLogsFileEnvVar = "DATABRICKS_TELEMETRY_UPLOAD_LOGS_FILE"
|
||||
const (
|
||||
UploadLogsFileEnvVar = "DATABRICKS_CLI_TELEMETRY_UPLOAD_LOGS_FILE"
|
||||
|
||||
// File containing the PID of the telemetry upload process.
|
||||
PidFileEnvVar = "DATABRICKS_CLI_TELEMETRY_PID_FILE"
|
||||
)
|
||||
|
||||
type UploadConfig struct {
|
||||
Logs []protos.FrontendLog `json:"logs"`
|
||||
|
@ -26,30 +30,29 @@ type UploadConfig struct {
|
|||
// Upload reads telemetry logs from stdin and uploads them to the telemetry endpoint.
|
||||
// This function is always expected to be called in a separate child process from
|
||||
// the main CLI process.
|
||||
func Upload() error {
|
||||
func Upload() (*ResponseBody, error) {
|
||||
var err error
|
||||
|
||||
b, err := io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read from stdin: %s\n", err)
|
||||
return nil, fmt.Errorf("failed to read from stdin: %s\n", err)
|
||||
}
|
||||
|
||||
in := UploadConfig{}
|
||||
err = json.Unmarshal(b, &in)
|
||||
if err != nil {
|
||||
printJson(in)
|
||||
return fmt.Errorf("failed to unmarshal input: %s\n", err)
|
||||
return nil, fmt.Errorf("failed to unmarshal input: %s\n", err)
|
||||
}
|
||||
|
||||
if len(in.Logs) == 0 {
|
||||
return fmt.Errorf("No logs to upload: %s\n", err)
|
||||
return nil, fmt.Errorf("No logs to upload: %s\n", err)
|
||||
}
|
||||
|
||||
protoLogs := make([]string, len(in.Logs))
|
||||
for i, log := range in.Logs {
|
||||
b, err := json.Marshal(log)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal log: %s\n", err)
|
||||
return nil, fmt.Errorf("failed to marshal log: %s\n", err)
|
||||
}
|
||||
protoLogs[i] = string(b)
|
||||
}
|
||||
|
@ -58,7 +61,7 @@ func Upload() error {
|
|||
// configure authentication.
|
||||
apiClient, err := client.New(&config.Config{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create API client: %s\n", err)
|
||||
return nil, fmt.Errorf("Failed to create API client: %s\n", err)
|
||||
}
|
||||
|
||||
// Set a maximum total time to try telemetry uploads.
|
||||
|
@ -69,7 +72,7 @@ func Upload() error {
|
|||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return errors.New("Failed to flush telemetry log due to timeout")
|
||||
return nil, errors.New("Failed to flush telemetry log due to timeout")
|
||||
|
||||
default:
|
||||
// Proceed
|
||||
|
@ -81,33 +84,16 @@ func Upload() error {
|
|||
Items: []string{},
|
||||
ProtoLogs: protoLogs,
|
||||
}, resp)
|
||||
var apierr *apierr.APIError
|
||||
if errors.As(err, &apierr) && apierr.StatusCode == http.StatusNotFound {
|
||||
return errors.New("telemetry endpoint not found")
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to upload telemetry logs: %s\n", err)
|
||||
return nil, fmt.Errorf("Failed to upload telemetry logs: %s\n", err)
|
||||
}
|
||||
|
||||
if len(resp.Errors) > 0 {
|
||||
return fmt.Errorf("Failed to upload telemetry logs: %s\n", resp.Errors)
|
||||
return nil, fmt.Errorf("Failed to upload telemetry logs: %s\n", resp.Errors)
|
||||
}
|
||||
|
||||
if resp.NumProtoSuccess == int64(len(in.Logs)) {
|
||||
fmt.Println("Successfully uploaded telemetry logs")
|
||||
fmt.Println("Response: ")
|
||||
printJson(resp)
|
||||
break
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func printJson(v any) {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
fmt.Printf("failed to marshal JSON: %s\n", err)
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
}
|
||||
|
|
|
@ -79,8 +79,8 @@ func TestTelemetryUpload(t *testing.T) {
|
|||
os.Stdin = old
|
||||
})
|
||||
|
||||
err = Upload()
|
||||
resp, err := Upload()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, int64(2), resp.NumProtoSuccess)
|
||||
assert.Equal(t, 2, count)
|
||||
}
|
||||
|
|
20
main.go
20
main.go
|
@ -2,7 +2,9 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/databricks/cli/cmd"
|
||||
|
@ -22,18 +24,28 @@ func main() {
|
|||
if len(os.Args) == 3 && os.Args[1] == "telemetry" && os.Args[2] == "upload" {
|
||||
var err error
|
||||
|
||||
// By default, this command should not write anything to stdout or stderr.
|
||||
outW := io.Discard
|
||||
errW := io.Discard
|
||||
|
||||
// If the environment variable is set, redirect stdout to the file.
|
||||
// This is useful for testing.
|
||||
if v := os.Getenv(telemetry.UploadLogsFileEnvVar); v != "" {
|
||||
os.Stdout, _ = os.OpenFile(v, os.O_CREATE|os.O_WRONLY, 0o644)
|
||||
os.Stderr = os.Stdout
|
||||
outW, _ = os.OpenFile(v, os.O_CREATE|os.O_WRONLY, 0o644)
|
||||
errW = outW
|
||||
}
|
||||
|
||||
err = telemetry.Upload()
|
||||
resp, err := telemetry.Upload()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: %s\n", err)
|
||||
fmt.Fprintf(errW, "error: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Fprintf(outW, "Telemetry logs uploaded successfully\n")
|
||||
fmt.Fprintln(outW, "Response:")
|
||||
b, err := json.Marshal(resp)
|
||||
if err == nil {
|
||||
fmt.Fprintln(outW, string(b))
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue