fix uploading and more RBAC refinements for auto deploy
This commit is contained in:
@@ -742,7 +742,12 @@ const (
|
||||
)
|
||||
|
||||
func HandleFileUploadHTTP(ctx context.Context, env *agentRuntime.Env, cmdID string, destPath string, sourceURL string, expectedSize int64) error {
|
||||
parsed, err := url.Parse(strings.TrimSpace(sourceURL))
|
||||
resolvedURL, err := resolveUploadPullURL(env, sourceURL)
|
||||
if err != nil {
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: err.Error()})
|
||||
}
|
||||
|
||||
parsed, err := url.Parse(resolvedURL)
|
||||
if err != nil || parsed == nil || parsed.Host == "" {
|
||||
return wire.WriteMsg(ctx, env.Conn, wire.CommandResult{Type: "command_result", CommandID: cmdID, OK: false, Message: "invalid upload url"})
|
||||
}
|
||||
@@ -951,6 +956,64 @@ func nextBackoff(d time.Duration) time.Duration {
|
||||
return next
|
||||
}
|
||||
|
||||
func resolveUploadPullURL(env *agentRuntime.Env, raw string) (string, error) {
|
||||
trimmed := strings.TrimSpace(raw)
|
||||
if trimmed == "" {
|
||||
return "", errors.New("missing upload url")
|
||||
}
|
||||
|
||||
parsed, err := url.Parse(trimmed)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid upload url: %w", err)
|
||||
}
|
||||
|
||||
if parsed.IsAbs() {
|
||||
scheme := strings.ToLower(parsed.Scheme)
|
||||
if scheme != "http" && scheme != "https" {
|
||||
return "", fmt.Errorf("unsupported upload url scheme: %s", parsed.Scheme)
|
||||
}
|
||||
if !strings.HasPrefix(parsed.Path, "/api/file/upload/pull/") {
|
||||
return parsed.String(), nil
|
||||
}
|
||||
}
|
||||
|
||||
if len(env.Cfg.ServerURLs) == 0 {
|
||||
if parsed.IsAbs() {
|
||||
return parsed.String(), nil
|
||||
}
|
||||
return "", errors.New("no server url configured for upload pull")
|
||||
}
|
||||
|
||||
idx := env.Cfg.ServerIndex
|
||||
if idx < 0 || idx >= len(env.Cfg.ServerURLs) {
|
||||
idx = 0
|
||||
}
|
||||
server, err := url.Parse(env.Cfg.ServerURLs[idx])
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid agent server url: %w", err)
|
||||
}
|
||||
switch strings.ToLower(server.Scheme) {
|
||||
case "wss":
|
||||
server.Scheme = "https"
|
||||
case "ws":
|
||||
server.Scheme = "http"
|
||||
case "https", "http":
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported agent server scheme: %s", server.Scheme)
|
||||
}
|
||||
|
||||
if parsed.Path == "" {
|
||||
return "", errors.New("missing upload pull path")
|
||||
}
|
||||
if !strings.HasPrefix(parsed.Path, "/") {
|
||||
parsed.Path = "/" + parsed.Path
|
||||
}
|
||||
server.Path = parsed.Path
|
||||
server.RawQuery = parsed.RawQuery
|
||||
server.Fragment = ""
|
||||
return server.String(), nil
|
||||
}
|
||||
|
||||
func HandleFileDelete(ctx context.Context, env *agentRuntime.Env, cmdID string, path string) error {
|
||||
log.Printf("file_delete: %s", path)
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"overlord-client/cmd/agent/config"
|
||||
@@ -522,6 +523,61 @@ func TestHandleFileUploadHTTP(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleFileUploadHTTP_RewritesUploadPullURLToAgentServer(t *testing.T) {
|
||||
data := []byte("wan-origin-upload-payload")
|
||||
var gotHost string
|
||||
var gotPath string
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
gotHost = r.Host
|
||||
gotPath = r.URL.RequestURI()
|
||||
if gotPath != "/api/file/upload/pull/test-id?token=abc" {
|
||||
t.Fatalf("expected rewritten upload pull path, got %q", gotPath)
|
||||
}
|
||||
_, _ = w.Write(data)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
tmpDir := t.TempDir()
|
||||
destPath := filepath.Join(tmpDir, "uploaded-http-rewritten.bin")
|
||||
|
||||
writer := &testWriter{}
|
||||
env := &rt.Env{
|
||||
Conn: writer,
|
||||
Cfg: config.Config{
|
||||
ServerURLs: []string{strings.Replace(ts.URL, "http://", "ws://", 1)},
|
||||
},
|
||||
}
|
||||
|
||||
publicOriginURL := "https://public.example.invalid/api/file/upload/pull/test-id?token=abc"
|
||||
if err := HandleFileUploadHTTP(context.Background(), env, "cmd-http-upload-rewrite", destPath, publicOriginURL, int64(len(data))); err != nil {
|
||||
t.Fatalf("HandleFileUploadHTTP failed: %v", err)
|
||||
}
|
||||
|
||||
if gotHost == "" || strings.Contains(gotHost, "public.example.invalid") {
|
||||
t.Fatalf("expected request to active agent server, got host %q", gotHost)
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(destPath)
|
||||
if err != nil {
|
||||
t.Fatalf("failed reading uploaded file: %v", err)
|
||||
}
|
||||
if !bytes.Equal(content, data) {
|
||||
t.Fatalf("uploaded content mismatch")
|
||||
}
|
||||
|
||||
if len(writer.msgs) != 1 {
|
||||
t.Fatalf("expected 1 message, got %d", len(writer.msgs))
|
||||
}
|
||||
var result wire.CommandResult
|
||||
if err := msgpack.Unmarshal(writer.msgs[0], &result); err != nil {
|
||||
t.Fatalf("failed to unmarshal command result: %v", err)
|
||||
}
|
||||
if !result.OK {
|
||||
t.Fatalf("expected OK=true, got false: %s", result.Message)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleFileUploadHTTP_ResumesWithRangeAfterShortRead(t *testing.T) {
|
||||
data := bytes.Repeat([]byte("range-retry-"), 128*1024)
|
||||
firstLen := len(data) / 3
|
||||
|
||||
@@ -119,8 +119,11 @@ func runClient(cfg config.Config) {
|
||||
|
||||
conn.SetReadLimit(8 * 1024 * 1024)
|
||||
|
||||
sessionCfg := cfg
|
||||
sessionCfg.ServerIndex = currentIndex
|
||||
|
||||
var sessionErr error
|
||||
if err := runSession(ctx, cancel, conn, cfg); err != nil {
|
||||
if err := runSession(ctx, cancel, conn, sessionCfg); err != nil {
|
||||
sessionErr = err
|
||||
log.Printf("session ended: %v (retrying in %s)", err, backoff)
|
||||
lastDisconnect = time.Now()
|
||||
|
||||
Reference in New Issue
Block a user