From c10fb395cb568d4722e427a196e5292ffa168883 Mon Sep 17 00:00:00 2001 From: ewezy Date: Thu, 8 Sep 2022 17:39:42 +0800 Subject: [PATCH 01/26] Add route_name_path to standard ensembler config --- api/api/openapi.bundle.yaml | 9 +++++++++ api/api/specs/routers.yaml | 4 ++++ api/turing/cluster/servicebuilder/router.go | 1 + api/turing/models/ensembler.go | 1 + .../router_configmap_standard_ensembler.yml | 1 + .../router_configmap_traffic_splitting.yml | 3 +++ 6 files changed, 19 insertions(+) diff --git a/api/api/openapi.bundle.yaml b/api/api/openapi.bundle.yaml index 05ae12eb8..809d8f328 100644 --- a/api/api/openapi.bundle.yaml +++ b/api/api/openapi.bundle.yaml @@ -1980,6 +1980,7 @@ components: timeout: timeout updated_at: 2000-01-23T04:56:07.000+00:00 standard_config: + route_name_path: route_name_path experiment_mappings: - treatment: treatment-1 route: route-1 @@ -2275,6 +2276,7 @@ components: timeout: timeout updated_at: 2000-01-23T04:56:07.000+00:00 standard_config: + route_name_path: route_name_path experiment_mappings: - treatment: treatment-1 route: route-1 @@ -2335,6 +2337,7 @@ components: EnsemblerStandardConfig: description: ensembler config when ensembler type is standard example: + route_name_path: route_name_path experiment_mappings: - treatment: treatment-1 route: route-1 @@ -2348,8 +2351,12 @@ components: items: $ref: '#/components/schemas/EnsemblerStandardConfig_experiment_mappings' type: array + route_name_path: + pattern: ^\w+(?:\.\w+)*$ + type: string required: - experiment_mappings + - route_name_path type: object EnsemblerDockerConfig: description: ensembler config when ensembler type is docker @@ -2626,6 +2633,7 @@ components: timeout: timeout updated_at: 2000-01-23T04:56:07.000+00:00 standard_config: + route_name_path: route_name_path experiment_mappings: - treatment: treatment-1 route: route-1 @@ -2781,6 +2789,7 @@ components: timeout: timeout updated_at: 2000-01-23T04:56:07.000+00:00 standard_config: + route_name_path: route_name_path experiment_mappings: - treatment: treatment-1 route: route-1 diff --git a/api/api/specs/routers.yaml b/api/api/specs/routers.yaml index 0b496bde2..8dcfdb14d 100644 --- a/api/api/specs/routers.yaml +++ b/api/api/specs/routers.yaml @@ -796,6 +796,7 @@ components: nullable: true required: - "experiment_mappings" + - "route_name_path" properties: experiment_mappings: type: "array" @@ -818,6 +819,9 @@ components: description: "route id of the routes configured in the router" type: "string" example: "route-1" + route_name_path: + type: "string" + pattern: '^\w+(?:\.\w+)*$' EnsemblerDockerConfig: description: "ensembler config when ensembler type is docker" diff --git a/api/turing/cluster/servicebuilder/router.go b/api/turing/cluster/servicebuilder/router.go index 166258cb8..93ac41245 100644 --- a/api/turing/cluster/servicebuilder/router.go +++ b/api/turing/cluster/servicebuilder/router.go @@ -492,6 +492,7 @@ func buildFiberConfigMap( if ver.Ensembler != nil && ver.Ensembler.Type == models.EnsemblerStandardType { propsMap["experiment_mappings"] = ver.Ensembler.StandardConfig.ExperimentMappings + propsMap["route_name_path"] = ver.Ensembler.StandardConfig.RouteNamePath } properties, err := json.Marshal(propsMap) diff --git a/api/turing/models/ensembler.go b/api/turing/models/ensembler.go index 4f4a4960e..47033f314 100644 --- a/api/turing/models/ensembler.go +++ b/api/turing/models/ensembler.go @@ -33,6 +33,7 @@ const ( type EnsemblerStandardConfig struct { ExperimentMappings []ExperimentMapping `json:"experiment_mappings" validate:"required,dive"` + RouteNamePath string `json:"route_name_path" validate:"required"` } type EnsemblerDockerConfig struct { diff --git a/api/turing/testdata/cluster/servicebuilder/router_configmap_standard_ensembler.yml b/api/turing/testdata/cluster/servicebuilder/router_configmap_standard_ensembler.yml index 28886b2b7..38e7164ed 100644 --- a/api/turing/testdata/cluster/servicebuilder/router_configmap_standard_ensembler.yml +++ b/api/turing/testdata/cluster/servicebuilder/router_configmap_standard_ensembler.yml @@ -28,5 +28,6 @@ strategy: - experiment: exp_exp_test_experiment_2 route: route-2 treatment: treatment + route_name_path: "" type: fiber.DefaultTuringRoutingStrategy type: EAGER_ROUTER diff --git a/api/turing/testdata/cluster/servicebuilder/router_configmap_traffic_splitting.yml b/api/turing/testdata/cluster/servicebuilder/router_configmap_traffic_splitting.yml index e7c923e50..bad549240 100644 --- a/api/turing/testdata/cluster/servicebuilder/router_configmap_traffic_splitting.yml +++ b/api/turing/testdata/cluster/servicebuilder/router_configmap_traffic_splitting.yml @@ -26,6 +26,7 @@ routes: - experiment: exp_exp_test_experiment_2 route: route-2 treatment: treatment + route_name_path: "" type: fiber.DefaultTuringRoutingStrategy type: EAGER_ROUTER - id: traffic-split-0 @@ -58,6 +59,7 @@ routes: - experiment: exp_exp_test_experiment_2 route: route-2 treatment: treatment + route_name_path: "" type: fiber.DefaultTuringRoutingStrategy type: EAGER_ROUTER - id: traffic-split-1 @@ -90,6 +92,7 @@ routes: - experiment: exp_exp_test_experiment_2 route: route-2 treatment: treatment + route_name_path: "" type: fiber.DefaultTuringRoutingStrategy type: EAGER_ROUTER - id: traffic-split-2 From 6c9f77c48c17ab90e19497516d7274dfd52964b7 Mon Sep 17 00:00:00 2001 From: ewezy Date: Thu, 8 Sep 2022 17:40:35 +0800 Subject: [PATCH 02/26] Update ensembler struct in test data to reflect latest schema --- .../router_version_missing_bigquery.json | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/api/turing/testdata/cluster/servicebuilder/router_version_missing_bigquery.json b/api/turing/testdata/cluster/servicebuilder/router_version_missing_bigquery.json index e3c2e97ff..470892147 100644 --- a/api/turing/testdata/cluster/servicebuilder/router_version_missing_bigquery.json +++ b/api/turing/testdata/cluster/servicebuilder/router_version_missing_bigquery.json @@ -62,25 +62,28 @@ }, "ensembler": { "id": 300, - "image": "asia.gcr.io/gcp-project-id/echo:1.0.2", - "resource_request": { - "min_replica": 1, - "max_replica": 2, - "cpu_request": "200m", - "memory_request": "256Mi" - }, - "autoscaling_policy": { - "metric": "memory", - "target": "90" - }, - "endpoint": "echo?delay=10ms", - "timeout": "2s", - "port": 8080, - "env": [ - { - "name": "TEST_ENV", - "value": "ensembler" - } - ] + "type": "docker", + "docker_config": { + "image": "asia.gcr.io/gcp-project-id/echo:1.0.2", + "resource_request": { + "min_replica": 1, + "max_replica": 2, + "cpu_request": "200m", + "memory_request": "256Mi" + }, + "autoscaling_policy": { + "metric": "memory", + "target": "90" + }, + "endpoint": "echo?delay=10ms", + "timeout": "2s", + "port": 8080, + "env": [ + { + "name": "TEST_ENV", + "value": "ensembler" + } + ] + } } } \ No newline at end of file From 519aa4e6b5dde915808c06f0deca25c0058a6d02 Mon Sep 17 00:00:00 2001 From: ewezy Date: Thu, 8 Sep 2022 18:41:17 +0800 Subject: [PATCH 03/26] Add additional validation tests to ensure that experiment mappings and route name path are not both set --- api/turing/api/request/request.go | 11 +++++- api/turing/api/request/request_test.go | 53 ++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/api/turing/api/request/request.go b/api/turing/api/request/request.go index 860192475..3db6d1e25 100644 --- a/api/turing/api/request/request.go +++ b/api/turing/api/request/request.go @@ -158,8 +158,15 @@ func (r RouterConfig) BuildRouterVersion( r.Ensembler.DockerConfig.AutoscalingPolicy = getAutoscalingPolicyOrDefault( r.Ensembler.DockerConfig.AutoscalingPolicy) } - if r.Ensembler.Type == models.EnsemblerStandardType && r.Ensembler.StandardConfig == nil { - return nil, errors.New("missing ensembler standard config") + if r.Ensembler.Type == models.EnsemblerStandardType { + if r.Ensembler.StandardConfig == nil { + return nil, errors.New("missing ensembler standard config") + } + + // Verify that the ExperimentMappings and RouteNamePath are not both set at the same time + if len(r.Ensembler.StandardConfig.ExperimentMappings) > 0 && r.Ensembler.StandardConfig.RouteNamePath != "" { + return nil, errors.New("Experiment mappings and route name path cannot both be configured together") + } } if r.Ensembler.Type == models.EnsemblerPyFuncType { if r.Ensembler.PyfuncConfig == nil { diff --git a/api/turing/api/request/request_test.go b/api/turing/api/request/request_test.go index bdfe8761d..cb182a694 100644 --- a/api/turing/api/request/request_test.go +++ b/api/turing/api/request/request_test.go @@ -446,6 +446,59 @@ func TestRequestBuildRouterVersionWithDefaultConfig(t *testing.T) { assertgotest.DeepEqual(t, expected, *got) } +func TestRequestBuildRouterVersionWithInvalidStandardEnsembler(t *testing.T) { + defaults := config.RouterDefaults{ + Image: "routerimage", + FiberDebugLogEnabled: true, + CustomMetricsEnabled: true, + JaegerEnabled: true, + JaegerCollectorEndpoint: "jaegerendpoint", + LogLevel: "DEBUG", + FluentdConfig: &config.FluentdConfig{ + Image: "fluentdimage", + Tag: "fluentdtag", + }, + } + projectID := models.ID(1) + router := createOrUpdateRequest.BuildRouter(projectID) + + // Set up mock Crypto service + cryptoSvc := &mocks.CryptoService{} + cryptoSvc.On("Encrypt", "dummy_passkey").Return("enc_passkey", nil) + + // Set up mock Experiment service + expSvc := &mocks.ExperimentsService{} + expSvc.On("IsClientSelectionEnabled", mock.Anything).Return(true, nil) + + // Set up mock Ensembler service + ensemblerSvc := &mocks.EnsemblersService{} + + // Creates a router config that is valid except for the invalid ensembler config + testRouterConfig := validRouterConfig + testRouterConfig.Ensembler = &models.Ensembler{ + Type: models.EnsemblerStandardType, + StandardConfig: &models.EnsemblerStandardConfig{ + ExperimentMappings: []models.ExperimentMapping{ + { + Experiment: "experiment-1", + Treatment: "treatment-1", + Route: "route-1", + }, + { + Experiment: "experiment-1", + Treatment: "treatment-1", + Route: "route-1", + }, + }, + RouteNamePath: "abc", + }, + } + + result, err := testRouterConfig.BuildRouterVersion(router, &defaults, cryptoSvc, expSvc, ensemblerSvc) + assert.Nil(t, result) + assert.EqualError(t, err, "Experiment mappings and route name path cannot both be configured together") +} + func TestRequestBuildRouterVersionWithUnavailablePyFuncEnsembler(t *testing.T) { defaults := config.RouterDefaults{ Image: "routerimage", From a7595995918b472f875044ed1d349b777b4829c1 Mon Sep 17 00:00:00 2001 From: ewezy Date: Thu, 8 Sep 2022 19:27:39 +0800 Subject: [PATCH 04/26] Add additional test case to verify the route name path is valid --- api/turing/api/request/request.go | 10 +++- api/turing/api/request/request_test.go | 64 ++++++++++++++++++-------- 2 files changed, 55 insertions(+), 19 deletions(-) diff --git a/api/turing/api/request/request.go b/api/turing/api/request/request.go index 3db6d1e25..bdcab9ca2 100644 --- a/api/turing/api/request/request.go +++ b/api/turing/api/request/request.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "regexp" "github.com/caraml-dev/turing/api/turing/config" "github.com/caraml-dev/turing/api/turing/models" @@ -12,6 +13,8 @@ import ( routercfg "github.com/caraml-dev/turing/engines/router/missionctl/config" ) +var routeNamePathRegex = regexp.MustCompile(`^\w+(?:\.\w+)*$`) + // CreateOrUpdateRouterRequest structure defines the format of the request payload // when creating or updating routers type CreateOrUpdateRouterRequest struct { @@ -165,7 +168,12 @@ func (r RouterConfig) BuildRouterVersion( // Verify that the ExperimentMappings and RouteNamePath are not both set at the same time if len(r.Ensembler.StandardConfig.ExperimentMappings) > 0 && r.Ensembler.StandardConfig.RouteNamePath != "" { - return nil, errors.New("Experiment mappings and route name path cannot both be configured together") + return nil, errors.New("experiment mappings and route name path cannot both be configured together") + } + + // Verify that the route name path is validated by the regex expression + if !routeNamePathRegex.MatchString(r.Ensembler.StandardConfig.RouteNamePath) { + return nil, errors.New("route name path is invalid") } } if r.Ensembler.Type == models.EnsemblerPyFuncType { diff --git a/api/turing/api/request/request_test.go b/api/turing/api/request/request_test.go index cb182a694..aed58387e 100644 --- a/api/turing/api/request/request_test.go +++ b/api/turing/api/request/request_test.go @@ -473,30 +473,58 @@ func TestRequestBuildRouterVersionWithInvalidStandardEnsembler(t *testing.T) { // Set up mock Ensembler service ensemblerSvc := &mocks.EnsemblersService{} - // Creates a router config that is valid except for the invalid ensembler config - testRouterConfig := validRouterConfig - testRouterConfig.Ensembler = &models.Ensembler{ - Type: models.EnsemblerStandardType, - StandardConfig: &models.EnsemblerStandardConfig{ - ExperimentMappings: []models.ExperimentMapping{ - { - Experiment: "experiment-1", - Treatment: "treatment-1", - Route: "route-1", + // Define tests + tests := map[string]struct { + testEnsembler models.Ensembler + err string + }{ + "failure | both experiment mappings and route name path are set": { + testEnsembler: models.Ensembler{ + Type: models.EnsemblerStandardType, + StandardConfig: &models.EnsemblerStandardConfig{ + ExperimentMappings: []models.ExperimentMapping{ + { + Experiment: "experiment-1", + Treatment: "treatment-1", + Route: "route-1", + }, + { + Experiment: "experiment-1", + Treatment: "treatment-1", + Route: "route-1", + }, + }, + RouteNamePath: "abc", }, - { - Experiment: "experiment-1", - Treatment: "treatment-1", - Route: "route-1", + }, + err: "experiment mappings and route name path cannot both be configured together", + }, + "failure | invalid route name path": { + testEnsembler: models.Ensembler{ + Type: models.EnsemblerStandardType, + StandardConfig: &models.EnsemblerStandardConfig{ + RouteNamePath: "abc def", }, }, - RouteNamePath: "abc", + err: "route name path is invalid", }, } - result, err := testRouterConfig.BuildRouterVersion(router, &defaults, cryptoSvc, expSvc, ensemblerSvc) - assert.Nil(t, result) - assert.EqualError(t, err, "Experiment mappings and route name path cannot both be configured together") + // Run tests + for name, data := range tests { + t.Run(name, func(t *testing.T) { + // Creates a router config that is valid except for the invalid ensembler config + testRouterConfig := validRouterConfig + testRouterConfig.Ensembler = &data.testEnsembler + + result, err := testRouterConfig.BuildRouterVersion(router, &defaults, cryptoSvc, expSvc, ensemblerSvc) + if data.err == "" { + assert.Nil(t, result) + } else { + assert.EqualError(t, err, data.err) + } + }) + } } func TestRequestBuildRouterVersionWithUnavailablePyFuncEnsembler(t *testing.T) { From 8f6738c5603720af1c2646286da44e4bfd311a2a Mon Sep 17 00:00:00 2001 From: ewezy Date: Thu, 8 Sep 2022 19:29:27 +0800 Subject: [PATCH 05/26] Refactor request parsing test --- api/turing/api/request/request_test.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/api/turing/api/request/request_test.go b/api/turing/api/request/request_test.go index aed58387e..43f591455 100644 --- a/api/turing/api/request/request_test.go +++ b/api/turing/api/request/request_test.go @@ -518,11 +518,8 @@ func TestRequestBuildRouterVersionWithInvalidStandardEnsembler(t *testing.T) { testRouterConfig.Ensembler = &data.testEnsembler result, err := testRouterConfig.BuildRouterVersion(router, &defaults, cryptoSvc, expSvc, ensemblerSvc) - if data.err == "" { - assert.Nil(t, result) - } else { - assert.EqualError(t, err, data.err) - } + assert.Nil(t, result) + assert.EqualError(t, err, data.err) }) } } From 29470747313ef43dcfcd5f2b2ad7904fd4a9a0f3 Mon Sep 17 00:00:00 2001 From: ewezy Date: Fri, 9 Sep 2022 14:36:03 +0800 Subject: [PATCH 06/26] Remove redundant regex route path name check --- api/turing/api/request/request.go | 8 -------- api/turing/api/request/request_test.go | 9 --------- 2 files changed, 17 deletions(-) diff --git a/api/turing/api/request/request.go b/api/turing/api/request/request.go index bdcab9ca2..57a243ac2 100644 --- a/api/turing/api/request/request.go +++ b/api/turing/api/request/request.go @@ -4,7 +4,6 @@ import ( "encoding/json" "errors" "fmt" - "regexp" "github.com/caraml-dev/turing/api/turing/config" "github.com/caraml-dev/turing/api/turing/models" @@ -13,8 +12,6 @@ import ( routercfg "github.com/caraml-dev/turing/engines/router/missionctl/config" ) -var routeNamePathRegex = regexp.MustCompile(`^\w+(?:\.\w+)*$`) - // CreateOrUpdateRouterRequest structure defines the format of the request payload // when creating or updating routers type CreateOrUpdateRouterRequest struct { @@ -170,11 +167,6 @@ func (r RouterConfig) BuildRouterVersion( if len(r.Ensembler.StandardConfig.ExperimentMappings) > 0 && r.Ensembler.StandardConfig.RouteNamePath != "" { return nil, errors.New("experiment mappings and route name path cannot both be configured together") } - - // Verify that the route name path is validated by the regex expression - if !routeNamePathRegex.MatchString(r.Ensembler.StandardConfig.RouteNamePath) { - return nil, errors.New("route name path is invalid") - } } if r.Ensembler.Type == models.EnsemblerPyFuncType { if r.Ensembler.PyfuncConfig == nil { diff --git a/api/turing/api/request/request_test.go b/api/turing/api/request/request_test.go index 43f591455..f9c1388c9 100644 --- a/api/turing/api/request/request_test.go +++ b/api/turing/api/request/request_test.go @@ -499,15 +499,6 @@ func TestRequestBuildRouterVersionWithInvalidStandardEnsembler(t *testing.T) { }, err: "experiment mappings and route name path cannot both be configured together", }, - "failure | invalid route name path": { - testEnsembler: models.Ensembler{ - Type: models.EnsemblerStandardType, - StandardConfig: &models.EnsemblerStandardConfig{ - RouteNamePath: "abc def", - }, - }, - err: "route name path is invalid", - }, } // Run tests From 8e54481d1c4e0a6f6b5b527ba9986ad91580a0b1 Mon Sep 17 00:00:00 2001 From: ewezy Date: Fri, 9 Sep 2022 17:00:26 +0800 Subject: [PATCH 07/26] Update regex expression to allow empty route_name_path string --- api/api/openapi.bundle.yaml | 2 +- api/api/specs/routers.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/api/openapi.bundle.yaml b/api/api/openapi.bundle.yaml index 809d8f328..37f01ea56 100644 --- a/api/api/openapi.bundle.yaml +++ b/api/api/openapi.bundle.yaml @@ -2352,7 +2352,7 @@ components: $ref: '#/components/schemas/EnsemblerStandardConfig_experiment_mappings' type: array route_name_path: - pattern: ^\w+(?:\.\w+)*$ + pattern: ^\w*(?:\.\w+)*$ type: string required: - experiment_mappings diff --git a/api/api/specs/routers.yaml b/api/api/specs/routers.yaml index 8dcfdb14d..c6f9c456c 100644 --- a/api/api/specs/routers.yaml +++ b/api/api/specs/routers.yaml @@ -821,7 +821,7 @@ components: example: "route-1" route_name_path: type: "string" - pattern: '^\w+(?:\.\w+)*$' + pattern: '^\w*(?:\.\w+)*$' EnsemblerDockerConfig: description: "ensembler config when ensembler type is docker" From 377ae0444eb8b7c878f75f03a11d2a9591813ce3 Mon Sep 17 00:00:00 2001 From: ewezy Date: Fri, 9 Sep 2022 17:48:24 +0800 Subject: [PATCH 08/26] Remove required tag from EnsemblerStandardConfig.RouteNamePath --- api/turing/models/ensembler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/turing/models/ensembler.go b/api/turing/models/ensembler.go index 47033f314..2137b088b 100644 --- a/api/turing/models/ensembler.go +++ b/api/turing/models/ensembler.go @@ -33,7 +33,7 @@ const ( type EnsemblerStandardConfig struct { ExperimentMappings []ExperimentMapping `json:"experiment_mappings" validate:"required,dive"` - RouteNamePath string `json:"route_name_path" validate:"required"` + RouteNamePath string `json:"route_name_path"` } type EnsemblerDockerConfig struct { From 39223e30583aa5094c95308cd31f37338d46f51f Mon Sep 17 00:00:00 2001 From: ewezy Date: Tue, 13 Sep 2022 09:48:21 +0800 Subject: [PATCH 09/26] Add e2e tests for std ensemblers with route_name_path --- api/e2e/test/00_all_e2e_test.go | 4 + ...with_route_name_path_std_ensembler_test.go | 69 ++++++++++++++++++ ...er_std_ensembler_route_name_path.json.tmpl | 73 +++++++++++++++++++ .../configs/plugin_config_example.yaml | 2 + 4 files changed, 148 insertions(+) create mode 100644 api/e2e/test/09_deploy_router_with_route_name_path_std_ensembler_test.go create mode 100644 api/e2e/test/testdata/create_router_std_ensembler_route_name_path.json.tmpl diff --git a/api/e2e/test/00_all_e2e_test.go b/api/e2e/test/00_all_e2e_test.go index 1fcbb3be8..26b7ab1ac 100644 --- a/api/e2e/test/00_all_e2e_test.go +++ b/api/e2e/test/00_all_e2e_test.go @@ -79,6 +79,10 @@ func TestEndToEnd(t *testing.T) { t.Parallel() t.Run("DeployRouter", TestDeployRouterWithTrafficRules) }) + t.Run("TestStandardEnsemblerWithRouteNamePath", func(t *testing.T) { + t.Parallel() + t.Run("DeployRouter", TestDeployRouterWithRouteNamePathInStandardEnsembler) + }) } func TestMain(m *testing.M) { diff --git a/api/e2e/test/09_deploy_router_with_route_name_path_std_ensembler_test.go b/api/e2e/test/09_deploy_router_with_route_name_path_std_ensembler_test.go new file mode 100644 index 000000000..37cfd219e --- /dev/null +++ b/api/e2e/test/09_deploy_router_with_route_name_path_std_ensembler_test.go @@ -0,0 +1,69 @@ +//go:build e2e + +package e2e + +import ( + "net/http" + "path/filepath" + "testing" + + "github.com/caraml-dev/turing/api/turing/models" + "github.com/stretchr/testify/assert" + "github.com/tidwall/gjson" +) + +/* +Steps: +Create a new router with valid config for enricher, ensembler, router. +a. Test GET router immediately > empty config +b. Wait for success response from deployment +c. Test GET router version > status shows "deployed" +d. Test GET router > config section shows version 1, status "deployed" +e. Test cluster that deployments exist +f. Make a request to the router, validate the response. +*/ +func TestDeployRouterWithRouteNamePathInStandardEnsembler(t *testing.T) { + // Create router + t.Log("Creating router") + data := makeRouterPayload( + filepath.Join("testdata", "create_router_std_ensembler_route_name_path.json.tmpl"), + globalTestContext) + + withDeployedRouter(t, data, + func(router *models.Router) { + t.Log("Testing router endpoint: POST " + router.Endpoint) + withRouterResponse(t, + http.MethodPost, + router.Endpoint, + http.Header{ + "Content-Type": {"application/json"}, + "X-Mirror-Body": {"true"}, + }, + `{"client": {"id": 4}}`, + func(response *http.Response, responsePayload []byte) { + assert.Equal(t, http.StatusOK, response.StatusCode, + "Unexpected response (code %d): %s", + response.StatusCode, string(responsePayload)) + actualResponse := gjson.GetBytes(responsePayload, "response").String() + expectedResponse := `{ + "experiment": { + "configuration": { + "foo":"bar", + } + }, + "route_responses": [ + { + "data": { + "version": "control" + }, + "is_default": false, + "route": "control" + } + ] + }` + assert.JSONEq(t, expectedResponse, actualResponse) + }) + }, + nil, + ) +} diff --git a/api/e2e/test/testdata/create_router_std_ensembler_route_name_path.json.tmpl b/api/e2e/test/testdata/create_router_std_ensembler_route_name_path.json.tmpl new file mode 100644 index 000000000..b16c19ade --- /dev/null +++ b/api/e2e/test/testdata/create_router_std_ensembler_route_name_path.json.tmpl @@ -0,0 +1,73 @@ +{ + "environment_name": "{{.DeploymentEnvironment}}", + "name": "e2e-std-ensembler-test-{{.TestID}}", + "config": { + "routes": [ + { + "id": "control", + "type": "PROXY", + "endpoint": "{{.MockserverEndpoint}}/control", + "timeout": "5s" + }, + { + "id": "treatment-a", + "type": "PROXY", + "endpoint": "{{.MockserverEndpoint}}/treatment-a", + "timeout": "5s" + } + ], + "experiment_engine": { + "type": "proprietary", + "config": { + "client": { + "id": "1", + "username": "test", + "passkey": "test" + }, + "experiments": [ + { + "id": "001", + "name": "exp_1" + } + ], + "variables": { + "experiment_variables": { + "001": [ + { + "name": "client_id", + "type": "unit", + "required": true + } + ] + }, + "config": [ + { + "name": "client_id", + "required": true, + "field": "client.id", + "field_source": "payload" + } + ] + } + } + }, + "resource_request": { + "min_replica": 1, + "max_replica": 1, + "cpu_request": "200m", + "memory_request": "250Mi" + }, + "timeout": "5s", + "log_config": { + "result_logger_type": "nop" + }, + "ensembler": { + "type": "standard", + "standard_config": { + "experiment_mappings": [], + "route_name_path": "treatment.configuration.policy.route_name" + } + }, + "default_route_id": "control" + } +} diff --git a/engines/experiment/examples/plugins/hardcoded/configs/plugin_config_example.yaml b/engines/experiment/examples/plugins/hardcoded/configs/plugin_config_example.yaml index fe0a159cf..e3b225314 100644 --- a/engines/experiment/examples/plugins/hardcoded/configs/plugin_config_example.yaml +++ b/engines/experiment/examples/plugins/hardcoded/configs/plugin_config_example.yaml @@ -17,10 +17,12 @@ experiments: traffic: 0.85 treatment_configuration: foo: bar + route_name: treatment-a treatment-1: traffic: 0.15 treatment_configuration: bar: baz + route_name: control variables: '001': - name: client From c35486f9a76e1140da85ae3369170ecef99fca09 Mon Sep 17 00:00:00 2001 From: ewezy Date: Tue, 13 Sep 2022 10:32:04 +0800 Subject: [PATCH 10/26] Add additional conditional to e2e tests to check for ensembler pods only when docker/pyfunc types are used --- api/e2e/test/helpers_api_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/e2e/test/helpers_api_test.go b/api/e2e/test/helpers_api_test.go index 119a9a990..12b547d84 100644 --- a/api/e2e/test/helpers_api_test.go +++ b/api/e2e/test/helpers_api_test.go @@ -275,7 +275,7 @@ func withDeployedRouter( globalTestContext.ProjectName, fmt.Sprintf("%s-turing-enricher-%d-0-deployment", router.Name, routerVersion.Version))) } - if router.CurrRouterVersion.Ensembler != nil { + if router.CurrRouterVersion.Ensembler != nil && router.CurrRouterVersion.Ensembler.Type != models.EnsemblerStandardType { require.True(t, isDeploymentExists( globalTestContext.clusterClients, globalTestContext.ProjectName, From 4449d2dfe53b013b336f387f6adba638e357ceb2 Mon Sep 17 00:00:00 2001 From: ewezy Date: Tue, 13 Sep 2022 14:21:59 +0800 Subject: [PATCH 11/26] Fix e2e test data --- ...with_route_name_path_std_ensembler_test.go | 20 +++---------------- ...er_std_ensembler_route_name_path.json.tmpl | 2 +- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/api/e2e/test/09_deploy_router_with_route_name_path_std_ensembler_test.go b/api/e2e/test/09_deploy_router_with_route_name_path_std_ensembler_test.go index 37cfd219e..4839e02dd 100644 --- a/api/e2e/test/09_deploy_router_with_route_name_path_std_ensembler_test.go +++ b/api/e2e/test/09_deploy_router_with_route_name_path_std_ensembler_test.go @@ -9,12 +9,11 @@ import ( "github.com/caraml-dev/turing/api/turing/models" "github.com/stretchr/testify/assert" - "github.com/tidwall/gjson" ) /* Steps: -Create a new router with valid config for enricher, ensembler, router. +Create a new router with valid config for the router. a. Test GET router immediately > empty config b. Wait for success response from deployment c. Test GET router version > status shows "deployed" @@ -44,22 +43,9 @@ func TestDeployRouterWithRouteNamePathInStandardEnsembler(t *testing.T) { assert.Equal(t, http.StatusOK, response.StatusCode, "Unexpected response (code %d): %s", response.StatusCode, string(responsePayload)) - actualResponse := gjson.GetBytes(responsePayload, "response").String() + actualResponse := string(responsePayload) expectedResponse := `{ - "experiment": { - "configuration": { - "foo":"bar", - } - }, - "route_responses": [ - { - "data": { - "version": "control" - }, - "is_default": false, - "route": "control" - } - ] + "version" : "control" }` assert.JSONEq(t, expectedResponse, actualResponse) }) diff --git a/api/e2e/test/testdata/create_router_std_ensembler_route_name_path.json.tmpl b/api/e2e/test/testdata/create_router_std_ensembler_route_name_path.json.tmpl index b16c19ade..dc031aa73 100644 --- a/api/e2e/test/testdata/create_router_std_ensembler_route_name_path.json.tmpl +++ b/api/e2e/test/testdata/create_router_std_ensembler_route_name_path.json.tmpl @@ -65,7 +65,7 @@ "type": "standard", "standard_config": { "experiment_mappings": [], - "route_name_path": "treatment.configuration.policy.route_name" + "route_name_path": "route_name" } }, "default_route_id": "control" From 382a18e2a542f5e7f22fe3dc2542c1b4e68b7db3 Mon Sep 17 00:00:00 2001 From: ewezy Date: Tue, 13 Sep 2022 14:47:36 +0800 Subject: [PATCH 12/26] Add positive test to verify valid router config creation --- api/turing/api/request/request_test.go | 65 ++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/api/turing/api/request/request_test.go b/api/turing/api/request/request_test.go index f9c1388c9..70bdc057c 100644 --- a/api/turing/api/request/request_test.go +++ b/api/turing/api/request/request_test.go @@ -446,7 +446,7 @@ func TestRequestBuildRouterVersionWithDefaultConfig(t *testing.T) { assertgotest.DeepEqual(t, expected, *got) } -func TestRequestBuildRouterVersionWithInvalidStandardEnsembler(t *testing.T) { +func TestRequestBuildRouterVersionWithStandardEnsembler(t *testing.T) { defaults := config.RouterDefaults{ Image: "routerimage", FiberDebugLogEnabled: true, @@ -475,9 +475,60 @@ func TestRequestBuildRouterVersionWithInvalidStandardEnsembler(t *testing.T) { // Define tests tests := map[string]struct { - testEnsembler models.Ensembler - err string + testEnsembler models.Ensembler + expectedEnsembler models.Ensembler + err string }{ + "success | only route name path is set": { + testEnsembler: models.Ensembler{ + Type: models.EnsemblerStandardType, + StandardConfig: &models.EnsemblerStandardConfig{ + RouteNamePath: "abc", + }, + }, + expectedEnsembler: models.Ensembler{ + Type: models.EnsemblerStandardType, + StandardConfig: &models.EnsemblerStandardConfig{ + RouteNamePath: "abc", + }, + }, + }, + "success | only experiment mappings are set": { + testEnsembler: models.Ensembler{ + Type: models.EnsemblerStandardType, + StandardConfig: &models.EnsemblerStandardConfig{ + ExperimentMappings: []models.ExperimentMapping{ + { + Experiment: "experiment-1", + Treatment: "treatment-1", + Route: "route-1", + }, + { + Experiment: "experiment-1", + Treatment: "treatment-1", + Route: "route-1", + }, + }, + }, + }, + expectedEnsembler: models.Ensembler{ + Type: models.EnsemblerStandardType, + StandardConfig: &models.EnsemblerStandardConfig{ + ExperimentMappings: []models.ExperimentMapping{ + { + Experiment: "experiment-1", + Treatment: "treatment-1", + Route: "route-1", + }, + { + Experiment: "experiment-1", + Treatment: "treatment-1", + Route: "route-1", + }, + }, + }, + }, + }, "failure | both experiment mappings and route name path are set": { testEnsembler: models.Ensembler{ Type: models.EnsemblerStandardType, @@ -509,8 +560,12 @@ func TestRequestBuildRouterVersionWithInvalidStandardEnsembler(t *testing.T) { testRouterConfig.Ensembler = &data.testEnsembler result, err := testRouterConfig.BuildRouterVersion(router, &defaults, cryptoSvc, expSvc, ensemblerSvc) - assert.Nil(t, result) - assert.EqualError(t, err, data.err) + if data.err != "" { + assert.Nil(t, result) + assert.EqualError(t, err, data.err) + } else { + assert.Equal(t, data.expectedEnsembler, *result.Ensembler) + } }) } } From 51eb7a2aa8005139e9e52213a907a82d505c7083 Mon Sep 17 00:00:00 2001 From: ewezy Date: Tue, 13 Sep 2022 14:59:22 +0800 Subject: [PATCH 13/26] Fix router api test data --- .../servicebuilder/router_configmap_traffic_splitting.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/api/turing/testdata/cluster/servicebuilder/router_configmap_traffic_splitting.yml b/api/turing/testdata/cluster/servicebuilder/router_configmap_traffic_splitting.yml index bad549240..d8e174ad9 100644 --- a/api/turing/testdata/cluster/servicebuilder/router_configmap_traffic_splitting.yml +++ b/api/turing/testdata/cluster/servicebuilder/router_configmap_traffic_splitting.yml @@ -125,6 +125,7 @@ routes: - experiment: exp_exp_test_experiment_2 route: route-2 treatment: treatment + route_name_path: "" type: fiber.DefaultTuringRoutingStrategy type: EAGER_ROUTER strategy: From 5efd62f417676df90870dc7e8ceffa9be68b0e25 Mon Sep 17 00:00:00 2001 From: ewezy Date: Tue, 13 Sep 2022 15:16:26 +0800 Subject: [PATCH 14/26] Add additional unit tests for router service creation with std ensemblers with route name path --- .../cluster/servicebuilder/router_test.go | 109 ++++++++++++++++- ...igmap_std_ensembler_with_exp_mappings.yml} | 0 ...map_std_ensembler_with_route_name_path.yml | 35 ++++++ ...cess_std_ensembler_with_exp_mappings.json} | 0 ...ss_std_ensembler_with_route_name_path.json | 111 ++++++++++++++++++ 5 files changed, 251 insertions(+), 4 deletions(-) rename api/turing/testdata/cluster/servicebuilder/{router_configmap_standard_ensembler.yml => router_configmap_std_ensembler_with_exp_mappings.yml} (100%) create mode 100644 api/turing/testdata/cluster/servicebuilder/router_configmap_std_ensembler_with_route_name_path.yml rename api/turing/testdata/cluster/servicebuilder/{router_version_success_standard_ensembler.json => router_version_success_std_ensembler_with_exp_mappings.json} (100%) create mode 100644 api/turing/testdata/cluster/servicebuilder/router_version_success_std_ensembler_with_route_name_path.json diff --git a/api/turing/cluster/servicebuilder/router_test.go b/api/turing/cluster/servicebuilder/router_test.go index 86d6aa76b..a9bb208e8 100644 --- a/api/turing/cluster/servicebuilder/router_test.go +++ b/api/turing/cluster/servicebuilder/router_test.go @@ -46,7 +46,13 @@ func TestNewRouterService(t *testing.T) { require.NoError(t, err) cfgmapEnsembling, err := tu.ReadFile(filepath.Join(testDataBasePath, "router_configmap_ensembling.yml")) require.NoError(t, err) - cfgmapStandardEnsemble, err := tu.ReadFile(filepath.Join(testDataBasePath, "router_configmap_standard_ensembler.yml")) + cfgmapStdEnsemblerWithExpMappings, err := tu.ReadFile( + filepath.Join(testDataBasePath, "router_configmap_std_ensembler_with_exp_mappings.yml"), + ) + require.NoError(t, err) + cfgmapStdEnsemblerWithRouteNamePath, err := tu.ReadFile( + filepath.Join(testDataBasePath, "router_configmap_std_ensembler_with_route_name_path.yml"), + ) require.NoError(t, err) cfgmapTrafficSplitting, err := tu.ReadFile(filepath.Join(testDataBasePath, "router_configmap_traffic_splitting.yml")) require.NoError(t, err) @@ -257,8 +263,103 @@ func TestNewRouterService(t *testing.T) { UserContainerLimitRequestFactor: 1.5, }, }, - "success | standard ensembler": { - filePath: filepath.Join(testDataBasePath, "router_version_success_standard_ensembler.json"), + "success | standard ensembler with experiment mappings": { + filePath: filepath.Join(testDataBasePath, "router_version_success_std_ensembler_with_exp_mappings.json"), + expRawConfig: json.RawMessage(expRunnerConfig), + expected: &cluster.KnativeService{ + BaseService: &cluster.BaseService{ + Name: "test-svc-turing-router-1", + Namespace: "test-project", + Image: "asia.gcr.io/gcp-project-id/turing-router:latest", + CPURequests: resource.MustParse("400m"), + MemoryRequests: resource.MustParse("512Mi"), + LivenessHTTPGetPath: "/v1/internal/live", + ReadinessHTTPGetPath: "/v1/internal/ready", + ConfigMap: &cluster.ConfigMap{ + Name: "test-svc-turing-fiber-config-1", + FileName: "fiber.yml", + Data: string(cfgmapStdEnsemblerWithExpMappings), + Labels: map[string]string{ + "app": "test-svc", + "environment": "", + "orchestrator": "turing", + "stream": "test-stream", + "team": "test-team", + }, + }, + Envs: []corev1.EnvVar{ + {Name: "APP_NAME", Value: "test-svc-1.test-project"}, + {Name: "APP_ENVIRONMENT", Value: "test-env"}, + {Name: "ROUTER_TIMEOUT", Value: "5s"}, + {Name: "APP_JAEGER_COLLECTOR_ENDPOINT", Value: "jaeger-endpoint"}, + {Name: "ROUTER_CONFIG_FILE", Value: "/app/config/fiber.yml"}, + {Name: "APP_SENTRY_ENABLED", Value: "true"}, + {Name: "APP_SENTRY_DSN", Value: "sentry-dsn"}, + {Name: "APP_LOGLEVEL", Value: "INFO"}, + {Name: "APP_CUSTOM_METRICS", Value: "false"}, + {Name: "APP_JAEGER_ENABLED", Value: "false"}, + {Name: "APP_RESULT_LOGGER", Value: "bigquery"}, + {Name: "APP_FIBER_DEBUG_LOG", Value: "false"}, + {Name: "APP_GCP_PROJECT", Value: "gcp-project-id"}, + {Name: "APP_BQ_DATASET", Value: "dataset_id"}, + {Name: "APP_BQ_TABLE", Value: "turing_log_test"}, + {Name: "APP_BQ_BATCH_LOAD", Value: "false"}, + {Name: "GOOGLE_APPLICATION_CREDENTIALS", Value: "/var/secret/router-service-account.json"}, + }, + Labels: map[string]string{ + "app": "test-svc", + "environment": "", + "orchestrator": "turing", + "stream": "test-stream", + "team": "test-team", + }, + Volumes: []corev1.Volume{ + { + Name: routerConfigMapVolume, + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "test-svc-turing-fiber-config-1", + }, + }, + }, + }, + { + Name: secretVolume, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "service-account", + Items: []corev1.KeyToPath{ + { + Key: secretKeyNameRouter, + Path: secretKeyNameRouter, + }, + }, + }, + }, + }, + }, + VolumeMounts: []corev1.VolumeMount{ + { + Name: routerConfigMapVolume, + MountPath: routerConfigMapMountPath, + }, + { + Name: secretVolume, + MountPath: secretMountPath, + }, + }, + }, + ContainerPort: 8080, + MinReplicas: 2, + MaxReplicas: 4, + TargetConcurrency: 1, + QueueProxyResourcePercentage: 20, + UserContainerLimitRequestFactor: 1.5, + }, + }, + "success | standard ensembler with route name path": { + filePath: filepath.Join(testDataBasePath, "router_version_success_std_ensembler_with_route_name_path.json"), expRawConfig: json.RawMessage(expRunnerConfig), expected: &cluster.KnativeService{ BaseService: &cluster.BaseService{ @@ -272,7 +373,7 @@ func TestNewRouterService(t *testing.T) { ConfigMap: &cluster.ConfigMap{ Name: "test-svc-turing-fiber-config-1", FileName: "fiber.yml", - Data: string(cfgmapStandardEnsemble), + Data: string(cfgmapStdEnsemblerWithRouteNamePath), Labels: map[string]string{ "app": "test-svc", "environment": "", diff --git a/api/turing/testdata/cluster/servicebuilder/router_configmap_standard_ensembler.yml b/api/turing/testdata/cluster/servicebuilder/router_configmap_std_ensembler_with_exp_mappings.yml similarity index 100% rename from api/turing/testdata/cluster/servicebuilder/router_configmap_standard_ensembler.yml rename to api/turing/testdata/cluster/servicebuilder/router_configmap_std_ensembler_with_exp_mappings.yml diff --git a/api/turing/testdata/cluster/servicebuilder/router_configmap_std_ensembler_with_route_name_path.yml b/api/turing/testdata/cluster/servicebuilder/router_configmap_std_ensembler_with_route_name_path.yml new file mode 100644 index 000000000..8ad7affa9 --- /dev/null +++ b/api/turing/testdata/cluster/servicebuilder/router_configmap_std_ensembler_with_route_name_path.yml @@ -0,0 +1,35 @@ +id: test-svc +routes: +- endpoint: http://example.com/treatment-b + id: treatment-b + timeout: 2s + type: PROXY +- endpoint: http://example.com/treatment-a + id: treatment-a + timeout: 2s + type: PROXY +- endpoint: http://www.mocky.io/v2/5e4caccc310000e2cad8c071 + id: control + timeout: 2s + type: PROXY +strategy: + properties: + default_route_id: control + experiment_engine: exp-engine-2 + experiment_engine_properties: + client_id: client_id + endpoint: exp-engine:8080 + experiments: + - experiment_name: exp_exp_test_experiment_1 + segmentation_field: customer_id + segmentation_field_source: payload + segmentation_unit: customer + timeout: 500ms + user_data: + app_version: + field: appVer + field_source: header + experiment_mappings: [] + route_name_path: policy.route_name + type: fiber.DefaultTuringRoutingStrategy +type: EAGER_ROUTER diff --git a/api/turing/testdata/cluster/servicebuilder/router_version_success_standard_ensembler.json b/api/turing/testdata/cluster/servicebuilder/router_version_success_std_ensembler_with_exp_mappings.json similarity index 100% rename from api/turing/testdata/cluster/servicebuilder/router_version_success_standard_ensembler.json rename to api/turing/testdata/cluster/servicebuilder/router_version_success_std_ensembler_with_exp_mappings.json diff --git a/api/turing/testdata/cluster/servicebuilder/router_version_success_std_ensembler_with_route_name_path.json b/api/turing/testdata/cluster/servicebuilder/router_version_success_std_ensembler_with_route_name_path.json new file mode 100644 index 000000000..89ff059a5 --- /dev/null +++ b/api/turing/testdata/cluster/servicebuilder/router_version_success_std_ensembler_with_route_name_path.json @@ -0,0 +1,111 @@ +{ + "router": { + "project_id": 10, + "environment_name": "id-dev", + "name": "test-svc" + }, + "version": 1, + "status": "pending", + "image": "asia.gcr.io/gcp-project-id/turing-router:latest", + "routes": [ + { + "id": "treatment-b", + "type": "PROXY", + "endpoint": "http://example.com/treatment-b", + "timeout": "2s" + }, + { + "id": "treatment-a", + "type": "PROXY", + "endpoint": "http://example.com/treatment-a", + "timeout": "2s" + }, + { + "id": "control", + "type": "PROXY", + "endpoint": "http://www.mocky.io/v2/5e4caccc310000e2cad8c071", + "timeout": "2s" + } + ], + "default_route_id": "control", + "experiment_engine": { + "type": "exp-engine-2", + "config": { + "deployment": { + "endpoint": "exp-engine:8080", + "timeout": "500ms" + }, + "client": { + "id": "1", + "username": "client_id", + "passkey": "xyz" + }, + "experiments": [ + { + "id": "2", + "name": "exp_exp_test_experiment_1", + "client_id": "1" + } + ], + "variables": { + "client_variables": [ + { + "name": "app_version", + "required": false, + "type": "filter" + } + ], + "experiment_variables": { + "2": [ + { + "name": "customer", + "required": true, + "type": "unit" + } + ] + }, + "config": [ + { + "name": "customer", + "required": true, + "field": "customer_id", + "field_source": "payload" + }, + { + "name": "app_version", + "required": false, + "field": "appVer", + "field_source": "header" + } + ] + } + } + }, + "resource_request": { + "min_replica": 2, + "max_replica": 4, + "cpu_request": "400m", + "memory_request": "512Mi" + }, + "timeout": "5s", + "log_config": { + "log_level": "INFO", + "custom_metrics_enabled": false, + "fiber_debug_log_enabled": false, + "jaeger_enabled": false, + "result_logger_type": "bigquery", + "bigquery_config": { + "table": "gcp-project-id.dataset_id.turing_log_test", + "service_account_name": "test-svc-account", + "batch_load": false + } + }, + "ensembler": { + "id": 300, + "type": "standard", + "standard_config": { + "experiment_mappings": [], + "route_name_path": "policy.route_name" + } + } +} From 1af930b9d7a048a60b6c2f86dc0a31ff45432a64 Mon Sep 17 00:00:00 2001 From: ewezy Date: Tue, 13 Sep 2022 15:47:39 +0800 Subject: [PATCH 15/26] Replace deprecated ioutil functions with corresponding io or os ones --- api/e2e/test/04_undeploy_router_test.go | 4 ++-- api/e2e/test/06_deploy_valid_config_test.go | 4 ++-- api/e2e/test/helpers_api_test.go | 12 ++++++------ api/e2e/test/helpers_test.go | 11 ++++++----- api/turing/internal/testutils/utils.go | 4 ++-- api/turing/middleware/openapi_validation_test.go | 6 +++--- api/turing/service/alert_service_test.go | 4 ++-- api/turing/service/mlp_service_test.go | 5 ++--- api/turing/service/pod_log_service_test.go | 14 +++++++------- api/turing/utils/merge.go | 3 +-- 10 files changed, 33 insertions(+), 34 deletions(-) diff --git a/api/e2e/test/04_undeploy_router_test.go b/api/e2e/test/04_undeploy_router_test.go index d30af731b..bd6da1509 100644 --- a/api/e2e/test/04_undeploy_router_test.go +++ b/api/e2e/test/04_undeploy_router_test.go @@ -5,7 +5,7 @@ package e2e import ( "context" "fmt" - "io/ioutil" + "io" "net/http" "testing" "time" @@ -42,7 +42,7 @@ func TestUndeployRouter(t *testing.T) { response, err := globalTestContext.httpClient.Do(req) require.NoError(t, err) assert.Equal(t, http.StatusOK, response.StatusCode) - responseBody, err := ioutil.ReadAll(response.Body) + responseBody, err := io.ReadAll(response.Body) defer response.Body.Close() require.NoError(t, err) t.Log("Undeploy Response:", string(responseBody)) diff --git a/api/e2e/test/06_deploy_valid_config_test.go b/api/e2e/test/06_deploy_valid_config_test.go index f50e5dd2b..b354284c7 100644 --- a/api/e2e/test/06_deploy_valid_config_test.go +++ b/api/e2e/test/06_deploy_valid_config_test.go @@ -5,7 +5,7 @@ package e2e import ( "context" "fmt" - "io/ioutil" + "io" "net/http" "testing" @@ -42,7 +42,7 @@ func TestDeployValidConfig(t *testing.T) { response, err := globalTestContext.httpClient.Do(req) require.NoError(t, err) assert.Equal(t, http.StatusAccepted, response.StatusCode, readBody(t, response)) - responseBody, err := ioutil.ReadAll(response.Body) + responseBody, err := io.ReadAll(response.Body) require.NoError(t, err) t.Log("Deploy Response:", string(responseBody)) diff --git a/api/e2e/test/helpers_api_test.go b/api/e2e/test/helpers_api_test.go index 12b547d84..c1a7d56be 100644 --- a/api/e2e/test/helpers_api_test.go +++ b/api/e2e/test/helpers_api_test.go @@ -6,7 +6,7 @@ import ( "bytes" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "testing" "time" @@ -34,7 +34,7 @@ func getRouter( } defer resp.Body.Close() - respBytes, err := ioutil.ReadAll(resp.Body) + respBytes, err := io.ReadAll(resp.Body) if err != nil { return nil, err } @@ -61,7 +61,7 @@ func getRouterByName( defer resp.Body.Close() routers := make([]*models.Router, 0) - data, err := ioutil.ReadAll(resp.Body) + data, err := io.ReadAll(resp.Body) if err != nil { return nil, err } @@ -96,7 +96,7 @@ func getRouterVersion( } defer resp.Body.Close() - respBytes, err := ioutil.ReadAll(resp.Body) + respBytes, err := io.ReadAll(resp.Body) if err != nil { return nil, err } @@ -143,7 +143,7 @@ func waitDeployVersion( } func getPodLogs(t *testing.T, resp *http.Response) []service.PodLog { - data, err := ioutil.ReadAll(resp.Body) + data, err := io.ReadAll(resp.Body) if err != nil { t.Error(err) } @@ -175,7 +175,7 @@ func withDeployedRouter( require.NoError(t, err) defer resp.Body.Close() - responsePayload, err := ioutil.ReadAll(resp.Body) + responsePayload, err := io.ReadAll(resp.Body) require.NoError(t, err) created := models.Router{} diff --git a/api/e2e/test/helpers_test.go b/api/e2e/test/helpers_test.go index 6165a2193..8de4550c7 100644 --- a/api/e2e/test/helpers_test.go +++ b/api/e2e/test/helpers_test.go @@ -4,8 +4,9 @@ package e2e import ( "bytes" - "io/ioutil" + "io" "net/http" + "os" "testing" "text/template" @@ -16,7 +17,7 @@ func readBody(t *testing.T, resp *http.Response) string { if resp.Body == nil { return "" } - data, err := ioutil.ReadAll(resp.Body) + data, err := io.ReadAll(resp.Body) if err != nil { t.Fatal(err) } @@ -25,7 +26,7 @@ func readBody(t *testing.T, resp *http.Response) string { // make payload to create new router from a template file func makeRouterPayload(payloadTemplateFile string, args TestContext) []byte { - data, err := ioutil.ReadFile(payloadTemplateFile) + data, err := os.ReadFile(payloadTemplateFile) if err != nil { panic(err) } @@ -52,7 +53,7 @@ func withRouterResponse( body string, assertion func(response *http.Response, responsePayload []byte)) { - req, err := http.NewRequest(method, url, ioutil.NopCloser(bytes.NewReader([]byte(body)))) + req, err := http.NewRequest(method, url, io.NopCloser(bytes.NewReader([]byte(body)))) require.NoError(t, err) req.Header = headers @@ -61,7 +62,7 @@ func withRouterResponse( require.NoError(t, err) defer resp.Body.Close() - responseBytes, err := ioutil.ReadAll(resp.Body) + responseBytes, err := io.ReadAll(resp.Body) require.NoError(t, err) assertion(resp, responseBytes) diff --git a/api/turing/internal/testutils/utils.go b/api/turing/internal/testutils/utils.go index e22ce2100..d4b3f0bd6 100644 --- a/api/turing/internal/testutils/utils.go +++ b/api/turing/internal/testutils/utils.go @@ -2,7 +2,7 @@ package testutils import ( "encoding/json" - "io/ioutil" + "io" "os" "testing" @@ -19,7 +19,7 @@ func ReadFile(filepath string) ([]byte, error) { } defer fileObj.Close() // Read contents - byteValue, err := ioutil.ReadAll(fileObj) + byteValue, err := io.ReadAll(fileObj) if err != nil { return nil, err } diff --git a/api/turing/middleware/openapi_validation_test.go b/api/turing/middleware/openapi_validation_test.go index 107839b6a..aeaf2cdcf 100644 --- a/api/turing/middleware/openapi_validation_test.go +++ b/api/turing/middleware/openapi_validation_test.go @@ -1,8 +1,8 @@ package middleware import ( - "io/ioutil" "net/http" + "os" "strings" "testing" ) @@ -136,7 +136,7 @@ func TestOpenAPIValidationValidate(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - d, err := ioutil.ReadFile("../../api/openapi.bundle.yaml") + d, err := os.ReadFile("../../api/openapi.bundle.yaml") if err != nil { t.Error(err) } @@ -189,7 +189,7 @@ func TestNewOpenAPIValidation(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - d, err := ioutil.ReadFile(tt.openapiYamlFile) + d, err := os.ReadFile(tt.openapiYamlFile) if err != nil { t.Error(err) } diff --git a/api/turing/service/alert_service_test.go b/api/turing/service/alert_service_test.go index 325ec1ae3..8fd4a45a7 100644 --- a/api/turing/service/alert_service_test.go +++ b/api/turing/service/alert_service_test.go @@ -3,7 +3,7 @@ package service import ( "errors" "fmt" - "io/ioutil" + "io" "net/http" "net/http/httptest" "strings" @@ -519,7 +519,7 @@ func newMockGitlabServer() (mockGitlab *httptest.Server, requestRecords map[stri records := make(map[string]string) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { record := fmt.Sprintf("%s %s", r.Method, r.URL.Path) - body, err := ioutil.ReadAll(r.Body) + body, err := io.ReadAll(r.Body) if body != nil && err == nil { records[record] = string(body) } else { diff --git a/api/turing/service/mlp_service_test.go b/api/turing/service/mlp_service_test.go index 2097b75b5..e39afc7a2 100644 --- a/api/turing/service/mlp_service_test.go +++ b/api/turing/service/mlp_service_test.go @@ -2,7 +2,6 @@ package service import ( "context" - "io/ioutil" "net/http" "os" "reflect" @@ -48,12 +47,12 @@ func testSetupEnvForGoogleCredentials(t *testing.T) (reset func()) { "token_uri": "https://oauth2.googleapis.com/token" }`) - file, err := ioutil.TempFile("", "dummy-service-account") + file, err := os.CreateTemp("", "dummy-service-account") if err != nil { t.Fatal(err) } - err = ioutil.WriteFile(file.Name(), serviceAccountKey, 0644) + err = os.WriteFile(file.Name(), serviceAccountKey, 0644) if err != nil { t.Fatal(err) } diff --git a/api/turing/service/pod_log_service_test.go b/api/turing/service/pod_log_service_test.go index 0a1bbf712..c4249a8de 100644 --- a/api/turing/service/pod_log_service_test.go +++ b/api/turing/service/pod_log_service_test.go @@ -3,7 +3,7 @@ package service import ( "bytes" "errors" - "io/ioutil" + "io" "testing" "time" @@ -119,24 +119,24 @@ func TestPodLogServiceListPodLogs(t *testing.T) { controller. On("ListPodLogs", mock.Anything, "namespace", "json-payload", &corev1.PodLogOptions{Container: "user-container", Timestamps: true, SinceTime: &sinceTimeV1Minus1Sec}). - Return(ioutil.NopCloser(bytes.NewBufferString(`2020-07-07T06:59:59Z {"foo":"bar", "baz": 5} + Return(io.NopCloser(bytes.NewBufferString(`2020-07-07T06:59:59Z {"foo":"bar", "baz": 5} 2020-07-07T07:00:05Z {"foo":"bar", "baz": 5} 2020-07-07T07:00:10Z {"foo":"bar", "baz": 10}`)), nil) controller. On("ListPodLogs", mock.Anything, "namespace", "json-payload", &corev1.PodLogOptions{Container: "user-container", Timestamps: true}). - Return(ioutil.NopCloser(bytes.NewBufferString(`2020-07-07T06:59:59Z {"foo":"bar", "baz": 5} + Return(io.NopCloser(bytes.NewBufferString(`2020-07-07T06:59:59Z {"foo":"bar", "baz": 5} 2020-07-07T07:00:05Z {"foo":"bar", "baz": 5} 2020-07-07T07:00:10Z {"foo":"bar", "baz": 10}`)), nil) controller. On("ListPodLogs", mock.Anything, "namespace", "json-payload", &corev1.PodLogOptions{Container: "user-container", Timestamps: true, TailLines: &tailLines}). - Return(ioutil.NopCloser(bytes.NewBufferString(`2020-07-07T07:00:05Z {"foo":"bar", "baz": 5} + Return(io.NopCloser(bytes.NewBufferString(`2020-07-07T07:00:05Z {"foo":"bar", "baz": 5} 2020-07-07T07:00:10Z {"foo":"bar", "baz": 10}`)), nil) controller. On("ListPodLogs", mock.Anything, "namespace", "text-payload", &corev1.PodLogOptions{Container: "user-container", Timestamps: true, SinceTime: &sinceTimeV1Minus1Sec}). - Return(ioutil.NopCloser(bytes.NewBufferString(`2020-07-07T08:00:05Z line1 + Return(io.NopCloser(bytes.NewBufferString(`2020-07-07T08:00:05Z line1 2020-07-07T08:00:10Z line2 invalidtimestamp line3 @@ -144,7 +144,7 @@ invalidtimestamp line3 controller. On("ListPodLogs", mock.Anything, "namespace", "text-payload", &corev1.PodLogOptions{Container: "user-container", Timestamps: true, TailLines: &tailLines}). - Return(ioutil.NopCloser(bytes.NewBufferString(`2020-07-07T08:00:05Z line1 + Return(io.NopCloser(bytes.NewBufferString(`2020-07-07T08:00:05Z line1 2020-07-07T08:00:10Z line2 invalidtimestamp line3 @@ -152,7 +152,7 @@ invalidtimestamp line3 controller. On("ListPodLogs", mock.Anything, "namespace", "text-payload", &corev1.PodLogOptions{Container: "user-container", Timestamps: true}). - Return(ioutil.NopCloser(bytes.NewBufferString(`2020-07-07T08:00:05Z line1 + Return(io.NopCloser(bytes.NewBufferString(`2020-07-07T08:00:05Z line1 2020-07-07T08:00:10Z line2 invalidtimestamp line3 diff --git a/api/turing/utils/merge.go b/api/turing/utils/merge.go index 2ae902f6d..0f132871c 100644 --- a/api/turing/utils/merge.go +++ b/api/turing/utils/merge.go @@ -3,7 +3,6 @@ package utils import ( "bytes" "fmt" - "io/ioutil" "os" "gopkg.in/yaml.v3" @@ -83,7 +82,7 @@ func MergeMaps(originalMap, override map[string]interface{}) (map[string]interfa } func readYAML(filepath string) (map[string]interface{}, error) { - file, err := ioutil.ReadFile(filepath) + file, err := os.ReadFile(filepath) if err != nil { return nil, err } From 32666cd14f2db408d9aa707a9d2711df365becce Mon Sep 17 00:00:00 2001 From: ewezy Date: Tue, 13 Sep 2022 15:49:12 +0800 Subject: [PATCH 16/26] Fix lint comments on indenting conditional statements --- api/e2e/test/helpers_api_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/e2e/test/helpers_api_test.go b/api/e2e/test/helpers_api_test.go index c1a7d56be..36e501b37 100644 --- a/api/e2e/test/helpers_api_test.go +++ b/api/e2e/test/helpers_api_test.go @@ -275,7 +275,8 @@ func withDeployedRouter( globalTestContext.ProjectName, fmt.Sprintf("%s-turing-enricher-%d-0-deployment", router.Name, routerVersion.Version))) } - if router.CurrRouterVersion.Ensembler != nil && router.CurrRouterVersion.Ensembler.Type != models.EnsemblerStandardType { + if router.CurrRouterVersion.Ensembler != nil && + router.CurrRouterVersion.Ensembler.Type != models.EnsemblerStandardType { require.True(t, isDeploymentExists( globalTestContext.clusterClients, globalTestContext.ProjectName, From 38b1c7c50da94db0c654c82971a16d5694630086 Mon Sep 17 00:00:00 2001 From: ewezy Date: Thu, 15 Sep 2022 10:36:12 +0800 Subject: [PATCH 17/26] Fix e2e tests after changes from rebasing --- api/turing/cluster/servicebuilder/router_test.go | 3 ++- ...er_version_success_std_ensembler_with_route_name_path.json | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/api/turing/cluster/servicebuilder/router_test.go b/api/turing/cluster/servicebuilder/router_test.go index a9bb208e8..a88c95e78 100644 --- a/api/turing/cluster/servicebuilder/router_test.go +++ b/api/turing/cluster/servicebuilder/router_test.go @@ -353,7 +353,8 @@ func TestNewRouterService(t *testing.T) { ContainerPort: 8080, MinReplicas: 2, MaxReplicas: 4, - TargetConcurrency: 1, + AutoscalingMetric: "rps", + AutoscalingTarget: "100", QueueProxyResourcePercentage: 20, UserContainerLimitRequestFactor: 1.5, }, diff --git a/api/turing/testdata/cluster/servicebuilder/router_version_success_std_ensembler_with_route_name_path.json b/api/turing/testdata/cluster/servicebuilder/router_version_success_std_ensembler_with_route_name_path.json index 89ff059a5..24ba9530e 100644 --- a/api/turing/testdata/cluster/servicebuilder/router_version_success_std_ensembler_with_route_name_path.json +++ b/api/turing/testdata/cluster/servicebuilder/router_version_success_std_ensembler_with_route_name_path.json @@ -87,6 +87,10 @@ "cpu_request": "400m", "memory_request": "512Mi" }, + "autoscaling_policy": { + "metric": "rps", + "target": "100" + }, "timeout": "5s", "log_config": { "log_level": "INFO", From 47f95ef4e156619472d7fa0f119b33f51f1802c6 Mon Sep 17 00:00:00 2001 From: ewezy Date: Thu, 15 Sep 2022 12:27:29 +0800 Subject: [PATCH 18/26] Fix bug in e2e test for std ensembler with route name path --- api/e2e/test/01_create_router_test.go | 35 ++++++++++--------- api/e2e/test/02_update_router_invalid_test.go | 3 +- api/e2e/test/03_create_router_version_test.go | 29 +++++++-------- ...with_route_name_path_std_ensembler_test.go | 2 +- .../configs/plugin_config_example.yaml | 2 -- infra/e2e/turing.values.yaml | 2 ++ 6 files changed, 39 insertions(+), 34 deletions(-) diff --git a/api/e2e/test/01_create_router_test.go b/api/e2e/test/01_create_router_test.go index 8c0116ac8..a91a19097 100644 --- a/api/e2e/test/01_create_router_test.go +++ b/api/e2e/test/01_create_router_test.go @@ -49,20 +49,21 @@ func TestCreateRouter(t *testing.T) { response.StatusCode, string(responsePayload)) actualResponse := gjson.GetBytes(responsePayload, "response").String() expectedResponse := `{ - "experiment": { - "configuration": { - "foo":"bar" - } - }, - "route_responses": [ - { - "data": { - "version": "control" - }, - "is_default": false, - "route": "control" - } - ] + "experiment": { + "configuration": { + "foo":"bar", + "route_name":"treatment-a" + } + }, + "route_responses": [ + { + "data": { + "version": "control" + }, + "is_default": false, + "route": "control" + } + ] }` assert.JSONEq(t, expectedResponse, actualResponse) }) @@ -94,7 +95,8 @@ func TestCreateRouter(t *testing.T) { "response": { "experiment": { "configuration": { - "foo": "bar" + "foo": "bar", + "route_name":"treatment-a" } }, "route_responses": [ @@ -120,7 +122,8 @@ func TestCreateRouter(t *testing.T) { "response": { "experiment": { "configuration": { - "bar": "baz" + "bar": "baz", + "route_name":"control" } }, "route_responses": [ diff --git a/api/e2e/test/02_update_router_invalid_test.go b/api/e2e/test/02_update_router_invalid_test.go index 005790d12..b96bed8ba 100644 --- a/api/e2e/test/02_update_router_invalid_test.go +++ b/api/e2e/test/02_update_router_invalid_test.go @@ -145,7 +145,8 @@ func TestUpdateRouterInvalidConfig(t *testing.T) { expectedResponse := `{ "experiment": { "configuration": { - "foo":"bar" + "foo":"bar", + "route_name":"treatment-a" } }, "route_responses": [ diff --git a/api/e2e/test/03_create_router_version_test.go b/api/e2e/test/03_create_router_version_test.go index 2bf12c66d..47d22ae88 100644 --- a/api/e2e/test/03_create_router_version_test.go +++ b/api/e2e/test/03_create_router_version_test.go @@ -130,20 +130,21 @@ func TestCreateRouterVersion(t *testing.T) { response.StatusCode, string(responsePayload)) actualResponse := gjson.GetBytes(responsePayload, "response").String() expectedResponse := `{ - "experiment": { - "configuration": { - "foo":"bar" - } - }, - "route_responses": [ - { - "data": { - "version": "control" - }, - "is_default": false, - "route": "control" - } - ] + "experiment": { + "configuration": { + "foo":"bar", + "route_name":"treatment-a" + } + }, + "route_responses": [ + { + "data": { + "version": "control" + }, + "is_default": false, + "route": "control" + } + ] }` assert.JSONEq(t, expectedResponse, actualResponse) }) diff --git a/api/e2e/test/09_deploy_router_with_route_name_path_std_ensembler_test.go b/api/e2e/test/09_deploy_router_with_route_name_path_std_ensembler_test.go index 4839e02dd..4ba2fc229 100644 --- a/api/e2e/test/09_deploy_router_with_route_name_path_std_ensembler_test.go +++ b/api/e2e/test/09_deploy_router_with_route_name_path_std_ensembler_test.go @@ -45,7 +45,7 @@ func TestDeployRouterWithRouteNamePathInStandardEnsembler(t *testing.T) { response.StatusCode, string(responsePayload)) actualResponse := string(responsePayload) expectedResponse := `{ - "version" : "control" + "version" : "treatment-a" }` assert.JSONEq(t, expectedResponse, actualResponse) }) diff --git a/engines/experiment/examples/plugins/hardcoded/configs/plugin_config_example.yaml b/engines/experiment/examples/plugins/hardcoded/configs/plugin_config_example.yaml index e3b225314..fe0a159cf 100644 --- a/engines/experiment/examples/plugins/hardcoded/configs/plugin_config_example.yaml +++ b/engines/experiment/examples/plugins/hardcoded/configs/plugin_config_example.yaml @@ -17,12 +17,10 @@ experiments: traffic: 0.85 treatment_configuration: foo: bar - route_name: treatment-a treatment-1: traffic: 0.15 treatment_configuration: bar: baz - route_name: control variables: '001': - name: client diff --git a/infra/e2e/turing.values.yaml b/infra/e2e/turing.values.yaml index b8ab47441..5c139ebe3 100644 --- a/infra/e2e/turing.values.yaml +++ b/infra/e2e/turing.values.yaml @@ -37,10 +37,12 @@ turing: traffic: 0.85 treatment_configuration: foo: bar + route_name: treatment-a treatment-1: traffic: 0.15 treatment_configuration: bar: baz + route_name: control postgresql: &postgresql persistence: From d93c4db40523c4f25123054205d7a45cf53d3cad Mon Sep 17 00:00:00 2001 From: ewezy Date: Thu, 15 Sep 2022 13:57:18 +0800 Subject: [PATCH 19/26] Fix sdk e2e tests --- sdk/e2e/01_create_router_test.py | 10 +++++++--- sdk/e2e/02_update_router_invalid_test.py | 2 +- sdk/e2e/03_create_router_version_test.py | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/sdk/e2e/01_create_router_test.py b/sdk/e2e/01_create_router_test.py index f6da7c468..903a3e4a8 100644 --- a/sdk/e2e/01_create_router_test.py +++ b/sdk/e2e/01_create_router_test.py @@ -137,7 +137,7 @@ def test_create_router(): ) assert response.status_code == 200 expected_response = { - "experiment": {"configuration": {"foo": "bar"}}, + "experiment": {"configuration": {"foo": "bar", "route_name": "treatment-a"}}, "route_responses": [ {"data": {"version": "control"}, "is_default": False, "route": "control"} ], @@ -161,7 +161,9 @@ def test_create_router(): "data": { "request": {"client": {"id": 4}}, "response": { - "experiment": {"configuration": {"foo": "bar"}}, + "experiment": { + "configuration": {"foo": "bar", "route_name": "treatment-a"} + }, "route_responses": [ { "data": {"version": "control"}, @@ -177,7 +179,9 @@ def test_create_router(): "data": { "request": {"client": {"id": 7}}, "response": { - "experiment": {"configuration": {"bar": "baz"}}, + "experiment": { + "configuration": {"bar": "baz", "route_name": "control"} + }, "route_responses": [ { "data": {"version": "control"}, diff --git a/sdk/e2e/02_update_router_invalid_test.py b/sdk/e2e/02_update_router_invalid_test.py index fc959bd36..13a5919b3 100644 --- a/sdk/e2e/02_update_router_invalid_test.py +++ b/sdk/e2e/02_update_router_invalid_test.py @@ -75,7 +75,7 @@ def test_update_router_invalid_config(): ) assert response.status_code == 200 expected_response = { - "experiment": {"configuration": {"foo": "bar"}}, + "experiment": {"configuration": {"foo": "bar", "route_name": "treatment-a"}}, "route_responses": [ {"data": {"version": "control"}, "is_default": False, "route": "control"} ], diff --git a/sdk/e2e/03_create_router_version_test.py b/sdk/e2e/03_create_router_version_test.py index 987f4ddea..b3110bba9 100644 --- a/sdk/e2e/03_create_router_version_test.py +++ b/sdk/e2e/03_create_router_version_test.py @@ -92,7 +92,7 @@ def test_create_router_version(): ) assert response.status_code == 200 expected_response = { - "experiment": {"configuration": {"foo": "bar"}}, + "experiment": {"configuration": {"foo": "bar", "route_name": "treatment-a"}}, "route_responses": [ {"data": {"version": "control"}, "is_default": False, "route": "control"} ], From b1916d71b2e2f8aa15a98149b695cbf097dc9d58 Mon Sep 17 00:00:00 2001 From: ewezy Date: Thu, 15 Sep 2022 14:20:28 +0800 Subject: [PATCH 20/26] Remove experiment mappings as a required field and update regex for route name path --- api/api/openapi.bundle.yaml | 5 +---- api/api/specs/routers.yaml | 5 +---- api/turing/models/ensembler.go | 2 +- .../router_configmap_std_ensembler_with_exp_mappings.yml | 1 - .../router_configmap_std_ensembler_with_route_name_path.yml | 1 - .../servicebuilder/router_configmap_traffic_splitting.yml | 4 ---- 6 files changed, 3 insertions(+), 15 deletions(-) diff --git a/api/api/openapi.bundle.yaml b/api/api/openapi.bundle.yaml index 37f01ea56..6bc856811 100644 --- a/api/api/openapi.bundle.yaml +++ b/api/api/openapi.bundle.yaml @@ -2352,11 +2352,8 @@ components: $ref: '#/components/schemas/EnsemblerStandardConfig_experiment_mappings' type: array route_name_path: - pattern: ^\w*(?:\.\w+)*$ + pattern: ^[\w\-]*(?:\.[\w\-]+)*$ type: string - required: - - experiment_mappings - - route_name_path type: object EnsemblerDockerConfig: description: ensembler config when ensembler type is docker diff --git a/api/api/specs/routers.yaml b/api/api/specs/routers.yaml index c6f9c456c..0da3bc964 100644 --- a/api/api/specs/routers.yaml +++ b/api/api/specs/routers.yaml @@ -794,9 +794,6 @@ components: description: "ensembler config when ensembler type is standard" type: "object" nullable: true - required: - - "experiment_mappings" - - "route_name_path" properties: experiment_mappings: type: "array" @@ -821,7 +818,7 @@ components: example: "route-1" route_name_path: type: "string" - pattern: '^\w*(?:\.\w+)*$' + pattern: '^[\w\-]*(?:\.[\w\-]+)*$' EnsemblerDockerConfig: description: "ensembler config when ensembler type is docker" diff --git a/api/turing/models/ensembler.go b/api/turing/models/ensembler.go index 2137b088b..0418d0f19 100644 --- a/api/turing/models/ensembler.go +++ b/api/turing/models/ensembler.go @@ -32,7 +32,7 @@ const ( ) type EnsemblerStandardConfig struct { - ExperimentMappings []ExperimentMapping `json:"experiment_mappings" validate:"required,dive"` + ExperimentMappings []ExperimentMapping `json:"experiment_mappings" validate:"dive"` RouteNamePath string `json:"route_name_path"` } diff --git a/api/turing/testdata/cluster/servicebuilder/router_configmap_std_ensembler_with_exp_mappings.yml b/api/turing/testdata/cluster/servicebuilder/router_configmap_std_ensembler_with_exp_mappings.yml index 38e7164ed..28886b2b7 100644 --- a/api/turing/testdata/cluster/servicebuilder/router_configmap_std_ensembler_with_exp_mappings.yml +++ b/api/turing/testdata/cluster/servicebuilder/router_configmap_std_ensembler_with_exp_mappings.yml @@ -28,6 +28,5 @@ strategy: - experiment: exp_exp_test_experiment_2 route: route-2 treatment: treatment - route_name_path: "" type: fiber.DefaultTuringRoutingStrategy type: EAGER_ROUTER diff --git a/api/turing/testdata/cluster/servicebuilder/router_configmap_std_ensembler_with_route_name_path.yml b/api/turing/testdata/cluster/servicebuilder/router_configmap_std_ensembler_with_route_name_path.yml index 8ad7affa9..13765275a 100644 --- a/api/turing/testdata/cluster/servicebuilder/router_configmap_std_ensembler_with_route_name_path.yml +++ b/api/turing/testdata/cluster/servicebuilder/router_configmap_std_ensembler_with_route_name_path.yml @@ -29,7 +29,6 @@ strategy: app_version: field: appVer field_source: header - experiment_mappings: [] route_name_path: policy.route_name type: fiber.DefaultTuringRoutingStrategy type: EAGER_ROUTER diff --git a/api/turing/testdata/cluster/servicebuilder/router_configmap_traffic_splitting.yml b/api/turing/testdata/cluster/servicebuilder/router_configmap_traffic_splitting.yml index d8e174ad9..e7c923e50 100644 --- a/api/turing/testdata/cluster/servicebuilder/router_configmap_traffic_splitting.yml +++ b/api/turing/testdata/cluster/servicebuilder/router_configmap_traffic_splitting.yml @@ -26,7 +26,6 @@ routes: - experiment: exp_exp_test_experiment_2 route: route-2 treatment: treatment - route_name_path: "" type: fiber.DefaultTuringRoutingStrategy type: EAGER_ROUTER - id: traffic-split-0 @@ -59,7 +58,6 @@ routes: - experiment: exp_exp_test_experiment_2 route: route-2 treatment: treatment - route_name_path: "" type: fiber.DefaultTuringRoutingStrategy type: EAGER_ROUTER - id: traffic-split-1 @@ -92,7 +90,6 @@ routes: - experiment: exp_exp_test_experiment_2 route: route-2 treatment: treatment - route_name_path: "" type: fiber.DefaultTuringRoutingStrategy type: EAGER_ROUTER - id: traffic-split-2 @@ -125,7 +122,6 @@ routes: - experiment: exp_exp_test_experiment_2 route: route-2 treatment: treatment - route_name_path: "" type: fiber.DefaultTuringRoutingStrategy type: EAGER_ROUTER strategy: From 73b5f0442544e01864b332ef3736644711985aaa Mon Sep 17 00:00:00 2001 From: ewezy Date: Fri, 16 Sep 2022 12:06:30 +0800 Subject: [PATCH 21/26] Add custom validator for standard ensembler config --- api/turing/validation/validator.go | 21 ++++++ api/turing/validation/validator_test.go | 88 +++++++++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/api/turing/validation/validator.go b/api/turing/validation/validator.go index ef584d220..4a36045ce 100644 --- a/api/turing/validation/validator.go +++ b/api/turing/validation/validator.go @@ -33,6 +33,9 @@ func NewValidator(expSvc service.ExperimentsService) (*validator.Validate, error request.ExperimentEngineConfig{}, ) } + + instance.RegisterStructValidation(validateEnsemblerStandardConfig, models.EnsemblerStandardConfig{}) + instance.RegisterStructValidation(validateRouterConfig, request.RouterConfig{}) // register common.RuleConditionOperator type to use its String representation for validation @@ -46,6 +49,24 @@ func NewValidator(expSvc service.ExperimentsService) (*validator.Validate, error return instance, nil } +func validateEnsemblerStandardConfig(sl validator.StructLevel) { + ensemblerStandardConfig := sl.Current().Interface().(models.EnsemblerStandardConfig) + // Verify that the ExperimentMappings and RouteNamePath are not both empty at the same time + if (len(ensemblerStandardConfig.ExperimentMappings) == 0) && ensemblerStandardConfig.RouteNamePath == "" { + sl.ReportError(ensemblerStandardConfig.ExperimentMappings, + "ExperimentMappings", "ExperimentMappings", "required when RouteNamePath is not set", "") + sl.ReportError(ensemblerStandardConfig.RouteNamePath, + "RouteNamePath", "RouteNamePath", "required when ExperimentMappings is not set", "") + } + // Verify that the ExperimentMappings and RouteNamePath are not both set at the same time + if len(ensemblerStandardConfig.ExperimentMappings) > 0 && ensemblerStandardConfig.RouteNamePath != "" { + sl.ReportError(ensemblerStandardConfig.ExperimentMappings, + "ExperimentMappings", "ExperimentMappings", "excluded when RouteNamePath is set", "") + sl.ReportError(ensemblerStandardConfig.RouteNamePath, + "RouteNamePath", "RouteNamePath", "excluded when ExperimentMappings is set", "") + } +} + func validateLogConfig(sl validator.StructLevel) { field := sl.Current().Interface().(request.LogConfig) switch field.ResultLoggerType { diff --git a/api/turing/validation/validator_test.go b/api/turing/validation/validator_test.go index 2c0c58601..feaaa9602 100644 --- a/api/turing/validation/validator_test.go +++ b/api/turing/validation/validator_test.go @@ -20,6 +20,94 @@ import ( "github.com/stretchr/testify/require" ) +func TestValidateEnsemblerStandardConfig(t *testing.T) { + tt := map[string]struct { + input models.EnsemblerStandardConfig + err string + }{ + "failure | experiment mappings and route name path undefined": { + input: models.EnsemblerStandardConfig{}, + err: "Key: 'EnsemblerStandardConfig.ExperimentMappings' Error:Field validation for 'ExperimentMappings' " + + "failed on the 'required when RouteNamePath is not set' tag\n" + + "Key: 'EnsemblerStandardConfig.RouteNamePath' Error:Field validation for 'RouteNamePath' failed on " + + "the 'required when ExperimentMappings is not set' tag", + }, + "failure | experiment mappings and route name path empty": { + input: models.EnsemblerStandardConfig{ + ExperimentMappings: []models.ExperimentMapping{}, + RouteNamePath: "", + }, + err: "Key: 'EnsemblerStandardConfig.ExperimentMappings' Error:Field validation for 'ExperimentMappings' " + + "failed on the 'required when RouteNamePath is not set' tag\n" + + "Key: 'EnsemblerStandardConfig.RouteNamePath' Error:Field validation for 'RouteNamePath' failed on " + + "the 'required when ExperimentMappings is not set' tag", + }, + "failure | experiment mappings empty and route name path undefined": { + input: models.EnsemblerStandardConfig{ + ExperimentMappings: []models.ExperimentMapping{}, + }, + err: "Key: 'EnsemblerStandardConfig.ExperimentMappings' Error:Field validation for 'ExperimentMappings' " + + "failed on the 'required when RouteNamePath is not set' tag\n" + + "Key: 'EnsemblerStandardConfig.RouteNamePath' Error:Field validation for 'RouteNamePath' failed on " + + "the 'required when ExperimentMappings is not set' tag", + }, + "failure | experiment mappings undefined and route name path empty": { + input: models.EnsemblerStandardConfig{ + RouteNamePath: "", + }, + err: "Key: 'EnsemblerStandardConfig.ExperimentMappings' Error:Field validation for 'ExperimentMappings' " + + "failed on the 'required when RouteNamePath is not set' tag\n" + + "Key: 'EnsemblerStandardConfig.RouteNamePath' Error:Field validation for 'RouteNamePath' failed on " + + "the 'required when ExperimentMappings is not set' tag", + }, + "failure | experiment mappings and route name path defined": { + input: models.EnsemblerStandardConfig{ + ExperimentMappings: []models.ExperimentMapping{ + { + Experiment: "experiment-1", + Treatment: "treatment-1", + Route: "route-1", + }, + }, + RouteNamePath: "route-1", + }, + err: "Key: 'EnsemblerStandardConfig.ExperimentMappings' Error:Field validation for 'ExperimentMappings' " + + "failed on the 'excluded when RouteNamePath is set' tag\n" + + "Key: 'EnsemblerStandardConfig.RouteNamePath' Error:Field validation for 'RouteNamePath' " + + "failed on the 'excluded when ExperimentMappings is set' tag", + }, + "success | only experiment mappings defined": { + input: models.EnsemblerStandardConfig{ + ExperimentMappings: []models.ExperimentMapping{ + { + Experiment: "experiment-1", + Treatment: "treatment-1", + Route: "route-1", + }, + }, + }, + }, + "success | only route name path defined": { + input: models.EnsemblerStandardConfig{ + RouteNamePath: "route-1", + }, + }, + } + + for name, tc := range tt { + t.Run(name, func(t *testing.T) { + validate, err := validation.NewValidator(nil) + assert.NoError(t, err) + err = validate.Struct(tc.input) + if tc.err == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.err) + } + }) + } +} + func TestValidateLogConfig(t *testing.T) { tt := map[string]struct { input request.LogConfig From ce788172b5aa8602b62b20994a1ce657dbf08ead Mon Sep 17 00:00:00 2001 From: ewezy Date: Fri, 16 Sep 2022 12:13:43 +0800 Subject: [PATCH 22/26] Remove standard ensembler config schema checks from request verification layer --- api/turing/api/request/request.go | 11 +-- api/turing/api/request/request_test.go | 124 ------------------------- 2 files changed, 2 insertions(+), 133 deletions(-) diff --git a/api/turing/api/request/request.go b/api/turing/api/request/request.go index 57a243ac2..860192475 100644 --- a/api/turing/api/request/request.go +++ b/api/turing/api/request/request.go @@ -158,15 +158,8 @@ func (r RouterConfig) BuildRouterVersion( r.Ensembler.DockerConfig.AutoscalingPolicy = getAutoscalingPolicyOrDefault( r.Ensembler.DockerConfig.AutoscalingPolicy) } - if r.Ensembler.Type == models.EnsemblerStandardType { - if r.Ensembler.StandardConfig == nil { - return nil, errors.New("missing ensembler standard config") - } - - // Verify that the ExperimentMappings and RouteNamePath are not both set at the same time - if len(r.Ensembler.StandardConfig.ExperimentMappings) > 0 && r.Ensembler.StandardConfig.RouteNamePath != "" { - return nil, errors.New("experiment mappings and route name path cannot both be configured together") - } + if r.Ensembler.Type == models.EnsemblerStandardType && r.Ensembler.StandardConfig == nil { + return nil, errors.New("missing ensembler standard config") } if r.Ensembler.Type == models.EnsemblerPyFuncType { if r.Ensembler.PyfuncConfig == nil { diff --git a/api/turing/api/request/request_test.go b/api/turing/api/request/request_test.go index 70bdc057c..bdfe8761d 100644 --- a/api/turing/api/request/request_test.go +++ b/api/turing/api/request/request_test.go @@ -446,130 +446,6 @@ func TestRequestBuildRouterVersionWithDefaultConfig(t *testing.T) { assertgotest.DeepEqual(t, expected, *got) } -func TestRequestBuildRouterVersionWithStandardEnsembler(t *testing.T) { - defaults := config.RouterDefaults{ - Image: "routerimage", - FiberDebugLogEnabled: true, - CustomMetricsEnabled: true, - JaegerEnabled: true, - JaegerCollectorEndpoint: "jaegerendpoint", - LogLevel: "DEBUG", - FluentdConfig: &config.FluentdConfig{ - Image: "fluentdimage", - Tag: "fluentdtag", - }, - } - projectID := models.ID(1) - router := createOrUpdateRequest.BuildRouter(projectID) - - // Set up mock Crypto service - cryptoSvc := &mocks.CryptoService{} - cryptoSvc.On("Encrypt", "dummy_passkey").Return("enc_passkey", nil) - - // Set up mock Experiment service - expSvc := &mocks.ExperimentsService{} - expSvc.On("IsClientSelectionEnabled", mock.Anything).Return(true, nil) - - // Set up mock Ensembler service - ensemblerSvc := &mocks.EnsemblersService{} - - // Define tests - tests := map[string]struct { - testEnsembler models.Ensembler - expectedEnsembler models.Ensembler - err string - }{ - "success | only route name path is set": { - testEnsembler: models.Ensembler{ - Type: models.EnsemblerStandardType, - StandardConfig: &models.EnsemblerStandardConfig{ - RouteNamePath: "abc", - }, - }, - expectedEnsembler: models.Ensembler{ - Type: models.EnsemblerStandardType, - StandardConfig: &models.EnsemblerStandardConfig{ - RouteNamePath: "abc", - }, - }, - }, - "success | only experiment mappings are set": { - testEnsembler: models.Ensembler{ - Type: models.EnsemblerStandardType, - StandardConfig: &models.EnsemblerStandardConfig{ - ExperimentMappings: []models.ExperimentMapping{ - { - Experiment: "experiment-1", - Treatment: "treatment-1", - Route: "route-1", - }, - { - Experiment: "experiment-1", - Treatment: "treatment-1", - Route: "route-1", - }, - }, - }, - }, - expectedEnsembler: models.Ensembler{ - Type: models.EnsemblerStandardType, - StandardConfig: &models.EnsemblerStandardConfig{ - ExperimentMappings: []models.ExperimentMapping{ - { - Experiment: "experiment-1", - Treatment: "treatment-1", - Route: "route-1", - }, - { - Experiment: "experiment-1", - Treatment: "treatment-1", - Route: "route-1", - }, - }, - }, - }, - }, - "failure | both experiment mappings and route name path are set": { - testEnsembler: models.Ensembler{ - Type: models.EnsemblerStandardType, - StandardConfig: &models.EnsemblerStandardConfig{ - ExperimentMappings: []models.ExperimentMapping{ - { - Experiment: "experiment-1", - Treatment: "treatment-1", - Route: "route-1", - }, - { - Experiment: "experiment-1", - Treatment: "treatment-1", - Route: "route-1", - }, - }, - RouteNamePath: "abc", - }, - }, - err: "experiment mappings and route name path cannot both be configured together", - }, - } - - // Run tests - for name, data := range tests { - t.Run(name, func(t *testing.T) { - // Creates a router config that is valid except for the invalid ensembler config - testRouterConfig := validRouterConfig - testRouterConfig.Ensembler = &data.testEnsembler - - result, err := testRouterConfig.BuildRouterVersion(router, &defaults, cryptoSvc, expSvc, ensemblerSvc) - if data.err != "" { - assert.Nil(t, result) - assert.EqualError(t, err, data.err) - } else { - assert.Equal(t, data.expectedEnsembler, *result.Ensembler) - } - }) - } -} - func TestRequestBuildRouterVersionWithUnavailablePyFuncEnsembler(t *testing.T) { defaults := config.RouterDefaults{ Image: "routerimage", From 25f603969a2ff3e5fc91683f1d988fa7db6456df Mon Sep 17 00:00:00 2001 From: ewezy Date: Fri, 16 Sep 2022 12:39:02 +0800 Subject: [PATCH 23/26] Make standard ensembler only parse non nil configs to fiber config --- api/turing/cluster/servicebuilder/router.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/api/turing/cluster/servicebuilder/router.go b/api/turing/cluster/servicebuilder/router.go index 93ac41245..66a202557 100644 --- a/api/turing/cluster/servicebuilder/router.go +++ b/api/turing/cluster/servicebuilder/router.go @@ -491,8 +491,13 @@ func buildFiberConfigMap( } if ver.Ensembler != nil && ver.Ensembler.Type == models.EnsemblerStandardType { - propsMap["experiment_mappings"] = ver.Ensembler.StandardConfig.ExperimentMappings - propsMap["route_name_path"] = ver.Ensembler.StandardConfig.RouteNamePath + if ver.Ensembler.StandardConfig.ExperimentMappings != nil && + len(ver.Ensembler.StandardConfig.ExperimentMappings) != 0 { + propsMap["experiment_mappings"] = ver.Ensembler.StandardConfig.ExperimentMappings + } + if ver.Ensembler.StandardConfig.RouteNamePath != "" { + propsMap["route_name_path"] = ver.Ensembler.StandardConfig.RouteNamePath + } } properties, err := json.Marshal(propsMap) From 673d7abe26be17916f09bb5a32e9af06534ae46d Mon Sep 17 00:00:00 2001 From: ewezy Date: Fri, 16 Sep 2022 12:47:18 +0800 Subject: [PATCH 24/26] Remove unnecessary route name path field in json request --- ...outer_version_success_std_ensembler_with_route_name_path.json | 1 - 1 file changed, 1 deletion(-) diff --git a/api/turing/testdata/cluster/servicebuilder/router_version_success_std_ensembler_with_route_name_path.json b/api/turing/testdata/cluster/servicebuilder/router_version_success_std_ensembler_with_route_name_path.json index 24ba9530e..d17282b44 100644 --- a/api/turing/testdata/cluster/servicebuilder/router_version_success_std_ensembler_with_route_name_path.json +++ b/api/turing/testdata/cluster/servicebuilder/router_version_success_std_ensembler_with_route_name_path.json @@ -108,7 +108,6 @@ "id": 300, "type": "standard", "standard_config": { - "experiment_mappings": [], "route_name_path": "policy.route_name" } } From c0c66db69f330bfa1babbde493b1a6f49a2fd4ce Mon Sep 17 00:00:00 2001 From: ewezy Date: Mon, 19 Sep 2022 00:10:48 +0800 Subject: [PATCH 25/26] Remove duplicated error messages from EnsemblerStandardConfig custom validation checks --- api/turing/validation/validator.go | 4 ---- api/turing/validation/validator_test.go | 20 +++++--------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/api/turing/validation/validator.go b/api/turing/validation/validator.go index 4a36045ce..45fcf07d7 100644 --- a/api/turing/validation/validator.go +++ b/api/turing/validation/validator.go @@ -55,15 +55,11 @@ func validateEnsemblerStandardConfig(sl validator.StructLevel) { if (len(ensemblerStandardConfig.ExperimentMappings) == 0) && ensemblerStandardConfig.RouteNamePath == "" { sl.ReportError(ensemblerStandardConfig.ExperimentMappings, "ExperimentMappings", "ExperimentMappings", "required when RouteNamePath is not set", "") - sl.ReportError(ensemblerStandardConfig.RouteNamePath, - "RouteNamePath", "RouteNamePath", "required when ExperimentMappings is not set", "") } // Verify that the ExperimentMappings and RouteNamePath are not both set at the same time if len(ensemblerStandardConfig.ExperimentMappings) > 0 && ensemblerStandardConfig.RouteNamePath != "" { sl.ReportError(ensemblerStandardConfig.ExperimentMappings, "ExperimentMappings", "ExperimentMappings", "excluded when RouteNamePath is set", "") - sl.ReportError(ensemblerStandardConfig.RouteNamePath, - "RouteNamePath", "RouteNamePath", "excluded when ExperimentMappings is set", "") } } diff --git a/api/turing/validation/validator_test.go b/api/turing/validation/validator_test.go index feaaa9602..51cf31b35 100644 --- a/api/turing/validation/validator_test.go +++ b/api/turing/validation/validator_test.go @@ -28,9 +28,7 @@ func TestValidateEnsemblerStandardConfig(t *testing.T) { "failure | experiment mappings and route name path undefined": { input: models.EnsemblerStandardConfig{}, err: "Key: 'EnsemblerStandardConfig.ExperimentMappings' Error:Field validation for 'ExperimentMappings' " + - "failed on the 'required when RouteNamePath is not set' tag\n" + - "Key: 'EnsemblerStandardConfig.RouteNamePath' Error:Field validation for 'RouteNamePath' failed on " + - "the 'required when ExperimentMappings is not set' tag", + "failed on the 'required when RouteNamePath is not set' tag", }, "failure | experiment mappings and route name path empty": { input: models.EnsemblerStandardConfig{ @@ -38,27 +36,21 @@ func TestValidateEnsemblerStandardConfig(t *testing.T) { RouteNamePath: "", }, err: "Key: 'EnsemblerStandardConfig.ExperimentMappings' Error:Field validation for 'ExperimentMappings' " + - "failed on the 'required when RouteNamePath is not set' tag\n" + - "Key: 'EnsemblerStandardConfig.RouteNamePath' Error:Field validation for 'RouteNamePath' failed on " + - "the 'required when ExperimentMappings is not set' tag", + "failed on the 'required when RouteNamePath is not set' tag", }, "failure | experiment mappings empty and route name path undefined": { input: models.EnsemblerStandardConfig{ ExperimentMappings: []models.ExperimentMapping{}, }, err: "Key: 'EnsemblerStandardConfig.ExperimentMappings' Error:Field validation for 'ExperimentMappings' " + - "failed on the 'required when RouteNamePath is not set' tag\n" + - "Key: 'EnsemblerStandardConfig.RouteNamePath' Error:Field validation for 'RouteNamePath' failed on " + - "the 'required when ExperimentMappings is not set' tag", + "failed on the 'required when RouteNamePath is not set' tag", }, "failure | experiment mappings undefined and route name path empty": { input: models.EnsemblerStandardConfig{ RouteNamePath: "", }, err: "Key: 'EnsemblerStandardConfig.ExperimentMappings' Error:Field validation for 'ExperimentMappings' " + - "failed on the 'required when RouteNamePath is not set' tag\n" + - "Key: 'EnsemblerStandardConfig.RouteNamePath' Error:Field validation for 'RouteNamePath' failed on " + - "the 'required when ExperimentMappings is not set' tag", + "failed on the 'required when RouteNamePath is not set' tag", }, "failure | experiment mappings and route name path defined": { input: models.EnsemblerStandardConfig{ @@ -72,9 +64,7 @@ func TestValidateEnsemblerStandardConfig(t *testing.T) { RouteNamePath: "route-1", }, err: "Key: 'EnsemblerStandardConfig.ExperimentMappings' Error:Field validation for 'ExperimentMappings' " + - "failed on the 'excluded when RouteNamePath is set' tag\n" + - "Key: 'EnsemblerStandardConfig.RouteNamePath' Error:Field validation for 'RouteNamePath' " + - "failed on the 'excluded when ExperimentMappings is set' tag", + "failed on the 'excluded when RouteNamePath is set' tag", }, "success | only experiment mappings defined": { input: models.EnsemblerStandardConfig{ From 0d79a22862c39a2ccee6ac35fdba61b1e280bb1d Mon Sep 17 00:00:00 2001 From: ewezy Date: Mon, 19 Sep 2022 10:39:21 +0800 Subject: [PATCH 26/26] Remove regex expression for route_name_path --- api/api/openapi.bundle.yaml | 1 - api/api/specs/routers.yaml | 1 - 2 files changed, 2 deletions(-) diff --git a/api/api/openapi.bundle.yaml b/api/api/openapi.bundle.yaml index 6bc856811..57201d662 100644 --- a/api/api/openapi.bundle.yaml +++ b/api/api/openapi.bundle.yaml @@ -2352,7 +2352,6 @@ components: $ref: '#/components/schemas/EnsemblerStandardConfig_experiment_mappings' type: array route_name_path: - pattern: ^[\w\-]*(?:\.[\w\-]+)*$ type: string type: object EnsemblerDockerConfig: diff --git a/api/api/specs/routers.yaml b/api/api/specs/routers.yaml index 0da3bc964..f5600d3af 100644 --- a/api/api/specs/routers.yaml +++ b/api/api/specs/routers.yaml @@ -818,7 +818,6 @@ components: example: "route-1" route_name_path: type: "string" - pattern: '^[\w\-]*(?:\.[\w\-]+)*$' EnsemblerDockerConfig: description: "ensembler config when ensembler type is docker"