diff --git a/go.mod b/go.mod index db2bf127b74..15e320646b1 100644 --- a/go.mod +++ b/go.mod @@ -70,7 +70,6 @@ require ( github.com/onsi/ginkgo/v2 v2.21.0 github.com/onsi/gomega v1.35.1 github.com/open-policy-agent/cert-controller v0.12.0 - github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.20.5 github.com/prometheus/client_model v0.6.1 diff --git a/go.sum b/go.sum index f1779b617be..e9a84f074a4 100644 --- a/go.sum +++ b/go.sum @@ -2206,8 +2206,6 @@ github.com/otiai10/mint v1.3.3/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= -github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= diff --git a/pkg/scalers/predictkube_scaler_test.go b/pkg/scalers/predictkube_scaler_test.go index 4872eb32522..57be8079450 100644 --- a/pkg/scalers/predictkube_scaler_test.go +++ b/pkg/scalers/predictkube_scaler_test.go @@ -6,12 +6,13 @@ import ( "log" "math/rand" "net" + "net/http" + "net/http/httptest" "testing" "time" libsSrv "github.com/dysnix/predictkube-libs/external/grpc/server" pb "github.com/dysnix/predictkube-proto/external/proto/services" - "github.com/phayes/freeport" "github.com/stretchr/testify/assert" "google.golang.org/grpc" "k8s.io/apimachinery/pkg/api/resource" @@ -19,6 +20,11 @@ import ( "github.com/kedacore/keda/v2/pkg/scalers/scalersconfig" ) +const ( + defaultTestPort = 50051 + testAPIKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0IENyZWF0ZUNsaWVudCIsImV4cCI6MTY0NjkxNzI3Nywic3ViIjoiODM4NjY5ODAtM2UzNS0xMWVjLTlmMjQtYWNkZTQ4MDAxMTIyIn0.5QEuO6_ysdk2abGvk3Xp7Q25M4H4pIFXeqP2E7n9rKI" // nosemgrep +) + type server struct { grpcSrv *grpc.Server listener net.Listener @@ -33,47 +39,6 @@ func (s *server) GetPredictMetric(_ context.Context, _ *pb.ReqGetPredictMetric) }, nil } -func (s *server) start() <-chan error { - errCh := make(chan error, 1) - - go func() { - defer close(errCh) - - var ( - err error - ) - - s.port, err = freeport.GetFreePort() - if err != nil { - log.Fatalf("Could not get free port for init mock grpc server: %s", err) - } - - serverURL := fmt.Sprintf("0.0.0.0:%d", s.port) - if s.listener == nil { - var err error - s.listener, err = net.Listen("tcp4", serverURL) - - if err != nil { - log.Println("starting grpc server with error") - - errCh <- err - return - } - } - - log.Printf("🚀 starting mock grpc server. On host 0.0.0.0, with port: %d", s.port) - - if err := s.grpcSrv.Serve(s.listener); err != nil { - log.Println(err, "serving grpc server with error") - - errCh <- err - return - } - }() - - return errCh -} - func (s *server) stop() error { s.grpcSrv.GracefulStop() return libsSrv.CheckNetErrClosing(s.listener.Close()) @@ -81,8 +46,10 @@ func (s *server) stop() error { func runMockGrpcPredictServer() (*server, *grpc.Server) { grpcServer := grpc.NewServer() - - mockGrpcServer := &server{grpcSrv: grpcServer} + mockGrpcServer := &server{ + grpcSrv: grpcServer, + port: defaultTestPort, + } defer func() { if r := recover(); r != nil { @@ -91,21 +58,27 @@ func runMockGrpcPredictServer() (*server, *grpc.Server) { } }() + pb.RegisterMlEngineServiceServer(grpcServer, mockGrpcServer) + + serverURL := fmt.Sprintf("0.0.0.0:%d", mockGrpcServer.port) + listener, err := net.Listen("tcp4", serverURL) + if err != nil { + log.Fatalf("Failed to listen: %v", err) + } + mockGrpcServer.listener = listener + go func() { - for errCh := range mockGrpcServer.start() { - if errCh != nil { - log.Printf("GRPC server listen error: %3v", errCh) - } + log.Printf("🚀 starting mock grpc server. On host 0.0.0.0, with port: %d", mockGrpcServer.port) + if err := grpcServer.Serve(listener); err != nil { + log.Printf("GRPC server listen error: %v", err) } }() - pb.RegisterMlEngineServiceServer(grpcServer, mockGrpcServer) + time.Sleep(time.Second) return mockGrpcServer, grpcServer } -const testAPIKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0ZXN0IENyZWF0ZUNsaWVudCIsImV4cCI6MTY0NjkxNzI3Nywic3ViIjoiODM4NjY5ODAtM2UzNS0xMWVjLTlmMjQtYWNkZTQ4MDAxMTIyIn0.5QEuO6_ysdk2abGvk3Xp7Q25M4H4pIFXeqP2E7n9rKI" - type predictKubeMetadataTestData struct { metadata map[string]string authParams map[string]string @@ -126,13 +99,11 @@ var testPredictKubeMetadata = []predictKubeMetadataTestData{ // malformed threshold { map[string]string{"predictHorizon": "2h", "historyTimeWindow": "7d", "prometheusAddress": "http://localhost:9090", "queryStep": "2m", "threshold": "one", "query": "up"}, - map[string]string{"apiKey": testAPIKey}, true, }, // malformed activation threshold { map[string]string{"predictHorizon": "2h", "historyTimeWindow": "7d", "prometheusAddress": "http://localhost:9090", "queryStep": "2m", "threshold": "1", "activationThreshold": "one", "query": "up"}, - map[string]string{"apiKey": testAPIKey}, true, }, // missing query @@ -167,18 +138,45 @@ var predictKubeMetricIdentifiers = []predictKubeMetricIdentifier{ func TestPredictKubeGetMetricSpecForScaling(t *testing.T) { mockPredictServer, grpcServer := runMockGrpcPredictServer() + if mockPredictServer == nil || grpcServer == nil { + t.Fatal("Failed to start mock server") + } + defer func() { _ = mockPredictServer.stop() grpcServer.GracefulStop() }() + err := waitForServer(fmt.Sprintf("0.0.0.0:%d", defaultTestPort), 5*time.Second) + if err != nil { + t.Fatalf("Server failed to start: %v", err) + } + + // Mock Prometheus server + mockPrometheus := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"status":"success","data":{"resultType":"vector","result":[]}}`)) + })) + defer mockPrometheus.Close() + mlEngineHost = "0.0.0.0" mlEnginePort = mockPredictServer.port for _, testData := range predictKubeMetricIdentifiers { + metadata := make(map[string]string) + for k, v := range testData.metadataTestData.metadata { + if k == "prometheusAddress" { + metadata[k] = mockPrometheus.URL + } else { + metadata[k] = v + } + } + mockPredictKubeScaler, err := NewPredictKubeScaler( - context.Background(), &scalersconfig.ScalerConfig{ - TriggerMetadata: testData.metadataTestData.metadata, + context.Background(), + &scalersconfig.ScalerConfig{ + TriggerMetadata: metadata, AuthParams: testData.metadataTestData.authParams, TriggerIndex: testData.triggerIndex, }, @@ -191,39 +189,96 @@ func TestPredictKubeGetMetricSpecForScaling(t *testing.T) { t.Error("Wrong External metric source name:", metricName) return } + } +} - t.Log(metricSpec) +func waitForServer(address string, timeout time.Duration) error { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + for { + select { + case <-ctx.Done(): + return fmt.Errorf("timeout waiting for server") + default: + conn, err := net.Dial("tcp", address) + if err == nil { + conn.Close() + return nil + } + time.Sleep(100 * time.Millisecond) + } } } func TestPredictKubeGetMetrics(t *testing.T) { - grpcConf.Conn.Insecure = true - mockPredictServer, grpcServer := runMockGrpcPredictServer() - <-time.After(time.Second * 3) + if mockPredictServer == nil || grpcServer == nil { + t.Fatal("Failed to start mock server") + } + defer func() { _ = mockPredictServer.stop() grpcServer.GracefulStop() }() + err := waitForServer(fmt.Sprintf("0.0.0.0:%d", defaultTestPort), 5*time.Second) + if err != nil { + t.Fatalf("Server failed to start: %v", err) + } + + // Mock Prometheus server + mockPrometheus := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"status":"success","data":{"resultType":"vector","result":[]}}`)) + })) + defer mockPrometheus.Close() + + grpcConf.Conn.Insecure = true mlEngineHost = "0.0.0.0" - mlEnginePort = mockPredictServer.port + mlEnginePort = defaultTestPort for _, testData := range predictKubeMetricIdentifiers { - mockPredictKubeScaler, err := NewPredictKubeScaler( - context.Background(), &scalersconfig.ScalerConfig{ - TriggerMetadata: testData.metadataTestData.metadata, - AuthParams: testData.metadataTestData.authParams, - TriggerIndex: testData.triggerIndex, - }, - ) - assert.NoError(t, err) + t.Run(fmt.Sprintf("trigger_index_%d", testData.triggerIndex), func(t *testing.T) { + metadata := make(map[string]string) + for k, v := range testData.metadataTestData.metadata { + if k == "prometheusAddress" { + metadata[k] = mockPrometheus.URL + } else { + metadata[k] = v + } + } - result, _, err := mockPredictKubeScaler.GetMetricsAndActivity(context.Background(), predictKubeMetricPrefix) - assert.NoError(t, err) - assert.Equal(t, len(result), 1) - assert.Equal(t, result[0].Value, *resource.NewMilliQuantity(mockPredictServer.val*1000, resource.DecimalSI)) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + mockPredictKubeScaler, err := NewPredictKubeScaler( + ctx, + &scalersconfig.ScalerConfig{ + TriggerMetadata: metadata, + AuthParams: testData.metadataTestData.authParams, + TriggerIndex: testData.triggerIndex, + }, + ) + if err != nil { + t.Fatalf("Failed to create scaler: %v", err) + } + + result, _, err := mockPredictKubeScaler.GetMetricsAndActivity(ctx, predictKubeMetricPrefix) + if err != nil { + t.Fatalf("Failed to get metrics: %v", err) + } + + if len(result) == 0 { + t.Fatal("Expected non-empty result") + } - t.Logf("get: %v, want: %v, predictMetric: %d", result[0].Value, *resource.NewQuantity(mockPredictServer.val, resource.DecimalSI), mockPredictServer.val) + assert.Equal(t, result[0].Value, *resource.NewMilliQuantity(mockPredictServer.val*1000, resource.DecimalSI)) + t.Logf("get: %v, want: %v, predictMetric: %d", + result[0].Value, + *resource.NewQuantity(mockPredictServer.val, resource.DecimalSI), + mockPredictServer.val) + }) } } diff --git a/vendor/modules.txt b/vendor/modules.txt index ba0780ef7e4..49de67aadd4 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1204,7 +1204,6 @@ github.com/onsi/gomega/types # github.com/open-policy-agent/cert-controller v0.12.0 ## explicit; go 1.22.0 github.com/open-policy-agent/cert-controller/pkg/rotator -# github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 ## explicit github.com/phayes/freeport # github.com/pierrec/lz4/v4 v4.1.21