From bbf4f3e0a55dd45d2ad6306c5a3aa3cf26048fcb Mon Sep 17 00:00:00 2001 From: Gyuho Lee Date: Tue, 11 Feb 2025 20:22:58 +0800 Subject: [PATCH] wip Signed-off-by: Gyuho Lee --- client/v1/healthz.go | 9 +- client/v1/healthz_test.go | 44 +------- client/v1/options.go | 30 ------ client/v1/options_test.go | 193 ------------------------------------ client/v1/package_status.go | 3 +- client/v1/v1.go | 22 ++-- 6 files changed, 25 insertions(+), 276 deletions(-) delete mode 100644 client/v1/options_test.go diff --git a/client/v1/healthz.go b/client/v1/healthz.go index 674b834e..e49c36f8 100644 --- a/client/v1/healthz.go +++ b/client/v1/healthz.go @@ -30,7 +30,7 @@ func CheckHealthz(ctx context.Context, addr string, opts ...OpOption) error { return fmt.Errorf("failed to marshal expected healthz response: %w", err) } - return checkHealthz(op.httpClient, req, exp) + return checkHealthz(createDefaultHTTPClient(), req, exp) } func checkHealthz(cli *http.Client, req *http.Request, exp []byte) error { @@ -72,12 +72,15 @@ func BlockUntilServerReady(ctx context.Context, addr string, opts ...OpOption) e return fmt.Errorf("failed to marshal expected healthz response: %w", err) } - ticker := time.NewTicker(op.checkInterval) + httpClient := createDefaultHTTPClient() + + ticker := time.NewTicker(time.Second) defer ticker.Stop() + for range 30 { select { case <-ticker.C: - if err := checkHealthz(op.httpClient, req, exp); err == nil { + if err := checkHealthz(httpClient, req, exp); err == nil { return nil } case <-ctx.Done(): diff --git a/client/v1/healthz_test.go b/client/v1/healthz_test.go index 642ab30a..6ae1b683 100644 --- a/client/v1/healthz_test.go +++ b/client/v1/healthz_test.go @@ -8,9 +8,10 @@ import ( "testing" "time" - "github.com/leptonai/gpud/internal/server" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/leptonai/gpud/internal/server" ) func TestCheckHealthz(t *testing.T) { @@ -166,44 +167,3 @@ func TestCheckHealthzContextCancellation(t *testing.T) { t.Error("CheckHealthz() with canceled context should return error") } } - -func TestCheckHealthzWithCustomClient(t *testing.T) { - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path != "/healthz" { - t.Errorf("Expected /healthz path, got %s", r.URL.Path) - http.NotFound(w, r) - return - } - w.WriteHeader(http.StatusOK) - json, _ := server.DefaultHealthz.JSON() - if _, err := w.Write(json); err != nil { - t.Errorf("Error writing response: %v", err) - } - })) - defer srv.Close() - - customClient := &http.Client{Timeout: 1 * time.Second} - err := CheckHealthz(context.Background(), srv.URL, WithHTTPClient(customClient)) - if err != nil { - t.Errorf("CheckHealthz() with custom client error = %v, want nil", err) - } -} - -func TestCheckHealthzTimeout(t *testing.T) { - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - time.Sleep(200 * time.Millisecond) - w.WriteHeader(http.StatusOK) - json, _ := server.DefaultHealthz.JSON() - _, err := w.Write(json) - if err != nil { - t.Errorf("Error writing response: %v", err) - } - })) - defer srv.Close() - - client := &http.Client{Timeout: 100 * time.Millisecond} - err := CheckHealthz(context.Background(), srv.URL, WithHTTPClient(client)) - if err == nil { - t.Error("CheckHealthz() with timeout should return error") - } -} diff --git a/client/v1/options.go b/client/v1/options.go index dc28ff32..fff7980f 100644 --- a/client/v1/options.go +++ b/client/v1/options.go @@ -2,16 +2,10 @@ package v1 import ( - "crypto/tls" - "net/http" - "time" - "github.com/leptonai/gpud/internal/server" ) type Op struct { - httpClient *http.Client - checkInterval time.Duration requestContentType string requestAcceptEncoding string components map[string]any @@ -24,33 +18,9 @@ func (op *Op) applyOpts(opts []OpOption) error { opt(op) } - if op.httpClient == nil { - op.httpClient = &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }, - } - } - - if op.checkInterval == 0 { - op.checkInterval = time.Second - } - return nil } -func WithHTTPClient(cli *http.Client) OpOption { - return func(op *Op) { - op.httpClient = cli - } -} - -func WithCheckInterval(interval time.Duration) OpOption { - return func(op *Op) { - op.checkInterval = interval - } -} - // WithRequestContentTypeYAML sets the request content type to YAML. func WithRequestContentTypeYAML() OpOption { return func(op *Op) { diff --git a/client/v1/options_test.go b/client/v1/options_test.go deleted file mode 100644 index ac331369..00000000 --- a/client/v1/options_test.go +++ /dev/null @@ -1,193 +0,0 @@ -package v1 - -import ( - "net/http" - "testing" - "time" - - "github.com/leptonai/gpud/internal/server" -) - -func TestWithHTTPClient(t *testing.T) { - customClient := &http.Client{Timeout: time.Hour} - op := &Op{} - opt := WithHTTPClient(customClient) - opt(op) - - if op.httpClient != customClient { - t.Errorf("WithHTTPClient() did not set the expected client") - } -} - -func TestWithCheckInterval(t *testing.T) { - interval := 5 * time.Second - op := &Op{} - opt := WithCheckInterval(interval) - opt(op) - - if op.checkInterval != interval { - t.Errorf("WithCheckInterval() = %v, want %v", op.checkInterval, interval) - } -} - -func TestWithRequestContentTypeYAML(t *testing.T) { - op := &Op{} - opt := WithRequestContentTypeYAML() - opt(op) - - if op.requestContentType != server.RequestHeaderYAML { - t.Errorf("WithRequestContentTypeYAML() = %v, want %v", op.requestContentType, server.RequestHeaderYAML) - } -} - -func TestWithRequestContentTypeJSON(t *testing.T) { - op := &Op{} - opt := WithRequestContentTypeJSON() - opt(op) - - if op.requestContentType != server.RequestHeaderJSON { - t.Errorf("WithRequestContentTypeJSON() = %v, want %v", op.requestContentType, server.RequestHeaderJSON) - } -} - -func TestWithAcceptEncodingGzip(t *testing.T) { - op := &Op{} - opt := WithAcceptEncodingGzip() - opt(op) - - if op.requestAcceptEncoding != server.RequestHeaderEncodingGzip { - t.Errorf("WithAcceptEncodingGzip() = %v, want %v", op.requestAcceptEncoding, server.RequestHeaderEncodingGzip) - } -} - -func TestWithComponent(t *testing.T) { - tests := []struct { - name string - component string - }{ - { - name: "single component", - component: "test-component", - }, - { - name: "empty component name", - component: "", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - op := &Op{} - opt := WithComponent(tt.component) - opt(op) - - if op.components == nil { - t.Fatal("WithComponent() did not initialize components map") - } - - if _, exists := op.components[tt.component]; !exists { - t.Errorf("WithComponent() did not add component %q to map", tt.component) - } - }) - } - - // Test multiple components - t.Run("multiple components", func(t *testing.T) { - op := &Op{} - components := []string{"comp1", "comp2", "comp3"} - - for _, comp := range components { - WithComponent(comp)(op) - } - - if len(op.components) != len(components) { - t.Errorf("WithComponent() expected %d components, got %d", len(components), len(op.components)) - } - - for _, comp := range components { - if _, exists := op.components[comp]; !exists { - t.Errorf("WithComponent() component %q not found in map", comp) - } - } - }) -} - -func TestOp_applyOpts(t *testing.T) { - t.Run("default values", func(t *testing.T) { - op := &Op{} - err := op.applyOpts(nil) - if err != nil { - t.Errorf("applyOpts() unexpected error = %v", err) - } - - // Check default http client - if op.httpClient == nil { - t.Error("applyOpts() did not set default http client") - } - transport, ok := op.httpClient.Transport.(*http.Transport) - if !ok { - t.Error("applyOpts() did not set expected transport type") - } - if transport.TLSClientConfig.InsecureSkipVerify != true { - t.Error("applyOpts() did not set InsecureSkipVerify to true") - } - - // Check default check interval - if op.checkInterval != time.Second { - t.Errorf("applyOpts() check interval = %v, want %v", op.checkInterval, time.Second) - } - }) - - t.Run("custom values", func(t *testing.T) { - customClient := &http.Client{Timeout: time.Hour} - customInterval := 5 * time.Second - - op := &Op{} - err := op.applyOpts([]OpOption{ - WithHTTPClient(customClient), - WithCheckInterval(customInterval), - WithRequestContentTypeJSON(), - WithAcceptEncodingGzip(), - WithComponent("test"), - }) - - if err != nil { - t.Errorf("applyOpts() unexpected error = %v", err) - } - - // Verify all options were applied - if op.httpClient != customClient { - t.Error("applyOpts() did not set custom http client") - } - if op.checkInterval != customInterval { - t.Error("applyOpts() did not set custom check interval") - } - if op.requestContentType != server.RequestHeaderJSON { - t.Error("applyOpts() did not set JSON content type") - } - if op.requestAcceptEncoding != server.RequestHeaderEncodingGzip { - t.Error("applyOpts() did not set gzip encoding") - } - if _, exists := op.components["test"]; !exists { - t.Error("applyOpts() did not set component") - } - }) - - t.Run("multiple applications", func(t *testing.T) { - op := &Op{} - // Apply options multiple times to ensure last one wins - err := op.applyOpts([]OpOption{ - WithCheckInterval(time.Second), - WithCheckInterval(2 * time.Second), - WithCheckInterval(3 * time.Second), - }) - - if err != nil { - t.Errorf("applyOpts() unexpected error = %v", err) - } - - if op.checkInterval != 3*time.Second { - t.Errorf("applyOpts() check interval = %v, want %v", op.checkInterval, 3*time.Second) - } - }) -} diff --git a/client/v1/package_status.go b/client/v1/package_status.go index 4dd03f18..967583da 100644 --- a/client/v1/package_status.go +++ b/client/v1/package_status.go @@ -17,13 +17,12 @@ func GetPackageStatus(ctx context.Context, url string, opts ...OpOption) ([]pack return nil, err } - httpClient := op.httpClient req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return nil, err } - resp, err := httpClient.Do(req) + resp, err := createDefaultHTTPClient().Do(req) if err != nil { return nil, err } diff --git a/client/v1/v1.go b/client/v1/v1.go index c95abe44..20d0a531 100644 --- a/client/v1/v1.go +++ b/client/v1/v1.go @@ -3,6 +3,7 @@ package v1 import ( "compress/gzip" "context" + "crypto/tls" "encoding/json" "errors" "fmt" @@ -11,10 +12,11 @@ import ( "net/url" "strings" + "sigs.k8s.io/yaml" + v1 "github.com/leptonai/gpud/api/v1" "github.com/leptonai/gpud/errdefs" "github.com/leptonai/gpud/internal/server" - "sigs.k8s.io/yaml" ) func GetComponents(ctx context.Context, addr string, opts ...OpOption) ([]string, error) { @@ -34,7 +36,7 @@ func GetComponents(ctx context.Context, addr string, opts ...OpOption) ([]string req.Header.Set(server.RequestHeaderAcceptEncoding, op.requestAcceptEncoding) } - resp, err := op.httpClient.Do(req) + resp, err := createDefaultHTTPClient().Do(req) if err != nil { return nil, fmt.Errorf("failed to make request: %w", err) } @@ -131,7 +133,7 @@ func GetInfo(ctx context.Context, addr string, opts ...OpOption) (v1.LeptonInfo, req.Header.Set(server.RequestHeaderAcceptEncoding, op.requestAcceptEncoding) } - resp, err := op.httpClient.Do(req) + resp, err := createDefaultHTTPClient().Do(req) if err != nil { return nil, fmt.Errorf("failed to make request: %w", err) } @@ -229,7 +231,7 @@ func GetStates(ctx context.Context, addr string, opts ...OpOption) (v1.LeptonSta req.Header.Set(server.RequestHeaderAcceptEncoding, op.requestAcceptEncoding) } - resp, err := op.httpClient.Do(req) + resp, err := createDefaultHTTPClient().Do(req) if err != nil { return nil, fmt.Errorf("failed to make request: %w", err) } @@ -316,7 +318,7 @@ func GetEvents(ctx context.Context, addr string, opts ...OpOption) (v1.LeptonEve req.Header.Set(server.RequestHeaderAcceptEncoding, op.requestAcceptEncoding) } - resp, err := op.httpClient.Do(req) + resp, err := createDefaultHTTPClient().Do(req) if err != nil { return nil, fmt.Errorf("failed to make request: %w", err) } @@ -399,7 +401,7 @@ func GetMetrics(ctx context.Context, addr string, opts ...OpOption) (v1.LeptonMe req.Header.Set(server.RequestHeaderAcceptEncoding, op.requestAcceptEncoding) } - resp, err := op.httpClient.Do(req) + resp, err := createDefaultHTTPClient().Do(req) if err != nil { return nil, fmt.Errorf("failed to make request: %w", err) } @@ -464,3 +466,11 @@ func ReadMetrics(rd io.Reader, opts ...OpOption) (v1.LeptonMetrics, error) { return metrics, nil } + +func createDefaultHTTPClient() *http.Client { + return &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + } +}