github.com/kubeshop/testkube@v1.17.23/internal/app/api/v1/server.go (about) 1 package v1 2 3 import ( 4 "context" 5 "io" 6 "net" 7 "os" 8 "reflect" 9 "strconv" 10 "sync" 11 "syscall" 12 "time" 13 14 "github.com/pkg/errors" 15 16 "github.com/kubeshop/testkube/internal/common" 17 "github.com/kubeshop/testkube/internal/config" 18 "github.com/kubeshop/testkube/pkg/api/v1/testkube" 19 repoConfig "github.com/kubeshop/testkube/pkg/repository/config" 20 "github.com/kubeshop/testkube/pkg/tcl/checktcl" 21 22 "github.com/kubeshop/testkube/pkg/version" 23 24 "github.com/kubeshop/testkube/pkg/datefilter" 25 "github.com/kubeshop/testkube/pkg/repository/result" 26 "github.com/kubeshop/testkube/pkg/repository/testresult" 27 28 "k8s.io/client-go/kubernetes" 29 30 "github.com/gofiber/fiber/v2" 31 "github.com/gofiber/fiber/v2/middleware/cors" 32 "github.com/gofiber/fiber/v2/middleware/proxy" 33 "github.com/kelseyhightower/envconfig" 34 35 executorsclientv1 "github.com/kubeshop/testkube-operator/pkg/client/executors/v1" 36 templatesclientv1 "github.com/kubeshop/testkube-operator/pkg/client/templates/v1" 37 testsclientv3 "github.com/kubeshop/testkube-operator/pkg/client/tests/v3" 38 testsourcesclientv1 "github.com/kubeshop/testkube-operator/pkg/client/testsources/v1" 39 testsuitesclientv3 "github.com/kubeshop/testkube-operator/pkg/client/testsuites/v3" 40 testkubeclientset "github.com/kubeshop/testkube-operator/pkg/clientset/versioned" 41 "github.com/kubeshop/testkube/internal/app/api/metrics" 42 "github.com/kubeshop/testkube/pkg/event" 43 "github.com/kubeshop/testkube/pkg/event/bus" 44 "github.com/kubeshop/testkube/pkg/event/kind/cdevent" 45 "github.com/kubeshop/testkube/pkg/event/kind/slack" 46 "github.com/kubeshop/testkube/pkg/event/kind/webhook" 47 ws "github.com/kubeshop/testkube/pkg/event/kind/websocket" 48 "github.com/kubeshop/testkube/pkg/executor/client" 49 "github.com/kubeshop/testkube/pkg/featureflags" 50 logsclient "github.com/kubeshop/testkube/pkg/logs/client" 51 "github.com/kubeshop/testkube/pkg/oauth" 52 "github.com/kubeshop/testkube/pkg/scheduler" 53 "github.com/kubeshop/testkube/pkg/secret" 54 "github.com/kubeshop/testkube/pkg/server" 55 "github.com/kubeshop/testkube/pkg/storage" 56 "github.com/kubeshop/testkube/pkg/telemetry" 57 "github.com/kubeshop/testkube/pkg/utils/text" 58 ) 59 60 const ( 61 HeartbeatInterval = time.Hour 62 DefaultHttpBodyLimit = 1 * 1024 * 1024 * 1024 // 1GB - needed for file uploads 63 ) 64 65 func NewTestkubeAPI( 66 namespace string, 67 testExecutionResults result.Repository, 68 testsuiteExecutionsResults testresult.Repository, 69 testsClient *testsclientv3.TestsClient, 70 executorsClient *executorsclientv1.ExecutorsClient, 71 testsuitesClient *testsuitesclientv3.TestSuitesClient, 72 secretClient *secret.Client, 73 webhookClient *executorsclientv1.WebhooksClient, 74 clientset kubernetes.Interface, 75 testkubeClientset testkubeclientset.Interface, 76 testsourcesClient *testsourcesclientv1.TestSourcesClient, 77 configMap repoConfig.Repository, 78 clusterId string, 79 eventsEmitter *event.Emitter, 80 executor client.Executor, 81 containerExecutor client.Executor, 82 metrics metrics.Metrics, 83 scheduler *scheduler.Scheduler, 84 slackLoader *slack.SlackLoader, 85 storage storage.Client, 86 graphqlPort string, 87 artifactsStorage storage.ArtifactsStorage, 88 templatesClient *templatesclientv1.TemplatesClient, 89 cdeventsTarget string, 90 dashboardURI string, 91 helmchartVersion string, 92 mode string, 93 eventsBus bus.Bus, 94 enableSecretsEndpoint bool, 95 ff featureflags.FeatureFlags, 96 logsStream logsclient.Stream, 97 logGrpcClient logsclient.StreamGetter, 98 subscriptionChecker checktcl.SubscriptionChecker, 99 disableSecretCreation bool, 100 serviceAccountNames map[string]string, 101 ) TestkubeAPI { 102 103 var httpConfig server.Config 104 err := envconfig.Process("APISERVER", &httpConfig) 105 // Do we want to panic here or just ignore the error 106 if err != nil { 107 panic(err) 108 } 109 110 httpConfig.ClusterID = clusterId 111 httpConfig.Http.BodyLimit = httpConfig.HttpBodyLimit 112 if httpConfig.HttpBodyLimit == 0 { 113 httpConfig.Http.BodyLimit = DefaultHttpBodyLimit 114 } 115 116 s := TestkubeAPI{ 117 HTTPServer: server.NewServer(httpConfig), 118 TestExecutionResults: testsuiteExecutionsResults, 119 ExecutionResults: testExecutionResults, 120 TestsClient: testsClient, 121 ExecutorsClient: executorsClient, 122 SecretClient: secretClient, 123 Clientset: clientset, 124 TestsSuitesClient: testsuitesClient, 125 TestKubeClientset: testkubeClientset, 126 Metrics: metrics, 127 Events: eventsEmitter, 128 WebhooksClient: webhookClient, 129 TestSourcesClient: testsourcesClient, 130 Namespace: namespace, 131 ConfigMap: configMap, 132 Executor: executor, 133 ContainerExecutor: containerExecutor, 134 scheduler: scheduler, 135 slackLoader: slackLoader, 136 Storage: storage, 137 graphqlPort: graphqlPort, 138 ArtifactsStorage: artifactsStorage, 139 TemplatesClient: templatesClient, 140 dashboardURI: dashboardURI, 141 helmchartVersion: helmchartVersion, 142 mode: mode, 143 eventsBus: eventsBus, 144 enableSecretsEndpoint: enableSecretsEndpoint, 145 featureFlags: ff, 146 logsStream: logsStream, 147 logGrpcClient: logGrpcClient, 148 SubscriptionChecker: subscriptionChecker, 149 disableSecretCreation: disableSecretCreation, 150 LabelSources: common.Ptr(make([]LabelSource, 0)), 151 serviceAccountNames: serviceAccountNames, 152 } 153 154 // will be reused in websockets handler 155 s.WebsocketLoader = ws.NewWebsocketLoader() 156 157 s.Events.Loader.Register(webhook.NewWebhookLoader(s.Log, webhookClient, templatesClient)) 158 s.Events.Loader.Register(s.WebsocketLoader) 159 s.Events.Loader.Register(s.slackLoader) 160 161 if cdeventsTarget != "" { 162 cdeventLoader, err := cdevent.NewCDEventLoader(cdeventsTarget, clusterId, namespace, dashboardURI, testkube.AllEventTypes) 163 if err == nil { 164 s.Events.Loader.Register(cdeventLoader) 165 } else { 166 s.Log.Debug("cdevents init error", "error", err.Error()) 167 } 168 } 169 170 s.InitEnvs() 171 s.InitRoutes() 172 173 return s 174 } 175 176 type TestkubeAPI struct { 177 server.HTTPServer 178 ExecutionResults result.Repository 179 TestExecutionResults testresult.Repository 180 Executor client.Executor 181 ContainerExecutor client.Executor 182 TestsSuitesClient *testsuitesclientv3.TestSuitesClient 183 TestsClient *testsclientv3.TestsClient 184 ExecutorsClient *executorsclientv1.ExecutorsClient 185 SecretClient *secret.Client 186 WebhooksClient *executorsclientv1.WebhooksClient 187 TestKubeClientset testkubeclientset.Interface 188 TestSourcesClient *testsourcesclientv1.TestSourcesClient 189 Metrics metrics.Metrics 190 Storage storage.Client 191 storageParams storageParams 192 Namespace string 193 oauthParams oauthParams 194 WebsocketLoader *ws.WebsocketLoader 195 Events *event.Emitter 196 ConfigMap repoConfig.Repository 197 scheduler *scheduler.Scheduler 198 Clientset kubernetes.Interface 199 slackLoader *slack.SlackLoader 200 graphqlPort string 201 ArtifactsStorage storage.ArtifactsStorage 202 TemplatesClient *templatesclientv1.TemplatesClient 203 dashboardURI string 204 helmchartVersion string 205 mode string 206 eventsBus bus.Bus 207 enableSecretsEndpoint bool 208 featureFlags featureflags.FeatureFlags 209 logsStream logsclient.Stream 210 logGrpcClient logsclient.StreamGetter 211 proContext *config.ProContext 212 SubscriptionChecker checktcl.SubscriptionChecker 213 disableSecretCreation bool 214 LabelSources *[]LabelSource 215 serviceAccountNames map[string]string 216 } 217 218 type storageParams struct { 219 SSL bool `envconfig:"STORAGE_SSL" default:"false"` 220 SkipVerify bool `envconfig:"STORAGE_SKIP_VERIFY" default:"false"` 221 CertFile string `envconfig:"STORAGE_CERT_FILE"` 222 KeyFile string `envconfig:"STORAGE_KEY_FILE"` 223 CAFile string `envconfig:"STORAGE_CA_FILE"` 224 Endpoint string 225 AccessKeyId string 226 SecretAccessKey string 227 Region string 228 Token string 229 Bucket string 230 } 231 232 type oauthParams struct { 233 ClientID string 234 ClientSecret string 235 Provider oauth.ProviderType 236 Scopes string 237 } 238 239 func (s *TestkubeAPI) WithFeatureFlags(ff featureflags.FeatureFlags) *TestkubeAPI { 240 s.featureFlags = ff 241 return s 242 } 243 244 type LabelSource interface { 245 ListLabels() (map[string][]string, error) 246 } 247 248 func (s *TestkubeAPI) WithLabelSources(l ...LabelSource) { 249 *s.LabelSources = append(*s.LabelSources, l...) 250 } 251 252 // SendTelemetryStartEvent sends anonymous start event to telemetry trackers 253 func (s TestkubeAPI) SendTelemetryStartEvent(ctx context.Context, ch chan struct{}) { 254 go func() { 255 defer func() { 256 ch <- struct{}{} 257 }() 258 259 telemetryEnabled, err := s.ConfigMap.GetTelemetryEnabled(ctx) 260 if err != nil { 261 s.Log.Errorw("error getting config map", "error", err) 262 } 263 264 if !telemetryEnabled { 265 return 266 } 267 268 out, err := telemetry.SendServerStartEvent(s.Config.ClusterID, version.Version) 269 if err != nil { 270 s.Log.Debug("telemetry send error", "error", err.Error()) 271 } else { 272 s.Log.Debugw("sending telemetry server start event", "output", out) 273 } 274 }() 275 } 276 277 // InitEnvs initializes api server settings 278 func (s *TestkubeAPI) InitEnvs() { 279 if err := envconfig.Process("STORAGE", &s.storageParams); err != nil { 280 s.Log.Infow("Processing STORAGE environment config", err) 281 } 282 283 if err := envconfig.Process("TESTKUBE_OAUTH", &s.oauthParams); err != nil { 284 s.Log.Infow("Processing TESTKUBE_OAUTH environment config", err) 285 } 286 } 287 288 func (s *TestkubeAPI) InitRoutes() { 289 s.Routes.Static("/api-docs", "./api/v1") 290 s.Routes.Use(cors.New()) 291 s.Routes.Use(s.AuthHandler()) 292 293 s.Routes.Get("/info", s.InfoHandler()) 294 s.Routes.Get("/routes", s.RoutesHandler()) 295 s.Routes.Get("/debug", s.DebugHandler()) 296 297 root := s.Routes 298 299 executors := root.Group("/executors") 300 301 executors.Post("/", s.CreateExecutorHandler()) 302 executors.Get("/", s.ListExecutorsHandler()) 303 executors.Get("/:name", s.GetExecutorHandler()) 304 executors.Patch("/:name", s.UpdateExecutorHandler()) 305 executors.Delete("/:name", s.DeleteExecutorHandler()) 306 executors.Delete("/", s.DeleteExecutorsHandler()) 307 308 executorByTypes := root.Group("/executor-by-types") 309 executorByTypes.Get("/", s.GetExecutorByTestTypeHandler()) 310 311 webhooks := root.Group("/webhooks") 312 313 webhooks.Post("/", s.CreateWebhookHandler()) 314 webhooks.Patch("/:name", s.UpdateWebhookHandler()) 315 webhooks.Get("/", s.ListWebhooksHandler()) 316 webhooks.Get("/:name", s.GetWebhookHandler()) 317 webhooks.Delete("/:name", s.DeleteWebhookHandler()) 318 webhooks.Delete("/", s.DeleteWebhooksHandler()) 319 320 executions := root.Group("/executions") 321 322 executions.Get("/", s.ListExecutionsHandler()) 323 executions.Post("/", s.ExecuteTestsHandler()) 324 executions.Get("/:executionID", s.GetExecutionHandler()) 325 executions.Get("/:executionID/artifacts", s.ListArtifactsHandler()) 326 executions.Get("/:executionID/logs", s.ExecutionLogsHandler()) 327 executions.Get("/:executionID/logs/stream", s.ExecutionLogsStreamHandler()) 328 executions.Get("/:executionID/logs/v2", s.ExecutionLogsHandlerV2()) 329 executions.Get("/:executionID/logs/stream/v2", s.ExecutionLogsStreamHandlerV2()) 330 executions.Get("/:executionID/artifacts/:filename", s.GetArtifactHandler()) 331 executions.Get("/:executionID/artifact-archive", s.GetArtifactArchiveHandler()) 332 333 tests := root.Group("/tests") 334 335 tests.Get("/", s.ListTestsHandler()) 336 tests.Post("/", s.CreateTestHandler()) 337 tests.Patch("/:id", s.UpdateTestHandler()) 338 tests.Delete("/", s.DeleteTestsHandler()) 339 340 tests.Get("/:id", s.GetTestHandler()) 341 tests.Delete("/:id", s.DeleteTestHandler()) 342 tests.Post("/:id/abort", s.AbortTestHandler()) 343 344 tests.Get("/:id/metrics", s.TestMetricsHandler()) 345 346 tests.Post("/:id/executions", s.ExecuteTestsHandler()) 347 348 tests.Get("/:id/executions", s.ListExecutionsHandler()) 349 tests.Get("/:id/executions/:executionID", s.GetExecutionHandler()) 350 tests.Patch("/:id/executions/:executionID", s.AbortExecutionHandler()) 351 352 testWithExecutions := s.Routes.Group("/test-with-executions") 353 testWithExecutions.Get("/", s.ListTestWithExecutionsHandler()) 354 testWithExecutions.Get("/:id", s.GetTestWithExecutionHandler()) 355 356 testsuites := root.Group("/test-suites") 357 358 testsuites.Post("/", s.CreateTestSuiteHandler()) 359 testsuites.Patch("/:id", s.UpdateTestSuiteHandler()) 360 testsuites.Get("/", s.ListTestSuitesHandler()) 361 testsuites.Delete("/", s.DeleteTestSuitesHandler()) 362 testsuites.Get("/:id", s.GetTestSuiteHandler()) 363 testsuites.Delete("/:id", s.DeleteTestSuiteHandler()) 364 testsuites.Post("/:id/abort", s.AbortTestSuiteHandler()) 365 366 testsuites.Post("/:id/executions", s.ExecuteTestSuitesHandler()) 367 testsuites.Get("/:id/executions", s.ListTestSuiteExecutionsHandler()) 368 testsuites.Get("/:id/executions/:executionID", s.GetTestSuiteExecutionHandler()) 369 testsuites.Get("/:id/executions/:executionID/artifacts", s.ListTestSuiteArtifactsHandler()) 370 testsuites.Patch("/:id/executions/:executionID", s.AbortTestSuiteExecutionHandler()) 371 372 testsuites.Get("/:id/tests", s.ListTestSuiteTestsHandler()) 373 374 testsuites.Get("/:id/metrics", s.TestSuiteMetricsHandler()) 375 376 testSuiteExecutions := root.Group("/test-suite-executions") 377 testSuiteExecutions.Get("/", s.ListTestSuiteExecutionsHandler()) 378 testSuiteExecutions.Post("/", s.ExecuteTestSuitesHandler()) 379 testSuiteExecutions.Get("/:executionID", s.GetTestSuiteExecutionHandler()) 380 testSuiteExecutions.Get("/:executionID/artifacts", s.ListTestSuiteArtifactsHandler()) 381 testSuiteExecutions.Patch("/:executionID", s.AbortTestSuiteExecutionHandler()) 382 383 testSuiteWithExecutions := root.Group("/test-suite-with-executions") 384 testSuiteWithExecutions.Get("/", s.ListTestSuiteWithExecutionsHandler()) 385 testSuiteWithExecutions.Get("/:id", s.GetTestSuiteWithExecutionHandler()) 386 387 testTriggers := root.Group("/triggers") 388 testTriggers.Get("/", s.ListTestTriggersHandler()) 389 testTriggers.Post("/", s.CreateTestTriggerHandler()) 390 testTriggers.Patch("/", s.BulkUpdateTestTriggersHandler()) 391 testTriggers.Delete("/", s.DeleteTestTriggersHandler()) 392 testTriggers.Get("/:id", s.GetTestTriggerHandler()) 393 testTriggers.Patch("/:id", s.UpdateTestTriggerHandler()) 394 testTriggers.Delete("/:id", s.DeleteTestTriggerHandler()) 395 396 keymap := root.Group("/keymap") 397 keymap.Get("/triggers", s.GetTestTriggerKeyMapHandler()) 398 399 testsources := root.Group("/test-sources") 400 testsources.Post("/", s.CreateTestSourceHandler()) 401 testsources.Get("/", s.ListTestSourcesHandler()) 402 testsources.Patch("/", s.ProcessTestSourceBatchHandler()) 403 testsources.Get("/:name", s.GetTestSourceHandler()) 404 testsources.Patch("/:name", s.UpdateTestSourceHandler()) 405 testsources.Delete("/:name", s.DeleteTestSourceHandler()) 406 testsources.Delete("/", s.DeleteTestSourcesHandler()) 407 408 templates := root.Group("/templates") 409 410 templates.Post("/", s.CreateTemplateHandler()) 411 templates.Patch("/:name", s.UpdateTemplateHandler()) 412 templates.Get("/", s.ListTemplatesHandler()) 413 templates.Get("/:name", s.GetTemplateHandler()) 414 templates.Delete("/:name", s.DeleteTemplateHandler()) 415 templates.Delete("/", s.DeleteTemplatesHandler()) 416 417 labels := root.Group("/labels") 418 labels.Get("/", s.ListLabelsHandler()) 419 420 slack := root.Group("/slack") 421 slack.Get("/", s.OauthHandler()) 422 423 events := root.Group("/events") 424 events.Post("/flux", s.FluxEventHandler()) 425 events.Get("/stream", s.EventsStreamHandler()) 426 427 configs := root.Group("/config") 428 configs.Get("/", s.GetConfigsHandler()) 429 configs.Patch("/", s.UpdateConfigsHandler()) 430 431 debug := root.Group("/debug") 432 debug.Get("/listeners", s.GetDebugListenersHandler()) 433 434 files := root.Group("/uploads") 435 files.Post("/", s.UploadFiles()) 436 437 if s.enableSecretsEndpoint { 438 files := root.Group("/secrets") 439 files.Get("/", s.ListSecretsHandler()) 440 } 441 442 repositories := root.Group("/repositories") 443 repositories.Post("/", s.ValidateRepositoryHandler()) 444 445 // mount dashboard on /ui 446 dashboardURI := os.Getenv("TESTKUBE_DASHBOARD_URI") 447 if dashboardURI == "" { 448 dashboardURI = "http://testkube-dashboard" 449 } 450 s.Log.Infow("dashboard uri", "uri", dashboardURI) 451 s.Mux.All("/", proxy.Forward(dashboardURI)) 452 453 // set up proxy for the internal GraphQL server 454 s.Mux.All("/graphql", func(c *fiber.Ctx) error { 455 // Connect to server 456 serverConn, err := net.Dial("tcp", ":"+s.graphqlPort) 457 if err != nil { 458 s.Log.Errorw("could not connect to GraphQL server as a proxy", "error", err) 459 return err 460 } 461 462 // Resend headers to the server 463 _, err = serverConn.Write(c.Request().Header.Header()) 464 if err != nil { 465 serverConn.Close() 466 s.Log.Errorw("error while sending headers to GraphQL server", "error", err) 467 return err 468 } 469 470 // Resend body to the server 471 _, err = serverConn.Write(c.Body()) 472 if err != nil && err != io.EOF { 473 serverConn.Close() 474 s.Log.Errorw("error while reading GraphQL client data", "error", err) 475 return err 476 } 477 478 // Handle optional WebSocket connection 479 c.Context().HijackSetNoResponse(true) 480 c.Context().Hijack(func(clientConn net.Conn) { 481 // Close the connection afterward 482 defer serverConn.Close() 483 defer clientConn.Close() 484 485 // Extract Unix connection 486 serverSock, ok := serverConn.(*net.TCPConn) 487 if !ok { 488 s.Log.Errorw("error while building TCPConn out ouf serverConn", "error", err) 489 return 490 } 491 clientSock, ok := reflect.Indirect(reflect.ValueOf(clientConn)).FieldByName("Conn").Interface().(*net.TCPConn) 492 if !ok { 493 s.Log.Errorw("error while building TCPConn out of hijacked connection", "error", err) 494 return 495 } 496 497 // Duplex communication between client and GraphQL server 498 var wg sync.WaitGroup 499 wg.Add(2) 500 go func() { 501 defer wg.Done() 502 _, err := io.Copy(clientSock, serverSock) 503 if err != nil && err != io.EOF && !errors.Is(err, syscall.ECONNRESET) && !errors.Is(err, syscall.EPIPE) { 504 s.Log.Errorw("error while reading GraphQL client data", "error", err) 505 } 506 serverSock.CloseWrite() 507 }() 508 go func() { 509 defer wg.Done() 510 _, err = io.Copy(serverSock, clientSock) 511 if err != nil && err != io.EOF { 512 s.Log.Errorw("error while reading GraphQL server data", "error", err) 513 } 514 clientSock.CloseWrite() 515 }() 516 wg.Wait() 517 }) 518 return nil 519 }) 520 } 521 522 func (s TestkubeAPI) StartTelemetryHeartbeats(ctx context.Context, ch chan struct{}) { 523 go func() { 524 <-ch 525 526 ticker := time.NewTicker(HeartbeatInterval) 527 for { 528 telemetryEnabled, err := s.ConfigMap.GetTelemetryEnabled(ctx) 529 if err != nil { 530 s.Log.Errorw("error getting config map", "error", err) 531 } 532 if telemetryEnabled { 533 l := s.Log.With("measurmentId", telemetry.TestkubeMeasurementID, "secret", text.Obfuscate(telemetry.TestkubeMeasurementSecret)) 534 host, err := os.Hostname() 535 if err != nil { 536 l.Debugw("getting hostname error", "hostname", host, "error", err) 537 } 538 out, err := telemetry.SendHeartbeatEvent(host, version.Version, s.Config.ClusterID) 539 if err != nil { 540 l.Debugw("sending heartbeat telemetry event error", "error", err) 541 } else { 542 l.Debugw("sending heartbeat telemetry event", "output", out) 543 } 544 545 } 546 <-ticker.C 547 } 548 }() 549 } 550 551 // TODO should we use single generic filter for all list based resources ? 552 // currently filters for e.g. tests are done "by hand" 553 func getFilterFromRequest(c *fiber.Ctx) result.Filter { 554 555 filter := result.NewExecutionsFilter() 556 557 // id for /tests/ID/executions 558 testName := c.Params("id", "") 559 if testName == "" { 560 // query param for /executions?testName 561 testName = c.Query("testName", "") 562 } 563 564 if testName != "" { 565 filter = filter.WithTestName(testName) 566 } 567 568 textSearch := c.Query("textSearch", "") 569 if textSearch != "" { 570 filter = filter.WithTextSearch(textSearch) 571 } 572 573 page, err := strconv.Atoi(c.Query("page", "")) 574 if err == nil { 575 filter = filter.WithPage(page) 576 } 577 578 pageSize, err := strconv.Atoi(c.Query("pageSize", "")) 579 if err == nil && pageSize != 0 { 580 filter = filter.WithPageSize(pageSize) 581 } 582 583 status := c.Query("status", "") 584 if status != "" { 585 filter = filter.WithStatus(status) 586 } 587 588 objectType := c.Query("type", "") 589 if objectType != "" { 590 filter = filter.WithType(objectType) 591 } 592 593 last, err := strconv.Atoi(c.Query("last", "0")) 594 if err == nil && last != 0 { 595 filter = filter.WithLastNDays(last) 596 } 597 598 dFilter := datefilter.NewDateFilter(c.Query("startDate", ""), c.Query("endDate", "")) 599 if dFilter.IsStartValid { 600 filter = filter.WithStartDate(dFilter.Start) 601 } 602 603 if dFilter.IsEndValid { 604 filter = filter.WithEndDate(dFilter.End) 605 } 606 607 selector := c.Query("selector") 608 if selector != "" { 609 filter = filter.WithSelector(selector) 610 } 611 612 return filter 613 } 614 615 // WithProContext sets pro context for the API 616 func (s *TestkubeAPI) WithProContext(proContext *config.ProContext) *TestkubeAPI { 617 s.proContext = proContext 618 return s 619 } 620 621 // WithSubscriptionChecker sets subscription checker for the API 622 // This is used to check if Pro/Enterprise subscription is valid 623 func (s *TestkubeAPI) WithSubscriptionChecker(subscriptionChecker checktcl.SubscriptionChecker) *TestkubeAPI { 624 s.SubscriptionChecker = subscriptionChecker 625 return s 626 }