diff --git a/go/provider/client/client.go b/go/provider/client/client.go index dbb90f21..6b107b10 100644 --- a/go/provider/client/client.go +++ b/go/provider/client/client.go @@ -603,7 +603,7 @@ func (c *client) LeaseStatus(ctx context.Context, id mtypes.LeaseID) (LeaseStatu return obj, nil } -func (c *client) LeaseEvents(ctx context.Context, id mtypes.LeaseID, _ string, follow bool) (*LeaseKubeEvents, error) { +func (c *client) LeaseEvents(ctx context.Context, id mtypes.LeaseID, services string, follow bool) (*LeaseKubeEvents, error) { endpoint, err := url.Parse(c.host.String() + "/" + LeaseEventsPath(id)) if err != nil { return nil, err @@ -619,6 +619,10 @@ func (c *client) LeaseEvents(ctx context.Context, id mtypes.LeaseID, _ string, f query := url.Values{} query.Set("follow", strconv.FormatBool(follow)) + if services != "" { + query.Set("service", services) + } + endpoint.RawQuery = query.Encode() rCl := c.NewReqClient(ctx) @@ -782,7 +786,7 @@ func (c *client) LeaseLogs(ctx context.Context, id mtypes.LeaseID, services string, follow bool, - _ int64, + tailLines int64, ) (*ServiceLogs, error) { endpoint, err := url.Parse(c.host.String() + "/" + ServiceLogsPath(id)) if err != nil { @@ -801,7 +805,11 @@ func (c *client) LeaseLogs(ctx context.Context, query.Set("follow", strconv.FormatBool(follow)) if services != "" { - query.Set("services", services) + query.Set("service", services) + } + + if tailLines > 0 { + query.Set("tail", strconv.FormatInt(tailLines, 10)) } endpoint.RawQuery = query.Encode() diff --git a/go/provider/client/client_test.go b/go/provider/client/client_test.go index df2bd5f6..8d7fee47 100644 --- a/go/provider/client/client_test.go +++ b/go/provider/client/client_test.go @@ -2,12 +2,20 @@ package rest import ( "context" + "crypto/tls" "errors" + "net/http" + "net/http/httptest" + "net/url" + "sync" "testing" + "github.com/gorilla/websocket" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/stretchr/testify/require" + mtypes "pkg.akt.dev/go/node/market/v1" _ "pkg.akt.dev/go/sdkutil" ) @@ -70,3 +78,142 @@ func TestNewClientWithProviderURL(t *testing.T) { }) } + +// captureQueryServer spins up an httptest.NewTLSServer that records the query +// string of the first websocket upgrade request, then closes the connection. +// The provider gateway uses singular `service` and `tail` query params; these +// tests assert the client sends exactly those keys (and not the legacy plural +// `services`). +// +// Cross-reference: akash-network/provider gateway/rest/middleware.go +// requestStreamParams reads vars.Get("service") and vars.Get("tail"). +func captureQueryServer(t *testing.T) (*httptest.Server, func() url.Values) { + t.Helper() + var ( + mu sync.Mutex + q url.Values + ) + upgrader := websocket.Upgrader{ + CheckOrigin: func(*http.Request) bool { return true }, + } + srv := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + mu.Lock() + q = r.URL.Query() + mu.Unlock() + + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + return + } + _ = conn.Close() + })) + return srv, func() url.Values { + mu.Lock() + defer mu.Unlock() + return q + } +} + +// newTestClient builds a *client pointed at the test server's URL with a TLS +// config that trusts the httptest self-signed certificate. Same-package +// access lets us replace tlsCfg directly, avoiding any production-surface +// change. +func newTestClient(t *testing.T, ctx context.Context, providerURL string) *client { + t.Helper() + addr, err := sdk.AccAddressFromBech32("akash1365yvmc4s7awdyj3n2sav7xfx76adc6dnmlx63") + require.NoError(t, err) + cl, err := NewClient(ctx, addr, WithProviderURL(providerURL)) + require.NoError(t, err) + c := cl.(*client) + c.tlsCfg = &tls.Config{InsecureSkipVerify: true} // nolint: gosec // httptest self-signed cert + return c +} + +func testLeaseID() mtypes.LeaseID { + const akashAddr = "akash1365yvmc4s7awdyj3n2sav7xfx76adc6dnmlx63" + return mtypes.LeaseID{ + Owner: akashAddr, + DSeq: 1, + GSeq: 1, + OSeq: 1, + Provider: akashAddr, + } +} + +func TestLeaseLogs_ForwardsServiceAndTailToGateway(t *testing.T) { + srv, getQuery := captureQueryServer(t) + defer srv.Close() + + ctx := context.Background() + c := newTestClient(t, ctx, srv.URL) + lid := testLeaseID() + + out, err := c.LeaseLogs(ctx, lid, "db-replica,api", true, 50) + require.NoError(t, err) + require.NotNil(t, out) + <-out.OnClose // wait for reader goroutine to drain after server close + + q := getQuery() + require.Equal(t, "db-replica,api", q.Get("service"), + "gateway requestStreamParams reads vars.Get(\"service\") — must be singular") + require.Equal(t, "50", q.Get("tail"), + "tailLines must be forwarded as ?tail=N") + require.Equal(t, "true", q.Get("follow")) + require.Empty(t, q.Get("services"), + "legacy plural 'services' key is silently dropped by the gateway — must not be sent") +} + +func TestLeaseLogs_OmitsEmptyServiceAndZeroTail(t *testing.T) { + srv, getQuery := captureQueryServer(t) + defer srv.Close() + + ctx := context.Background() + c := newTestClient(t, ctx, srv.URL) + lid := testLeaseID() + + out, err := c.LeaseLogs(ctx, lid, "", false, 0) + require.NoError(t, err) + <-out.OnClose + + q := getQuery() + require.Empty(t, q.Get("service"), "empty service filter must not be sent") + require.Empty(t, q.Get("tail"), "tail <= 0 must not be sent (gateway default is -1)") + require.Equal(t, "false", q.Get("follow")) +} + +func TestLeaseEvents_ForwardsServiceToGateway(t *testing.T) { + srv, getQuery := captureQueryServer(t) + defer srv.Close() + + ctx := context.Background() + c := newTestClient(t, ctx, srv.URL) + lid := testLeaseID() + + out, err := c.LeaseEvents(ctx, lid, "db-replica", true) + require.NoError(t, err) + require.NotNil(t, out) + <-out.OnClose + + q := getQuery() + require.Equal(t, "db-replica", q.Get("service")) + require.Equal(t, "true", q.Get("follow")) + require.Empty(t, q.Get("services")) +} + +func TestLeaseEvents_OmitsEmptyService(t *testing.T) { + srv, getQuery := captureQueryServer(t) + defer srv.Close() + + ctx := context.Background() + c := newTestClient(t, ctx, srv.URL) + lid := testLeaseID() + + out, err := c.LeaseEvents(ctx, lid, "", false) + require.NoError(t, err) + <-out.OnClose + + q := getQuery() + require.Empty(t, q.Get("service")) + require.Equal(t, "false", q.Get("follow")) + require.Empty(t, q.Get("services")) +}