Refactor installation and runner scripts for enhanced usability and security
- Updated the installation script to clarify that production wrappers do not include fixed test secrets, improving user guidance for local testing. - Modified the jiggablend-manager and jiggablend-runner scripts to remove fixed test configurations, emphasizing the use of local test credentials via `make init-test`. - Enhanced error handling in the runner script to ensure that an API key is provided, improving security and user feedback. - Added new flags and options for the runner, including sandboxing capabilities and GPU ray tracing control, enhancing flexibility for users. - Improved README documentation to reflect changes in usage and configuration, ensuring users have clear instructions for setup and execution.
This commit is contained in:
@@ -301,7 +301,12 @@ func (j *JobConnection) OutputUploaded(taskID int64, fileName string) {
|
||||
}
|
||||
|
||||
// Complete sends task completion to the manager.
|
||||
func (j *JobConnection) Complete(taskID int64, success bool, errorMsg error) {
|
||||
// errorMsg must be JSON-encoded as a string: encoding an error interface
|
||||
// marshals to {} and the manager fails to unmarshal task_complete, which
|
||||
// previously surfaced as a generic "WebSocket connection lost" with no retries.
|
||||
// freeRequeue asks the manager to requeue a failure without incrementing retry_count
|
||||
// (used when this attempt newly armed GPU lockout).
|
||||
func (j *JobConnection) Complete(taskID int64, success bool, errorMsg error, freeRequeue bool) {
|
||||
if j.conn == nil {
|
||||
log.Printf("Cannot send task complete: WebSocket connection is nil")
|
||||
return
|
||||
@@ -310,13 +315,20 @@ func (j *JobConnection) Complete(taskID int64, success bool, errorMsg error) {
|
||||
j.writeMu.Lock()
|
||||
defer j.writeMu.Unlock()
|
||||
|
||||
data := map[string]interface{}{
|
||||
"task_id": taskID,
|
||||
"success": success,
|
||||
}
|
||||
if errorMsg != nil {
|
||||
data["error"] = errorMsg.Error()
|
||||
}
|
||||
if freeRequeue {
|
||||
data["free_requeue"] = true
|
||||
}
|
||||
|
||||
msg := map[string]interface{}{
|
||||
"type": "task_complete",
|
||||
"data": map[string]interface{}{
|
||||
"task_id": taskID,
|
||||
"success": success,
|
||||
"error": errorMsg,
|
||||
},
|
||||
"type": "task_complete",
|
||||
"data": data,
|
||||
"timestamp": time.Now().Unix(),
|
||||
}
|
||||
if err := j.conn.WriteJSON(msg); err != nil {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
@@ -10,6 +12,79 @@ import (
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
// TestComplete_ErrorIsJSONString ensures task_complete encodes the failure
|
||||
// reason as a string. Marshaling a Go error interface produces {}, which the
|
||||
// manager cannot unmarshal into WSTaskUpdate.Error and used to look like a
|
||||
// silent WebSocket drop with no retries.
|
||||
func TestComplete_ErrorIsJSONString(t *testing.T) {
|
||||
upgrader := websocket.Upgrader{}
|
||||
received := make(chan map[string]interface{}, 1)
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// auth handshake
|
||||
var auth map[string]interface{}
|
||||
if err := conn.ReadJSON(&auth); err != nil {
|
||||
return
|
||||
}
|
||||
if auth["type"] == "auth" {
|
||||
_ = conn.WriteJSON(map[string]string{"type": "auth_ok"})
|
||||
}
|
||||
|
||||
var msg map[string]interface{}
|
||||
if err := conn.ReadJSON(&msg); err != nil {
|
||||
return
|
||||
}
|
||||
received <- msg
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
jc := NewJobConnection()
|
||||
if err := jc.Connect(server.URL, "/job/1", "token123"); err != nil {
|
||||
t.Fatalf("Connect failed: %v", err)
|
||||
}
|
||||
defer jc.Close()
|
||||
|
||||
jc.Complete(42, false, errors.New("blender failed: signal: segmentation fault (core dumped)"), true)
|
||||
|
||||
select {
|
||||
case msg := <-received:
|
||||
if msg["type"] != "task_complete" {
|
||||
t.Fatalf("type = %v, want task_complete", msg["type"])
|
||||
}
|
||||
data, ok := msg["data"].(map[string]interface{})
|
||||
if !ok {
|
||||
// gorilla may leave nested objects as map[string]interface{} after JSON round-trip;
|
||||
// also accept raw re-marshal path
|
||||
raw, _ := json.Marshal(msg["data"])
|
||||
data = map[string]interface{}{}
|
||||
if err := json.Unmarshal(raw, &data); err != nil {
|
||||
t.Fatalf("data type %T: %v", msg["data"], err)
|
||||
}
|
||||
}
|
||||
errVal, ok := data["error"].(string)
|
||||
if !ok {
|
||||
t.Fatalf("error field type %T value %#v; want string (not object)", data["error"], data["error"])
|
||||
}
|
||||
if errVal != "blender failed: signal: segmentation fault (core dumped)" {
|
||||
t.Fatalf("error = %q, want segfault message", errVal)
|
||||
}
|
||||
if data["success"] != false {
|
||||
t.Fatalf("success = %v, want false", data["success"])
|
||||
}
|
||||
if data["free_requeue"] != true {
|
||||
t.Fatalf("free_requeue = %v, want true", data["free_requeue"])
|
||||
}
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("timed out waiting for task_complete message")
|
||||
}
|
||||
}
|
||||
|
||||
func TestJobConnection_ConnectAndClose(t *testing.T) {
|
||||
upgrader := websocket.Upgrader{}
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in New Issue
Block a user