diff --git a/.github/workflows/codecov-config/codecov.yml b/.github/workflows/codecov-config/codecov.yml
new file mode 100644
index 000000000..8901728c4
--- /dev/null
+++ b/.github/workflows/codecov-config/codecov.yml
@@ -0,0 +1,5 @@
+coverage:
+ status:
+ patch:
+ default:
+ threshold: 0.03%
\ No newline at end of file
diff --git a/.github/workflows/sdk.yaml b/.github/workflows/sdk.yaml
index 431a93272..e4134bec6 100644
--- a/.github/workflows/sdk.yaml
+++ b/.github/workflows/sdk.yaml
@@ -58,6 +58,7 @@ jobs:
flags: sdk-test-${{ matrix.python-version }}
name: sdk-test-${{ matrix.python-version }}
token: ${{ secrets.CODECOV_TOKEN }}
+ codecov_yml_path: ./.github/workflows/codecov-config/codecov.yml
release-rules:
runs-on: ubuntu-latest
diff --git a/.github/workflows/turing.yaml b/.github/workflows/turing.yaml
index 65b89bb4c..31a817b4f 100644
--- a/.github/workflows/turing.yaml
+++ b/.github/workflows/turing.yaml
@@ -282,6 +282,7 @@ jobs:
name: api-test
token: ${{ secrets.CODECOV_TOKEN }}
working-directory: api
+ codecov_yml_path: ../.github/workflows/codecov-config/codecov.yml
test-engines-router:
runs-on: ubuntu-latest
diff --git a/api/api/openapi.bundle.yaml b/api/api/openapi.bundle.yaml
index ab625aef3..0c8f8cd48 100644
--- a/api/api/openapi.bundle.yaml
+++ b/api/api/openapi.bundle.yaml
@@ -2125,6 +2125,7 @@ components:
port: 5
created_at: 2000-01-23T04:56:07.000+00:00
resource_request:
+ cpu_limit: cpu_limit
min_replica: 0
max_replica: 6
memory_request: memory_request
@@ -2161,6 +2162,7 @@ components:
updated_at: 2000-01-23T04:56:07.000+00:00
default_route_id: default_route_id
resource_request:
+ cpu_limit: cpu_limit
min_replica: 0
max_replica: 6
memory_request: memory_request
@@ -2174,6 +2176,7 @@ components:
project_id: 7
ensembler_id: 9
resource_request:
+ cpu_limit: cpu_limit
min_replica: 0
max_replica: 6
memory_request: memory_request
@@ -2207,6 +2210,7 @@ components:
target: target
port: 2
resource_request:
+ cpu_limit: cpu_limit
min_replica: 0
max_replica: 6
memory_request: memory_request
@@ -2311,6 +2315,7 @@ components:
type: object
ResourceRequest:
example:
+ cpu_limit: cpu_limit
min_replica: 0
max_replica: 6
memory_request: memory_request
@@ -2323,6 +2328,10 @@ components:
cpu_request:
pattern: ^(\d{1,3}(\.\d{1,3})?)$|^(\d{2,5}m)$
type: string
+ cpu_limit:
+ nullable: true
+ pattern: ^(\d{1,3}(\.\d{1,3})?)$|^(\d{2,5}m)$
+ type: string
memory_request:
pattern: ^\d+(Ei?|Pi?|Ti?|Gi?|Mi?|Ki?)?$
type: string
@@ -2419,6 +2428,7 @@ components:
port: 5
created_at: 2000-01-23T04:56:07.000+00:00
resource_request:
+ cpu_limit: cpu_limit
min_replica: 0
max_replica: 6
memory_request: memory_request
@@ -2481,6 +2491,7 @@ components:
project_id: 7
ensembler_id: 9
resource_request:
+ cpu_limit: cpu_limit
min_replica: 0
max_replica: 6
memory_request: memory_request
@@ -2514,6 +2525,7 @@ components:
target: target
port: 2
resource_request:
+ cpu_limit: cpu_limit
min_replica: 0
max_replica: 6
memory_request: memory_request
@@ -2588,6 +2600,7 @@ components:
target: target
port: 2
resource_request:
+ cpu_limit: cpu_limit
min_replica: 0
max_replica: 6
memory_request: memory_request
@@ -2640,6 +2653,7 @@ components:
project_id: 7
ensembler_id: 9
resource_request:
+ cpu_limit: cpu_limit
min_replica: 0
max_replica: 6
memory_request: memory_request
@@ -2770,6 +2784,7 @@ components:
port: 5
created_at: 2000-01-23T04:56:07.000+00:00
resource_request:
+ cpu_limit: cpu_limit
min_replica: 0
max_replica: 6
memory_request: memory_request
@@ -2830,6 +2845,7 @@ components:
- values
operator: in
resource_request:
+ cpu_limit: cpu_limit
min_replica: 0
max_replica: 6
memory_request: memory_request
@@ -2842,6 +2858,7 @@ components:
project_id: 7
ensembler_id: 9
resource_request:
+ cpu_limit: cpu_limit
min_replica: 0
max_replica: 6
memory_request: memory_request
@@ -2875,6 +2892,7 @@ components:
target: target
port: 2
resource_request:
+ cpu_limit: cpu_limit
min_replica: 0
max_replica: 6
memory_request: memory_request
@@ -2929,6 +2947,7 @@ components:
port: 5
created_at: 2000-01-23T04:56:07.000+00:00
resource_request:
+ cpu_limit: cpu_limit
min_replica: 0
max_replica: 6
memory_request: memory_request
@@ -2989,6 +3008,7 @@ components:
- values
operator: in
resource_request:
+ cpu_limit: cpu_limit
min_replica: 0
max_replica: 6
memory_request: memory_request
@@ -3001,6 +3021,7 @@ components:
project_id: 7
ensembler_id: 9
resource_request:
+ cpu_limit: cpu_limit
min_replica: 0
max_replica: 6
memory_request: memory_request
@@ -3034,6 +3055,7 @@ components:
target: target
port: 2
resource_request:
+ cpu_limit: cpu_limit
min_replica: 0
max_replica: 6
memory_request: memory_request
diff --git a/api/api/specs/routers.yaml b/api/api/specs/routers.yaml
index a8debcd09..e81cb8551 100644
--- a/api/api/specs/routers.yaml
+++ b/api/api/specs/routers.yaml
@@ -949,6 +949,10 @@ components:
cpu_request:
type: "string"
pattern: '^(\d{1,3}(\.\d{1,3})?)$|^(\d{2,5}m)$'
+ cpu_limit:
+ type: "string"
+ pattern: '^(\d{1,3}(\.\d{1,3})?)$|^(\d{2,5}m)$'
+ nullable: true
memory_request:
type: "string"
pattern: '^\d+(Ei?|Pi?|Ti?|Gi?|Mi?|Ki?)?$'
diff --git a/api/turing/cluster/knative_service.go b/api/turing/cluster/knative_service.go
index 48e900e6d..dc00550b5 100644
--- a/api/turing/cluster/knative_service.go
+++ b/api/turing/cluster/knative_service.go
@@ -50,9 +50,7 @@ type KnativeService struct {
TopologySpreadConstraints []corev1.TopologySpreadConstraint `json:"topologySpreadConstraints"`
// Resource properties
- QueueProxyResourcePercentage int `json:"queueProxyResourcePercentage"`
- UserContainerCPULimitRequestFactor float64 `json:"userContainerLimitCPURequestFactor"`
- UserContainerMemoryLimitRequestFactor float64 `json:"userContainerLimitMemoryRequestFactor"`
+ QueueProxyResourcePercentage int `json:"queueProxyResourcePercentage"`
}
// Creates a new config object compatible with the knative serving API, from
@@ -131,12 +129,6 @@ func (cfg *KnativeService) buildSvcSpec(
// Revision name
revisionName := getDefaultRevisionName(cfg.Name)
- // Build resource requirements for the user container
- resourceReqs := cfg.buildResourceReqs(
- cfg.UserContainerCPULimitRequestFactor,
- cfg.UserContainerMemoryLimitRequestFactor,
- )
-
// Build container spec
var portName string
// If protocol is using GRPC, add "h2c" which is required for grpc knative
@@ -151,7 +143,7 @@ func (cfg *KnativeService) buildSvcSpec(
ContainerPort: cfg.ContainerPort,
},
},
- Resources: resourceReqs,
+ Resources: cfg.buildResourceReqs(),
VolumeMounts: cfg.VolumeMounts,
Env: cfg.Envs,
}
diff --git a/api/turing/cluster/knative_service_test.go b/api/turing/cluster/knative_service_test.go
index 20ff03dc3..e9d36c017 100644
--- a/api/turing/cluster/knative_service_test.go
+++ b/api/turing/cluster/knative_service_test.go
@@ -17,13 +17,16 @@ import (
)
func TestBuildKnativeServiceConfig(t *testing.T) {
+ cpuRequest := resource.MustParse("400m")
+ memoryRequest := resource.MustParse("512Mi")
+
// Test configuration
- baseSvc := &BaseService{
+ baseSvc := BaseService{
Name: "test-svc",
Namespace: "test-namespace",
Image: "asia.gcr.io/gcp-project-id/turing-router:latest",
- CPURequests: resource.MustParse("400m"),
- MemoryRequests: resource.MustParse("512Mi"),
+ CPURequests: cpuRequest,
+ MemoryRequests: memoryRequest,
ProbePort: 8080,
LivenessHTTPGetPath: "/v1/internal/live",
ReadinessHTTPGetPath: "/v1/internal/ready",
@@ -87,6 +90,10 @@ func TestBuildKnativeServiceConfig(t *testing.T) {
},
}
+ baseSvcWithResourceLimits := baseSvc
+ baseSvcWithResourceLimits.CPULimit = &cpuRequest
+ baseSvcWithResourceLimits.MemoryLimit = &memoryRequest
+
// Expected specs
var defaultConcurrency, defaultTrafficPercent int64 = 0, 100
var defaultLatestRevision = true
@@ -100,13 +107,10 @@ func TestBuildKnativeServiceConfig(t *testing.T) {
},
}
resources := corev1.ResourceRequirements{
- Limits: map[corev1.ResourceName]resource.Quantity{
- corev1.ResourceCPU: resource.MustParse("600m"),
- corev1.ResourceMemory: resource.MustParse("768Mi"),
- },
+ Limits: map[corev1.ResourceName]resource.Quantity{},
Requests: map[corev1.ResourceName]resource.Quantity{
- corev1.ResourceCPU: resource.MustParse("400m"),
- corev1.ResourceMemory: resource.MustParse("512Mi"),
+ corev1.ResourceCPU: cpuRequest,
+ corev1.ResourceMemory: memoryRequest,
},
}
envs := []corev1.EnvVar{
@@ -123,47 +127,57 @@ func TestBuildKnativeServiceConfig(t *testing.T) {
{Name: "APP_BQ_TABLE", Value: "turing_log_test"},
{Name: "APP_BQ_BATCH_LOAD", Value: "false"},
}
- podSpec := corev1.PodSpec{
- Containers: []corev1.Container{
+
+ containerSpec := corev1.Container{
+ Name: "user-container",
+ Image: "asia.gcr.io/gcp-project-id/turing-router:latest",
+ Ports: []corev1.ContainerPort{
{
- Name: "user-container",
- Image: "asia.gcr.io/gcp-project-id/turing-router:latest",
- Ports: []corev1.ContainerPort{
- {
- ContainerPort: 8080,
- },
- },
- Resources: resources,
- LivenessProbe: &corev1.Probe{
- ProbeHandler: corev1.ProbeHandler{
- HTTPGet: &corev1.HTTPGetAction{
- Port: intstr.FromInt(8080),
- Path: "/v1/internal/live",
- },
- },
- InitialDelaySeconds: 20,
- PeriodSeconds: 10,
- TimeoutSeconds: 5,
- FailureThreshold: 5,
+ ContainerPort: 8080,
+ },
+ },
+ Resources: resources,
+ LivenessProbe: &corev1.Probe{
+ ProbeHandler: corev1.ProbeHandler{
+ HTTPGet: &corev1.HTTPGetAction{
+ Port: intstr.FromInt(8080),
+ Path: "/v1/internal/live",
},
- ReadinessProbe: &corev1.Probe{
- ProbeHandler: corev1.ProbeHandler{
- HTTPGet: &corev1.HTTPGetAction{
- Port: intstr.FromInt(8080),
- Path: "/v1/internal/ready",
- },
- },
- InitialDelaySeconds: 20,
- PeriodSeconds: 10,
- SuccessThreshold: 1,
- TimeoutSeconds: 5,
- FailureThreshold: 5,
+ },
+ InitialDelaySeconds: 20,
+ PeriodSeconds: 10,
+ TimeoutSeconds: 5,
+ FailureThreshold: 5,
+ },
+ ReadinessProbe: &corev1.Probe{
+ ProbeHandler: corev1.ProbeHandler{
+ HTTPGet: &corev1.HTTPGetAction{
+ Port: intstr.FromInt(8080),
+ Path: "/v1/internal/ready",
},
- VolumeMounts: baseSvc.VolumeMounts,
- Env: envs,
},
+ InitialDelaySeconds: 20,
+ PeriodSeconds: 10,
+ SuccessThreshold: 1,
+ TimeoutSeconds: 5,
+ FailureThreshold: 5,
},
- Volumes: baseSvc.Volumes,
+ VolumeMounts: baseSvc.VolumeMounts,
+ Env: envs,
+ }
+ podSpec := corev1.PodSpec{
+ Containers: []corev1.Container{containerSpec},
+ Volumes: baseSvc.Volumes,
+ }
+
+ containerSpecWithResourceLimits := containerSpec
+ containerSpecWithResourceLimits.Resources.Limits = map[corev1.ResourceName]resource.Quantity{
+ corev1.ResourceCPU: cpuRequest,
+ corev1.ResourceMemory: memoryRequest,
+ }
+ podSpecWithResourceLimits := corev1.PodSpec{
+ Containers: []corev1.Container{containerSpecWithResourceLimits},
+ Volumes: baseSvc.Volumes,
}
tests := map[string]struct {
@@ -172,16 +186,14 @@ func TestBuildKnativeServiceConfig(t *testing.T) {
}{
"basic": {
serviceCfg: KnativeService{
- BaseService: baseSvc,
- ContainerPort: 8080,
- MinReplicas: 1,
- MaxReplicas: 2,
- AutoscalingMetric: "concurrency",
- AutoscalingTarget: "1",
- IsClusterLocal: true,
- QueueProxyResourcePercentage: 30,
- UserContainerCPULimitRequestFactor: 1.5,
- UserContainerMemoryLimitRequestFactor: 1.5,
+ BaseService: &baseSvc,
+ ContainerPort: 8080,
+ MinReplicas: 1,
+ MaxReplicas: 2,
+ AutoscalingMetric: "concurrency",
+ AutoscalingTarget: "1",
+ IsClusterLocal: true,
+ QueueProxyResourcePercentage: 30,
},
expectedSpec: knservingv1.Service{
ObjectMeta: metav1.ObjectMeta{
@@ -220,20 +232,66 @@ func TestBuildKnativeServiceConfig(t *testing.T) {
},
},
},
+ "basic with limits": {
+ serviceCfg: KnativeService{
+ BaseService: &baseSvcWithResourceLimits,
+ ContainerPort: 8080,
+ MinReplicas: 1,
+ MaxReplicas: 2,
+ AutoscalingMetric: "concurrency",
+ AutoscalingTarget: "1",
+ IsClusterLocal: true,
+ QueueProxyResourcePercentage: 30,
+ },
+ expectedSpec: knservingv1.Service{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-svc",
+ Namespace: "test-namespace",
+ Labels: map[string]string{
+ "labelKey": "labelVal",
+ "networking.knative.dev/visibility": "cluster-local",
+ },
+ },
+ Spec: knservingv1.ServiceSpec{
+ ConfigurationSpec: knservingv1.ConfigurationSpec{
+ Template: knservingv1.RevisionTemplateSpec{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-svc-0",
+ Labels: map[string]string{
+ "labelKey": "labelVal",
+ },
+ Annotations: map[string]string{
+ "autoscaling.knative.dev/minScale": "1",
+ "autoscaling.knative.dev/maxScale": "2",
+ "autoscaling.knative.dev/metric": "concurrency",
+ "autoscaling.knative.dev/target": "1.00",
+ "autoscaling.knative.dev/class": "kpa.autoscaling.knative.dev",
+ "queue.sidecar.serving.knative.dev/resourcePercentage": "30",
+ },
+ },
+ Spec: knservingv1.RevisionSpec{
+ PodSpec: podSpecWithResourceLimits,
+ TimeoutSeconds: &timeout,
+ ContainerConcurrency: &defaultConcurrency,
+ },
+ },
+ },
+ RouteSpec: defaultRouteSpec,
+ },
+ },
+ },
// upi has no liveness probe in pod spec and user-container is using h2c
"upi": {
serviceCfg: KnativeService{
- BaseService: baseSvc,
- ContainerPort: 8080,
- MinReplicas: 1,
- MaxReplicas: 2,
- AutoscalingMetric: "concurrency",
- AutoscalingTarget: "1",
- IsClusterLocal: true,
- QueueProxyResourcePercentage: 30,
- UserContainerCPULimitRequestFactor: 1.5,
- UserContainerMemoryLimitRequestFactor: 1.5,
- Protocol: routerConfig.UPI,
+ BaseService: &baseSvc,
+ ContainerPort: 8080,
+ MinReplicas: 1,
+ MaxReplicas: 2,
+ AutoscalingMetric: "concurrency",
+ AutoscalingTarget: "1",
+ IsClusterLocal: true,
+ QueueProxyResourcePercentage: 30,
+ Protocol: routerConfig.UPI,
},
expectedSpec: knservingv1.Service{
ObjectMeta: metav1.ObjectMeta{
@@ -293,15 +351,13 @@ func TestBuildKnativeServiceConfig(t *testing.T) {
},
"annotations": {
serviceCfg: KnativeService{
- BaseService: baseSvc,
- ContainerPort: 8080,
- MinReplicas: 5,
- MaxReplicas: 6,
- AutoscalingMetric: "memory",
- AutoscalingTarget: "70",
- IsClusterLocal: false,
- UserContainerCPULimitRequestFactor: 1.5,
- UserContainerMemoryLimitRequestFactor: 1.5,
+ BaseService: &baseSvc,
+ ContainerPort: 8080,
+ MinReplicas: 5,
+ MaxReplicas: 6,
+ AutoscalingMetric: "memory",
+ AutoscalingTarget: "70",
+ IsClusterLocal: false,
},
expectedSpec: knservingv1.Service{
ObjectMeta: metav1.ObjectMeta{
@@ -340,7 +396,7 @@ func TestBuildKnativeServiceConfig(t *testing.T) {
},
"topology spread constraints": {
serviceCfg: KnativeService{
- BaseService: baseSvc,
+ BaseService: &baseSvc,
ContainerPort: 8080,
MinReplicas: 1,
MaxReplicas: 2,
@@ -384,11 +440,9 @@ func TestBuildKnativeServiceConfig(t *testing.T) {
},
},
},
- IsClusterLocal: true,
- QueueProxyResourcePercentage: 30,
- UserContainerCPULimitRequestFactor: 1.5,
- UserContainerMemoryLimitRequestFactor: 1.5,
- Protocol: routerConfig.UPI,
+ IsClusterLocal: true,
+ QueueProxyResourcePercentage: 30,
+ Protocol: routerConfig.UPI,
},
expectedSpec: knservingv1.Service{
ObjectMeta: metav1.ObjectMeta{
diff --git a/api/turing/cluster/kubernetes_service.go b/api/turing/cluster/kubernetes_service.go
index 91c281231..c3de0e954 100644
--- a/api/turing/cluster/kubernetes_service.go
+++ b/api/turing/cluster/kubernetes_service.go
@@ -7,15 +7,6 @@ import (
"k8s.io/apimachinery/pkg/util/intstr"
)
-const (
- // defaultCPULimitRequestFactor is the default multiplication factor applied to the CPU request,
- // to be set as the limit
- defaultCPULimitRequestFactor = 1.0
- // defaultMemoryLimitRequestFactor is the default multiplication factor applied to the memory request,
- // to be set as the limit
- defaultMemoryLimitRequestFactor = 2.0
-)
-
// KubernetesService defines the properties for Kubernetes services
type KubernetesService struct {
*BaseService
@@ -73,7 +64,7 @@ func (cfg *KubernetesService) buildStatefulSet(labels map[string]string) *appsv1
Args: cfg.Command,
Ports: cfg.buildContainerPorts(),
Env: cfg.Envs,
- Resources: cfg.buildResourceReqs(defaultCPULimitRequestFactor, defaultMemoryLimitRequestFactor),
+ Resources: cfg.buildResourceReqs(),
VolumeMounts: cfg.VolumeMounts,
LivenessProbe: cfg.buildContainerProbe(livenessProbeType, int(cfg.ProbePort)),
ReadinessProbe: cfg.buildContainerProbe(readinessProbeType, int(cfg.ProbePort)),
diff --git a/api/turing/cluster/kubernetes_service_test.go b/api/turing/cluster/kubernetes_service_test.go
index 65b0e1aa3..433f3c4f0 100644
--- a/api/turing/cluster/kubernetes_service_test.go
+++ b/api/turing/cluster/kubernetes_service_test.go
@@ -15,13 +15,17 @@ import (
func TestBuildKubernetesServiceConfig(t *testing.T) {
id := int64(999)
+ cpuLimit := resource.MustParse("1")
+ memoryLimit := resource.MustParse("2")
svcConf := KubernetesService{
BaseService: &BaseService{
Name: "test-svc-fluentd-logger",
Namespace: "namespace",
Image: "fluentdimage:1.0.0",
CPURequests: resource.MustParse("1"),
+ CPULimit: &cpuLimit,
MemoryRequests: resource.MustParse("1"),
+ MemoryLimit: &memoryLimit,
ProbePort: 8080,
LivenessHTTPGetPath: "/fluentd.pod.healthcheck?json=%7B%22log%22%3A+%22health+check%22%7D",
ReadinessHTTPGetPath: "/fluentd.pod.healthcheck?json=%7B%22log%22%3A+%22health+check%22%7D",
diff --git a/api/turing/cluster/models.go b/api/turing/cluster/models.go
index f046b3343..07f68b9ef 100644
--- a/api/turing/cluster/models.go
+++ b/api/turing/cluster/models.go
@@ -35,8 +35,10 @@ type BaseService struct {
Image string `json:"image"`
// Resources
- CPURequests resource.Quantity `json:"cpu_requests"`
- MemoryRequests resource.Quantity `json:"memory_requests"`
+ CPURequests resource.Quantity `json:"cpu_requests"`
+ CPULimit *resource.Quantity `json:"cpu_limit"`
+ MemoryRequests resource.Quantity `json:"memory_requests"`
+ MemoryLimit *resource.Quantity `json:"memory_limit"`
// Health Checks
ProbePort int32 `json:"probe_port"`
@@ -62,22 +64,21 @@ type BaseService struct {
InitContainers []Container `json:"init_containers"`
}
-func (cfg *BaseService) buildResourceReqs(
- UserContainerCPULimitRequestFactor float64,
- UserContainerMemoryLimitRequestFactor float64,
-) corev1.ResourceRequirements {
+// buildResourceReqs sets the necessary resource requests and limits based on the values stored in BaseService. These
+// values are expected to have already been computed (e.g. setting of default values) upstream by the clusterSvcBuilder
+// that creates the BaseService
+func (cfg *BaseService) buildResourceReqs() corev1.ResourceRequirements {
reqs := map[corev1.ResourceName]resource.Quantity{
corev1.ResourceCPU: cfg.CPURequests,
corev1.ResourceMemory: cfg.MemoryRequests,
}
- // Set resource limits to request * userContainerCPULimitRequestFactor or UserContainerMemoryLimitRequestFactor
limits := map[corev1.ResourceName]resource.Quantity{}
- if UserContainerCPULimitRequestFactor != 0 {
- limits[corev1.ResourceCPU] = ComputeResource(cfg.CPURequests, UserContainerCPULimitRequestFactor)
+ if cfg.CPULimit != nil {
+ limits[corev1.ResourceCPU] = *cfg.CPULimit
}
- if UserContainerMemoryLimitRequestFactor != 0 {
- limits[corev1.ResourceMemory] = ComputeResource(cfg.MemoryRequests, UserContainerMemoryLimitRequestFactor)
+ if cfg.MemoryLimit != nil {
+ limits[corev1.ResourceMemory] = *cfg.MemoryLimit
}
return corev1.ResourceRequirements{
diff --git a/api/turing/cluster/servicebuilder/fluentd.go b/api/turing/cluster/servicebuilder/fluentd.go
index a729cd086..afb1750c4 100644
--- a/api/turing/cluster/servicebuilder/fluentd.go
+++ b/api/turing/cluster/servicebuilder/fluentd.go
@@ -21,6 +21,13 @@ const (
fluentdPort = 24224
cacheVolumeMountPath = "/cache/"
cacheVolumeSize = "2Gi"
+
+ // defaultCPULimitRequestFactor is the default multiplication factor applied to the CPU request,
+ // to be set as the limit
+ defaultCPULimitRequestFactor = 1.0
+ // defaultMemoryLimitRequestFactor is the default multiplication factor applied to the memory request,
+ // to be set as the limit
+ defaultMemoryLimitRequestFactor = 2.0
)
// NewFluentdService builds a fluentd KubernetesService configuration
@@ -61,13 +68,20 @@ func (sb *clusterSvcBuilder) NewFluentdService(
// Overriding the security context so that fluentd is able to write logs
// to the persistent volume.
securityContextID := int64(999)
+
+ cpuRequest := resource.MustParse(fluentdCPURequest)
+ cpuLimit := cluster.ComputeResource(cpuRequest, defaultCPULimitRequestFactor)
+ memoryRequest := resource.MustParse(fluentdMemRequest)
+ memoryLimit := cluster.ComputeResource(memoryRequest, defaultMemoryLimitRequestFactor)
return &cluster.KubernetesService{
BaseService: &cluster.BaseService{
Name: name,
Namespace: project.Name,
Image: fluentdConfig.Image,
CPURequests: resource.MustParse(fluentdCPURequest),
+ CPULimit: &cpuLimit,
MemoryRequests: resource.MustParse(fluentdMemRequest),
+ MemoryLimit: &memoryLimit,
ProbePort: 9880,
LivenessHTTPGetPath: fluentdHealthCheckPath,
ReadinessHTTPGetPath: fluentdHealthCheckPath,
diff --git a/api/turing/cluster/servicebuilder/fluentd_test.go b/api/turing/cluster/servicebuilder/fluentd_test.go
index 1c86640ac..707ad0e9d 100644
--- a/api/turing/cluster/servicebuilder/fluentd_test.go
+++ b/api/turing/cluster/servicebuilder/fluentd_test.go
@@ -19,11 +19,9 @@ import (
)
func TestNewFluentdService(t *testing.T) {
- cpuLimit := resource.MustParse("400m")
- memoryLimit := resource.MustParse("512Mi")
sb := clusterSvcBuilder{
- MaxCPU: cpuLimit,
- MaxMemory: memoryLimit,
+ MaxCPU: resource.MustParse("400m"),
+ MaxMemory: resource.MustParse("512Mi"),
}
testDataBasePath := filepath.Join("..", "..", "testdata", "cluster", "servicebuilder")
@@ -42,6 +40,11 @@ func TestNewFluentdService(t *testing.T) {
Team: "test-team",
}
+ cpuRequest := resource.MustParse(fluentdCPURequest)
+ cpuLimits := cluster.ComputeResource(cpuRequest, defaultCPULimitRequestFactor)
+ memoryRequest := resource.MustParse(fluentdMemRequest)
+ memoryLimits := cluster.ComputeResource(memoryRequest, defaultMemoryLimitRequestFactor)
+
id := int64(999)
volSize, _ := resource.ParseQuantity(cacheVolumeSize)
expected := &cluster.KubernetesService{
@@ -49,8 +52,10 @@ func TestNewFluentdService(t *testing.T) {
Name: "test-svc-turing-fluentd-logger-1",
Namespace: project.Name,
Image: "fluentdimage:1.0.0",
- CPURequests: resource.MustParse(fluentdCPURequest),
- MemoryRequests: resource.MustParse(fluentdMemRequest),
+ CPURequests: cpuRequest,
+ CPULimit: &cpuLimits,
+ MemoryRequests: memoryRequest,
+ MemoryLimit: &memoryLimits,
LivenessHTTPGetPath: "/fluentd.pod.healthcheck?json=%7B%22log%22%3A+%22health+check%22%7D",
ReadinessHTTPGetPath: "/fluentd.pod.healthcheck?json=%7B%22log%22%3A+%22health+check%22%7D",
ProbeInitDelaySeconds: 10,
diff --git a/api/turing/cluster/servicebuilder/router.go b/api/turing/cluster/servicebuilder/router.go
index 86a083f33..87c3ce35a 100644
--- a/api/turing/cluster/servicebuilder/router.go
+++ b/api/turing/cluster/servicebuilder/router.go
@@ -104,9 +104,6 @@ func (sb *clusterSvcBuilder) NewRouterService(
routerDefaults *config.RouterDefaults,
sentryEnabled bool,
sentryDSN string,
- knativeQueueProxyResourcePercentage int,
- userContainerCPULimitRequestFactor float64,
- userContainerMemoryLimitRequestFactor float64,
initialScale *int,
) (*cluster.KnativeService, error) {
// Create service name
@@ -141,7 +138,9 @@ func (sb *clusterSvcBuilder) NewRouterService(
Namespace: namespace,
Image: routerVersion.Image,
CPURequests: routerVersion.ResourceRequest.CPURequest,
+ CPULimit: sb.getCPULimit(routerVersion.ResourceRequest),
MemoryRequests: routerVersion.ResourceRequest.MemoryRequest,
+ MemoryLimit: sb.getMemoryLimit(routerVersion.ResourceRequest),
LivenessHTTPGetPath: routerLivenessPath,
ReadinessHTTPGetPath: routerReadinessPath,
Envs: envs,
@@ -151,18 +150,16 @@ func (sb *clusterSvcBuilder) NewRouterService(
VolumeMounts: volumeMounts,
InitContainers: initContainers,
},
- IsClusterLocal: false,
- ContainerPort: routerPort,
- Protocol: routerVersion.Protocol,
- MinReplicas: routerVersion.ResourceRequest.MinReplica,
- MaxReplicas: routerVersion.ResourceRequest.MaxReplica,
- InitialScale: initialScale,
- AutoscalingMetric: string(routerVersion.AutoscalingPolicy.Metric),
- AutoscalingTarget: routerVersion.AutoscalingPolicy.Target,
- TopologySpreadConstraints: topologySpreadConstraints,
- QueueProxyResourcePercentage: knativeQueueProxyResourcePercentage,
- UserContainerCPULimitRequestFactor: userContainerCPULimitRequestFactor,
- UserContainerMemoryLimitRequestFactor: userContainerMemoryLimitRequestFactor,
+ IsClusterLocal: false,
+ ContainerPort: routerPort,
+ Protocol: routerVersion.Protocol,
+ MinReplicas: routerVersion.ResourceRequest.MinReplica,
+ MaxReplicas: routerVersion.ResourceRequest.MaxReplica,
+ InitialScale: initialScale,
+ AutoscalingMetric: string(routerVersion.AutoscalingPolicy.Metric),
+ AutoscalingTarget: routerVersion.AutoscalingPolicy.Target,
+ TopologySpreadConstraints: topologySpreadConstraints,
+ QueueProxyResourcePercentage: sb.knativeServiceConfig.QueueProxyResourcePercentage,
}
return sb.validateKnativeService(svc)
}
@@ -213,26 +210,29 @@ func (sb *clusterSvcBuilder) buildRouterEnvs(
sentryDSN string,
ver *models.RouterVersion,
) ([]corev1.EnvVar, error) {
+ envs := sb.getEnvVars(ver.ResourceRequest, nil)
+
// Add app name, router timeout, jaeger collector
- envs := []corev1.EnvVar{
- {Name: envAppName, Value: fmt.Sprintf("%s-%d.%s", ver.Router.Name, ver.Version, namespace)},
- {Name: envAppEnvironment, Value: environmentType},
- {Name: envRouterTimeout, Value: ver.Timeout},
- {Name: envJaegerEndpoint, Value: routerDefaults.JaegerCollectorEndpoint},
- {Name: envRouterConfigFile, Value: routerConfigMapMountPath + routerConfigFileName},
- {Name: envRouterProtocol, Value: string(ver.Protocol)},
- {Name: envSentryEnabled, Value: strconv.FormatBool(sentryEnabled)},
- {Name: envSentryDSN, Value: sentryDSN},
- }
+ envs = mergeEnvVars(envs,
+ []corev1.EnvVar{
+ {Name: envAppName, Value: fmt.Sprintf("%s-%d.%s", ver.Router.Name, ver.Version, namespace)},
+ {Name: envAppEnvironment, Value: environmentType},
+ {Name: envRouterTimeout, Value: ver.Timeout},
+ {Name: envJaegerEndpoint, Value: routerDefaults.JaegerCollectorEndpoint},
+ {Name: envRouterConfigFile, Value: routerConfigMapMountPath + routerConfigFileName},
+ {Name: envRouterProtocol, Value: string(ver.Protocol)},
+ {Name: envSentryEnabled, Value: strconv.FormatBool(sentryEnabled)},
+ {Name: envSentryDSN, Value: sentryDSN},
+ })
// Add enricher / ensembler related env vars, if enabled
if ver.Enricher != nil {
endpoint := buildPrePostProcessorEndpoint(ver, namespace,
ComponentTypes.Enricher, ver.Enricher.Endpoint)
- envs = append(envs, []corev1.EnvVar{
+ envs = mergeEnvVars(envs, []corev1.EnvVar{
{Name: envEnricherEndpoint, Value: endpoint},
{Name: envEnricherTimeout, Value: ver.Enricher.Timeout},
- }...)
+ })
}
if ver.HasDockerConfig() {
endpoint := buildPrePostProcessorEndpoint(
@@ -241,28 +241,28 @@ func (sb *clusterSvcBuilder) buildRouterEnvs(
ComponentTypes.Ensembler,
ver.Ensembler.DockerConfig.Endpoint,
)
- envs = append(envs, []corev1.EnvVar{
+ envs = mergeEnvVars(envs, []corev1.EnvVar{
{Name: envEnsemblerEndpoint, Value: endpoint},
{Name: envEnsemblerTimeout, Value: ver.Ensembler.DockerConfig.Timeout},
- }...)
+ })
}
// Add exp engine secret path as env var if service account key file path is specified for exp engine
if ver.ExperimentEngine != nil && ver.ExperimentEngine.ServiceAccountKeyFilePath != nil {
- envs = append(envs, []corev1.EnvVar{
+ envs = mergeEnvVars(envs, []corev1.EnvVar{
{Name: envExpGoogleApplicationCredentials, Value: secretMountPathExpEngine + secretKeyNameExpEngine},
- }...)
+ })
}
// Process Log config
logConfig := ver.LogConfig
- envs = append(envs, []corev1.EnvVar{
+ envs = mergeEnvVars(envs, []corev1.EnvVar{
{Name: envLogLevel, Value: string(logConfig.LogLevel)},
{Name: envCustomMetrics, Value: strconv.FormatBool(logConfig.CustomMetricsEnabled)},
{Name: envJaegerEnabled, Value: strconv.FormatBool(logConfig.JaegerEnabled)},
{Name: envResultLogger, Value: string(logConfig.ResultLoggerType)},
{Name: envFiberDebugLog, Value: strconv.FormatBool(logConfig.FiberDebugLogEnabled)},
- }...)
+ })
// Add BQ config
switch logConfig.ResultLoggerType {
@@ -275,29 +275,29 @@ func (sb *clusterSvcBuilder) buildRouterEnvs(
return envs, fmt.Errorf("Invalid BigQuery table name %s",
logConfig.BigQueryConfig.Table)
}
- envs = append(envs, []corev1.EnvVar{
+ envs = mergeEnvVars(envs, []corev1.EnvVar{
{Name: envGcpProject, Value: bqFQN[0]},
{Name: envBQDataset, Value: bqFQN[1]},
{Name: envBQTable, Value: bqFQN[2]},
{Name: envBQBatchLoad, Value: strconv.FormatBool(logConfig.BigQueryConfig.BatchLoad)},
{Name: envGoogleApplicationCredentials, Value: secretMountPathRouter + secretKeyNameRouter},
- }...)
+ })
if logConfig.BigQueryConfig.BatchLoad {
- envs = append(envs, []corev1.EnvVar{
+ envs = mergeEnvVars(envs, []corev1.EnvVar{
{Name: envFluentdHost, Value: buildFluentdHost(ver, namespace)},
{Name: envFluentdPort, Value: strconv.Itoa(fluentdPort)},
{Name: envFluentdTag, Value: routerDefaults.FluentdConfig.Tag},
- }...)
+ })
}
case models.KafkaLogger, models.UPILogger:
// UPILogger's kafka details are created in BuildRouterVersion so that information are persisted in DB
- envs = append(envs, []corev1.EnvVar{
+ envs = mergeEnvVars(envs, []corev1.EnvVar{
{Name: envKafkaBrokers, Value: logConfig.KafkaConfig.Brokers},
{Name: envKafkaTopic, Value: logConfig.KafkaConfig.Topic},
{Name: envKafkaSerializationFormat, Value: string(logConfig.KafkaConfig.SerializationFormat)},
{Name: envKafkaMaxMessageBytes, Value: strconv.Itoa(routerDefaults.KafkaConfig.MaxMessageBytes)},
{Name: envKafkaCompressionType, Value: routerDefaults.KafkaConfig.CompressionType},
- }...)
+ })
}
return envs, nil
diff --git a/api/turing/cluster/servicebuilder/router_test.go b/api/turing/cluster/servicebuilder/router_test.go
index 5ccd64b10..a4fd0d491 100644
--- a/api/turing/cluster/servicebuilder/router_test.go
+++ b/api/turing/cluster/servicebuilder/router_test.go
@@ -20,7 +20,22 @@ import (
)
func TestNewRouterService(t *testing.T) {
- sb := NewClusterServiceBuilder(resource.MustParse("2"), resource.MustParse("2Gi"), 30, testTopologySpreadConstraints)
+ userContainerMemoryLimitRequestFactor := 1.5
+ abcDefaultEnvVar := corev1.EnvVar{Name: "ABC", Value: "true"}
+ defDefaultEnvVar := corev1.EnvVar{Name: "DEF", Value: "false"}
+
+ sb := NewClusterServiceBuilder(
+ resource.MustParse("2"),
+ resource.MustParse("2Gi"),
+ 30,
+ testTopologySpreadConstraints,
+ &config.KnativeServiceDefaults{
+ QueueProxyResourcePercentage: 20,
+ UserContainerCPULimitRequestFactor: 0,
+ UserContainerMemoryLimitRequestFactor: userContainerMemoryLimitRequestFactor,
+ DefaultEnvVarsWithoutCPULimits: []corev1.EnvVar{abcDefaultEnvVar, defDefaultEnvVar},
+ },
+ )
testDataBasePath := filepath.Join("..", "..", "testdata", "cluster", "servicebuilder")
enrEndpoint := "http://test-svc-turing-enricher-1.test-project.svc.cluster.local/echo?delay=10ms"
ensEndpoint := "http://test-svc-turing-ensembler-1.test-project.svc.cluster.local/echo?delay=20ms"
@@ -69,6 +84,9 @@ func TestNewRouterService(t *testing.T) {
cfgmapNoDefaultRoute, err := tu.ReadFile(filepath.Join(testDataBasePath, "router_configmap_no_default_route.yml"))
require.NoError(t, err)
+ memoryRequest := resource.MustParse("512Mi")
+ memoryLimit := cluster.ComputeResource(memoryRequest, userContainerMemoryLimitRequestFactor)
+
testInitialScale := 3
// Define tests
@@ -83,7 +101,8 @@ func TestNewRouterService(t *testing.T) {
Namespace: "test-project",
Image: "asia.gcr.io/gcp-project-id/turing-router:latest",
CPURequests: resource.MustParse("400m"),
- MemoryRequests: resource.MustParse("512Mi"),
+ MemoryRequests: memoryRequest,
+ MemoryLimit: &memoryLimit,
LivenessHTTPGetPath: "/v1/internal/live",
ReadinessHTTPGetPath: "/v1/internal/ready",
ConfigMap: &cluster.ConfigMap{
@@ -99,6 +118,8 @@ func TestNewRouterService(t *testing.T) {
},
},
Envs: []corev1.EnvVar{
+ abcDefaultEnvVar,
+ defDefaultEnvVar,
{Name: "APP_NAME", Value: "test-svc-1.test-project"},
{Name: "APP_ENVIRONMENT", Value: "test-env"},
{Name: "ROUTER_TIMEOUT", Value: "5s"},
@@ -162,17 +183,15 @@ func TestNewRouterService(t *testing.T) {
},
},
},
- ContainerPort: 8080,
- Protocol: routerConfig.HTTP,
- MinReplicas: 2,
- MaxReplicas: 4,
- InitialScale: &testInitialScale,
- AutoscalingMetric: "concurrency",
- AutoscalingTarget: "1",
- TopologySpreadConstraints: testTopologySpreadConstraints,
- QueueProxyResourcePercentage: 20,
- UserContainerCPULimitRequestFactor: 0,
- UserContainerMemoryLimitRequestFactor: 1.5,
+ ContainerPort: 8080,
+ Protocol: routerConfig.HTTP,
+ MinReplicas: 2,
+ MaxReplicas: 4,
+ InitialScale: &testInitialScale,
+ AutoscalingMetric: "concurrency",
+ AutoscalingTarget: "1",
+ TopologySpreadConstraints: testTopologySpreadConstraints,
+ QueueProxyResourcePercentage: 20,
},
},
"success | basic upi": {
@@ -185,7 +204,8 @@ func TestNewRouterService(t *testing.T) {
Namespace: "test-project",
Image: "asia.gcr.io/gcp-project-id/turing-router:latest",
CPURequests: resource.MustParse("400m"),
- MemoryRequests: resource.MustParse("512Mi"),
+ MemoryRequests: memoryRequest,
+ MemoryLimit: &memoryLimit,
LivenessHTTPGetPath: "/v1/internal/live",
ReadinessHTTPGetPath: "/v1/internal/ready",
ConfigMap: &cluster.ConfigMap{
@@ -201,6 +221,8 @@ func TestNewRouterService(t *testing.T) {
},
},
Envs: []corev1.EnvVar{
+ abcDefaultEnvVar,
+ defDefaultEnvVar,
{Name: "APP_NAME", Value: "test-svc-1.test-project"},
{Name: "APP_ENVIRONMENT", Value: "test-env"},
{Name: "ROUTER_TIMEOUT", Value: "5s"},
@@ -264,17 +286,15 @@ func TestNewRouterService(t *testing.T) {
},
},
},
- ContainerPort: 8080,
- Protocol: routerConfig.UPI,
- MinReplicas: 2,
- MaxReplicas: 4,
- InitialScale: &testInitialScale,
- AutoscalingMetric: "concurrency",
- AutoscalingTarget: "1",
- TopologySpreadConstraints: testTopologySpreadConstraints,
- QueueProxyResourcePercentage: 20,
- UserContainerCPULimitRequestFactor: 0,
- UserContainerMemoryLimitRequestFactor: 1.5,
+ ContainerPort: 8080,
+ Protocol: routerConfig.UPI,
+ MinReplicas: 2,
+ MaxReplicas: 4,
+ InitialScale: &testInitialScale,
+ AutoscalingMetric: "concurrency",
+ AutoscalingTarget: "1",
+ TopologySpreadConstraints: testTopologySpreadConstraints,
+ QueueProxyResourcePercentage: 20,
},
},
"success | all components": {
@@ -286,7 +306,8 @@ func TestNewRouterService(t *testing.T) {
Namespace: "test-project",
Image: "asia.gcr.io/gcp-project-id/turing-router:latest",
CPURequests: resource.MustParse("400m"),
- MemoryRequests: resource.MustParse("512Mi"),
+ MemoryRequests: memoryRequest,
+ MemoryLimit: &memoryLimit,
LivenessHTTPGetPath: "/v1/internal/live",
ReadinessHTTPGetPath: "/v1/internal/ready",
ConfigMap: &cluster.ConfigMap{
@@ -302,6 +323,8 @@ func TestNewRouterService(t *testing.T) {
},
},
Envs: []corev1.EnvVar{
+ abcDefaultEnvVar,
+ defDefaultEnvVar,
{Name: "APP_NAME", Value: "test-svc-1.test-project"},
{Name: "APP_ENVIRONMENT", Value: "test-env"},
{Name: "ROUTER_TIMEOUT", Value: "5s"},
@@ -373,16 +396,14 @@ func TestNewRouterService(t *testing.T) {
},
},
},
- ContainerPort: 8080,
- Protocol: routerConfig.HTTP,
- MinReplicas: 2,
- MaxReplicas: 4,
- AutoscalingMetric: "concurrency",
- AutoscalingTarget: "1",
- TopologySpreadConstraints: testTopologySpreadConstraints,
- QueueProxyResourcePercentage: 20,
- UserContainerCPULimitRequestFactor: 0,
- UserContainerMemoryLimitRequestFactor: 1.5,
+ ContainerPort: 8080,
+ Protocol: routerConfig.HTTP,
+ MinReplicas: 2,
+ MaxReplicas: 4,
+ AutoscalingMetric: "concurrency",
+ AutoscalingTarget: "1",
+ TopologySpreadConstraints: testTopologySpreadConstraints,
+ QueueProxyResourcePercentage: 20,
},
},
"success | standard ensembler with experiment mappings": {
@@ -394,7 +415,8 @@ func TestNewRouterService(t *testing.T) {
Namespace: "test-project",
Image: "asia.gcr.io/gcp-project-id/turing-router:latest",
CPURequests: resource.MustParse("400m"),
- MemoryRequests: resource.MustParse("512Mi"),
+ MemoryRequests: memoryRequest,
+ MemoryLimit: &memoryLimit,
LivenessHTTPGetPath: "/v1/internal/live",
ReadinessHTTPGetPath: "/v1/internal/ready",
ConfigMap: &cluster.ConfigMap{
@@ -410,6 +432,8 @@ func TestNewRouterService(t *testing.T) {
},
},
Envs: []corev1.EnvVar{
+ abcDefaultEnvVar,
+ defDefaultEnvVar,
{Name: "APP_NAME", Value: "test-svc-1.test-project"},
{Name: "APP_ENVIRONMENT", Value: "test-env"},
{Name: "ROUTER_TIMEOUT", Value: "5s"},
@@ -473,16 +497,14 @@ func TestNewRouterService(t *testing.T) {
},
},
},
- ContainerPort: 8080,
- Protocol: routerConfig.HTTP,
- MinReplicas: 2,
- MaxReplicas: 4,
- AutoscalingMetric: "rps",
- AutoscalingTarget: "100",
- TopologySpreadConstraints: testTopologySpreadConstraints,
- QueueProxyResourcePercentage: 20,
- UserContainerCPULimitRequestFactor: 0,
- UserContainerMemoryLimitRequestFactor: 1.5,
+ ContainerPort: 8080,
+ Protocol: routerConfig.HTTP,
+ MinReplicas: 2,
+ MaxReplicas: 4,
+ AutoscalingMetric: "rps",
+ AutoscalingTarget: "100",
+ TopologySpreadConstraints: testTopologySpreadConstraints,
+ QueueProxyResourcePercentage: 20,
},
},
"success | standard ensembler with route name path": {
@@ -494,7 +516,8 @@ func TestNewRouterService(t *testing.T) {
Namespace: "test-project",
Image: "asia.gcr.io/gcp-project-id/turing-router:latest",
CPURequests: resource.MustParse("400m"),
- MemoryRequests: resource.MustParse("512Mi"),
+ MemoryRequests: memoryRequest,
+ MemoryLimit: &memoryLimit,
LivenessHTTPGetPath: "/v1/internal/live",
ReadinessHTTPGetPath: "/v1/internal/ready",
ConfigMap: &cluster.ConfigMap{
@@ -510,6 +533,8 @@ func TestNewRouterService(t *testing.T) {
},
},
Envs: []corev1.EnvVar{
+ abcDefaultEnvVar,
+ defDefaultEnvVar,
{Name: "APP_NAME", Value: "test-svc-1.test-project"},
{Name: "APP_ENVIRONMENT", Value: "test-env"},
{Name: "ROUTER_TIMEOUT", Value: "5s"},
@@ -573,16 +598,14 @@ func TestNewRouterService(t *testing.T) {
},
},
},
- ContainerPort: 8080,
- Protocol: routerConfig.HTTP,
- MinReplicas: 2,
- MaxReplicas: 4,
- AutoscalingMetric: "rps",
- AutoscalingTarget: "100",
- TopologySpreadConstraints: testTopologySpreadConstraints,
- QueueProxyResourcePercentage: 20,
- UserContainerCPULimitRequestFactor: 0,
- UserContainerMemoryLimitRequestFactor: 1.5,
+ ContainerPort: 8080,
+ Protocol: routerConfig.HTTP,
+ MinReplicas: 2,
+ MaxReplicas: 4,
+ AutoscalingMetric: "rps",
+ AutoscalingTarget: "100",
+ TopologySpreadConstraints: testTopologySpreadConstraints,
+ QueueProxyResourcePercentage: 20,
},
},
"success | standard ensembler lazy routing": {
@@ -594,7 +617,8 @@ func TestNewRouterService(t *testing.T) {
Namespace: "test-project",
Image: "asia.gcr.io/gcp-project-id/turing-router:latest",
CPURequests: resource.MustParse("400m"),
- MemoryRequests: resource.MustParse("512Mi"),
+ MemoryRequests: memoryRequest,
+ MemoryLimit: &memoryLimit,
LivenessHTTPGetPath: "/v1/internal/live",
ReadinessHTTPGetPath: "/v1/internal/ready",
ConfigMap: &cluster.ConfigMap{
@@ -610,6 +634,8 @@ func TestNewRouterService(t *testing.T) {
},
},
Envs: []corev1.EnvVar{
+ abcDefaultEnvVar,
+ defDefaultEnvVar,
{Name: "APP_NAME", Value: "test-svc-1.test-project"},
{Name: "APP_ENVIRONMENT", Value: "test-env"},
{Name: "ROUTER_TIMEOUT", Value: "5s"},
@@ -673,16 +699,14 @@ func TestNewRouterService(t *testing.T) {
},
},
},
- ContainerPort: 8080,
- Protocol: routerConfig.HTTP,
- MinReplicas: 2,
- MaxReplicas: 4,
- AutoscalingMetric: "rps",
- AutoscalingTarget: "100",
- TopologySpreadConstraints: testTopologySpreadConstraints,
- QueueProxyResourcePercentage: 20,
- UserContainerCPULimitRequestFactor: 0,
- UserContainerMemoryLimitRequestFactor: 1.5,
+ ContainerPort: 8080,
+ Protocol: routerConfig.HTTP,
+ MinReplicas: 2,
+ MaxReplicas: 4,
+ AutoscalingMetric: "rps",
+ AutoscalingTarget: "100",
+ TopologySpreadConstraints: testTopologySpreadConstraints,
+ QueueProxyResourcePercentage: 20,
},
},
"success | traffic-splitting": {
@@ -694,7 +718,8 @@ func TestNewRouterService(t *testing.T) {
Namespace: "test-project",
Image: "asia.gcr.io/gcp-project-id/turing-router:latest",
CPURequests: resource.MustParse("400m"),
- MemoryRequests: resource.MustParse("512Mi"),
+ MemoryRequests: memoryRequest,
+ MemoryLimit: &memoryLimit,
LivenessHTTPGetPath: "/v1/internal/live",
ReadinessHTTPGetPath: "/v1/internal/ready",
ConfigMap: &cluster.ConfigMap{
@@ -710,6 +735,8 @@ func TestNewRouterService(t *testing.T) {
},
},
Envs: []corev1.EnvVar{
+ abcDefaultEnvVar,
+ defDefaultEnvVar,
{Name: "APP_NAME", Value: "test-svc-1.test-project"},
{Name: "APP_ENVIRONMENT", Value: "test-env"},
{Name: "ROUTER_TIMEOUT", Value: "5s"},
@@ -773,16 +800,14 @@ func TestNewRouterService(t *testing.T) {
},
},
},
- ContainerPort: 8080,
- Protocol: routerConfig.HTTP,
- MinReplicas: 2,
- MaxReplicas: 4,
- AutoscalingMetric: "concurrency",
- AutoscalingTarget: "1",
- TopologySpreadConstraints: testTopologySpreadConstraints,
- QueueProxyResourcePercentage: 20,
- UserContainerCPULimitRequestFactor: 0,
- UserContainerMemoryLimitRequestFactor: 1.5,
+ ContainerPort: 8080,
+ Protocol: routerConfig.HTTP,
+ MinReplicas: 2,
+ MaxReplicas: 4,
+ AutoscalingMetric: "concurrency",
+ AutoscalingTarget: "1",
+ TopologySpreadConstraints: testTopologySpreadConstraints,
+ QueueProxyResourcePercentage: 20,
},
},
"success | experiment engine": {
@@ -794,7 +819,8 @@ func TestNewRouterService(t *testing.T) {
Namespace: "test-project",
Image: "ghcr.io/caraml-dev/turing/turing-router:latest",
CPURequests: resource.MustParse("400m"),
- MemoryRequests: resource.MustParse("512Mi"),
+ MemoryRequests: memoryRequest,
+ MemoryLimit: &memoryLimit,
LivenessHTTPGetPath: "/v1/internal/live",
ReadinessHTTPGetPath: "/v1/internal/ready",
ConfigMap: &cluster.ConfigMap{
@@ -810,6 +836,8 @@ func TestNewRouterService(t *testing.T) {
},
},
Envs: []corev1.EnvVar{
+ abcDefaultEnvVar,
+ defDefaultEnvVar,
{Name: "APP_NAME", Value: "router-with-exp-engine-1.test-project"},
{Name: "APP_ENVIRONMENT", Value: "test-env"},
{Name: "ROUTER_TIMEOUT", Value: "5s"},
@@ -902,16 +930,14 @@ func TestNewRouterService(t *testing.T) {
},
},
},
- ContainerPort: 8080,
- Protocol: routerConfig.HTTP,
- MinReplicas: 2,
- MaxReplicas: 4,
- AutoscalingMetric: "rps",
- AutoscalingTarget: "100",
- TopologySpreadConstraints: testTopologySpreadConstraints,
- QueueProxyResourcePercentage: 20,
- UserContainerCPULimitRequestFactor: 0,
- UserContainerMemoryLimitRequestFactor: 1.5,
+ ContainerPort: 8080,
+ Protocol: routerConfig.HTTP,
+ MinReplicas: 2,
+ MaxReplicas: 4,
+ AutoscalingMetric: "rps",
+ AutoscalingTarget: "100",
+ TopologySpreadConstraints: testTopologySpreadConstraints,
+ QueueProxyResourcePercentage: 20,
},
},
"success | no default route": {
@@ -923,7 +949,8 @@ func TestNewRouterService(t *testing.T) {
Namespace: "test-project",
Image: "asia.gcr.io/gcp-project-id/turing-router:latest",
CPURequests: resource.MustParse("400m"),
- MemoryRequests: resource.MustParse("512Mi"),
+ MemoryRequests: memoryRequest,
+ MemoryLimit: &memoryLimit,
LivenessHTTPGetPath: "/v1/internal/live",
ReadinessHTTPGetPath: "/v1/internal/ready",
ConfigMap: &cluster.ConfigMap{
@@ -939,6 +966,8 @@ func TestNewRouterService(t *testing.T) {
},
},
Envs: []corev1.EnvVar{
+ abcDefaultEnvVar,
+ defDefaultEnvVar,
{Name: "APP_NAME", Value: "test-svc-1.test-project"},
{Name: "APP_ENVIRONMENT", Value: "test-env"},
{Name: "ROUTER_TIMEOUT", Value: "5s"},
@@ -979,16 +1008,14 @@ func TestNewRouterService(t *testing.T) {
},
},
},
- ContainerPort: 8080,
- Protocol: routerConfig.HTTP,
- MinReplicas: 2,
- MaxReplicas: 4,
- AutoscalingMetric: "memory",
- AutoscalingTarget: "90",
- TopologySpreadConstraints: testTopologySpreadConstraints,
- QueueProxyResourcePercentage: 20,
- UserContainerCPULimitRequestFactor: 0,
- UserContainerMemoryLimitRequestFactor: 1.5,
+ ContainerPort: 8080,
+ Protocol: routerConfig.HTTP,
+ MinReplicas: 2,
+ MaxReplicas: 4,
+ AutoscalingMetric: "memory",
+ AutoscalingTarget: "90",
+ TopologySpreadConstraints: testTopologySpreadConstraints,
+ QueueProxyResourcePercentage: 20,
},
},
"failure missing bigquery": {
@@ -1025,7 +1052,7 @@ func TestNewRouterService(t *testing.T) {
},
true,
"sentry-dsn",
- 20, 0, 1.5, data.initialScale,
+ data.initialScale,
)
if data.err == "" {
@@ -1040,7 +1067,17 @@ func TestNewRouterService(t *testing.T) {
func TestNewRouterEndpoint(t *testing.T) {
// Get router version
- sb := NewClusterServiceBuilder(resource.MustParse("2"), resource.MustParse("2Gi"), 30, testTopologySpreadConstraints)
+ sb := NewClusterServiceBuilder(
+ resource.MustParse("2"),
+ resource.MustParse("2Gi"),
+ 30,
+ testTopologySpreadConstraints,
+ &config.KnativeServiceDefaults{
+ QueueProxyResourcePercentage: 10,
+ UserContainerCPULimitRequestFactor: 0,
+ UserContainerMemoryLimitRequestFactor: 1.5,
+ },
+ )
testDataBasePath := filepath.Join("..", "..", "testdata", "cluster", "servicebuilder")
fileBytes, err := tu.ReadFile(filepath.Join(testDataBasePath, "router_version_success.json"))
require.NoError(t, err)
diff --git a/api/turing/cluster/servicebuilder/service_builder.go b/api/turing/cluster/servicebuilder/service_builder.go
index 2a6e31a69..4dd50fdf9 100644
--- a/api/turing/cluster/servicebuilder/service_builder.go
+++ b/api/turing/cluster/servicebuilder/service_builder.go
@@ -66,18 +66,12 @@ type ClusterServiceBuilder interface {
ver *models.RouterVersion,
project *mlp.Project,
secretName string,
- knativeQueueProxyResourcePercentage int,
- userContainerCPULimitRequestFactor float64,
- userContainerMemoryLimitRequestFactor float64,
initialScale *int,
) (*cluster.KnativeService, error)
NewEnsemblerService(
ver *models.RouterVersion,
project *mlp.Project,
secretName string,
- knativeQueueProxyResourcePercentage int,
- userContainerCPULimitRequestFactor float64,
- userContainerMemoryLimitRequestFactor float64,
initialScale *int,
) (*cluster.KnativeService, error)
NewRouterService(
@@ -89,9 +83,6 @@ type ClusterServiceBuilder interface {
routerDefaults *config.RouterDefaults,
sentryEnabled bool,
sentryDSN string,
- knativeQueueProxyResourcePercentage int,
- userContainerCPULimitRequestFactor float64,
- userContainerMemoryLimitRequestFactor float64,
initialScale *int,
) (*cluster.KnativeService, error)
NewFluentdService(
@@ -128,6 +119,9 @@ type clusterSvcBuilder struct {
MaxMemory resource.Quantity
MaxAllowedReplica int
TopologySpreadConstraints []corev1.TopologySpreadConstraint
+
+ // Knative service configs
+ knativeServiceConfig *config.KnativeServiceDefaults
}
// NewClusterServiceBuilder creates a new service builder with the supplied configs for defaults
@@ -136,12 +130,14 @@ func NewClusterServiceBuilder(
memoryLimit resource.Quantity,
maxAllowedReplica int,
topologySpreadConstraints []corev1.TopologySpreadConstraint,
+ knativeServiceConfig *config.KnativeServiceDefaults,
) ClusterServiceBuilder {
return &clusterSvcBuilder{
MaxCPU: cpuLimit,
MaxMemory: memoryLimit,
MaxAllowedReplica: maxAllowedReplica,
TopologySpreadConstraints: topologySpreadConstraints,
+ knativeServiceConfig: knativeServiceConfig,
}
}
@@ -151,9 +147,6 @@ func (sb *clusterSvcBuilder) NewEnricherService(
routerVersion *models.RouterVersion,
project *mlp.Project,
secretName string,
- knativeQueueProxyResourcePercentage int,
- userContainerCPULimitRequestFactor float64,
- userContainerMemoryLimitRequestFactor float64,
initialScale *int,
) (*cluster.KnativeService, error) {
// Get the enricher reference
@@ -213,23 +206,23 @@ func (sb *clusterSvcBuilder) NewEnricherService(
Namespace: namespace,
Image: enricher.Image,
CPURequests: enricher.ResourceRequest.CPURequest,
+ CPULimit: sb.getCPULimit(enricher.ResourceRequest),
MemoryRequests: enricher.ResourceRequest.MemoryRequest,
- Envs: enricher.Env.ToKubernetesEnvVars(),
+ MemoryLimit: sb.getMemoryLimit(enricher.ResourceRequest),
+ Envs: sb.getEnvVars(enricher.ResourceRequest, &enricher.Env),
Labels: buildLabels(project, routerVersion.Router),
Volumes: volumes,
VolumeMounts: volumeMounts,
},
- IsClusterLocal: true,
- ContainerPort: int32(enricher.Port),
- MinReplicas: enricher.ResourceRequest.MinReplica,
- MaxReplicas: enricher.ResourceRequest.MaxReplica,
- InitialScale: initialScale,
- AutoscalingMetric: string(enricher.AutoscalingPolicy.Metric),
- AutoscalingTarget: enricher.AutoscalingPolicy.Target,
- TopologySpreadConstraints: topologySpreadConstraints,
- QueueProxyResourcePercentage: knativeQueueProxyResourcePercentage,
- UserContainerCPULimitRequestFactor: userContainerCPULimitRequestFactor,
- UserContainerMemoryLimitRequestFactor: userContainerMemoryLimitRequestFactor,
+ IsClusterLocal: true,
+ ContainerPort: int32(enricher.Port),
+ MinReplicas: enricher.ResourceRequest.MinReplica,
+ MaxReplicas: enricher.ResourceRequest.MaxReplica,
+ InitialScale: initialScale,
+ AutoscalingMetric: string(enricher.AutoscalingPolicy.Metric),
+ AutoscalingTarget: enricher.AutoscalingPolicy.Target,
+ TopologySpreadConstraints: topologySpreadConstraints,
+ QueueProxyResourcePercentage: sb.knativeServiceConfig.QueueProxyResourcePercentage,
})
}
@@ -239,9 +232,6 @@ func (sb *clusterSvcBuilder) NewEnsemblerService(
routerVersion *models.RouterVersion,
project *mlp.Project,
secretName string,
- knativeQueueProxyResourcePercentage int,
- userContainerCPULimitRequestFactor float64,
- userContainerMemoryLimitRequestFactor float64,
initialScale *int,
) (*cluster.KnativeService, error) {
// Get the ensembler reference
@@ -302,23 +292,23 @@ func (sb *clusterSvcBuilder) NewEnsemblerService(
Namespace: namespace,
Image: docker.Image,
CPURequests: docker.ResourceRequest.CPURequest,
+ CPULimit: sb.getCPULimit(docker.ResourceRequest),
MemoryRequests: docker.ResourceRequest.MemoryRequest,
- Envs: docker.Env.ToKubernetesEnvVars(),
+ MemoryLimit: sb.getMemoryLimit(docker.ResourceRequest),
+ Envs: sb.getEnvVars(docker.ResourceRequest, &docker.Env),
Labels: buildLabels(project, routerVersion.Router),
Volumes: volumes,
VolumeMounts: volumeMounts,
},
- IsClusterLocal: true,
- ContainerPort: int32(docker.Port),
- MinReplicas: docker.ResourceRequest.MinReplica,
- MaxReplicas: docker.ResourceRequest.MaxReplica,
- InitialScale: initialScale,
- AutoscalingMetric: string(docker.AutoscalingPolicy.Metric),
- AutoscalingTarget: docker.AutoscalingPolicy.Target,
- TopologySpreadConstraints: topologySpreadConstraints,
- QueueProxyResourcePercentage: knativeQueueProxyResourcePercentage,
- UserContainerCPULimitRequestFactor: userContainerCPULimitRequestFactor,
- UserContainerMemoryLimitRequestFactor: userContainerMemoryLimitRequestFactor,
+ IsClusterLocal: true,
+ ContainerPort: int32(docker.Port),
+ MinReplicas: docker.ResourceRequest.MinReplica,
+ MaxReplicas: docker.ResourceRequest.MaxReplica,
+ InitialScale: initialScale,
+ AutoscalingMetric: string(docker.AutoscalingPolicy.Metric),
+ AutoscalingTarget: docker.AutoscalingPolicy.Target,
+ TopologySpreadConstraints: topologySpreadConstraints,
+ QueueProxyResourcePercentage: sb.knativeServiceConfig.QueueProxyResourcePercentage,
})
}
@@ -414,6 +404,45 @@ func (sb *clusterSvcBuilder) getTopologySpreadConstraints() ([]corev1.TopologySp
return topologySpreadConstraints, nil
}
+func (sb *clusterSvcBuilder) getCPULimit(resourceRequest *models.ResourceRequest) *resource.Quantity {
+ if resourceRequest == nil {
+ return nil
+ }
+
+ if resourceRequest.CPULimit != nil && !resourceRequest.CPULimit.IsZero() {
+ return resourceRequest.CPULimit
+ }
+
+ if sb.knativeServiceConfig.UserContainerCPULimitRequestFactor == 0 {
+ return nil
+ }
+
+ cpuLimit := cluster.ComputeResource(resourceRequest.CPURequest,
+ sb.knativeServiceConfig.UserContainerCPULimitRequestFactor)
+ return &cpuLimit
+}
+
+func (sb *clusterSvcBuilder) getMemoryLimit(resourceRequest *models.ResourceRequest) *resource.Quantity {
+ if resourceRequest != nil && sb.knativeServiceConfig.UserContainerMemoryLimitRequestFactor != 0 {
+ memoryLimit := cluster.ComputeResource(resourceRequest.MemoryRequest,
+ sb.knativeServiceConfig.UserContainerMemoryLimitRequestFactor)
+ return &memoryLimit
+ }
+ return nil
+}
+
+func (sb *clusterSvcBuilder) getEnvVars(resourceRequest *models.ResourceRequest,
+ userEnvVars *models.EnvVars) (newEnvVars []corev1.EnvVar) {
+ if resourceRequest != nil && (resourceRequest.CPULimit == nil || resourceRequest.CPULimit.IsZero()) &&
+ sb.knativeServiceConfig.UserContainerCPULimitRequestFactor == 0 {
+ newEnvVars = mergeEnvVars(newEnvVars, sb.knativeServiceConfig.DefaultEnvVarsWithoutCPULimits)
+ }
+ if userEnvVars != nil {
+ newEnvVars = mergeEnvVars(newEnvVars, userEnvVars.ToKubernetesEnvVars())
+ }
+ return
+}
+
func GetComponentName(routerVersion *models.RouterVersion, componentType string) string {
return fmt.Sprintf("%s-turing-%s-%d", routerVersion.Router.Name, componentType, routerVersion.Version)
}
@@ -434,3 +463,23 @@ func buildLabels(
}
return labeller.BuildLabels(r)
}
+
+// mergeEnvVars merges multiple sets of environment variables and return the merging result.
+// All the EnvVars passed as arguments will be not mutated.
+// EnvVars to the right have higher precedence.
+func mergeEnvVars(left []corev1.EnvVar, rightEnvVars ...[]corev1.EnvVar) []corev1.EnvVar {
+ for _, right := range rightEnvVars {
+ envIndexMap := make(map[string]int, len(left)+len(right))
+ for index, ev := range left {
+ envIndexMap[ev.Name] = index
+ }
+ for _, add := range right {
+ if index, exist := envIndexMap[add.Name]; exist {
+ left[index].Value = add.Value
+ } else {
+ left = append(left, add)
+ }
+ }
+ }
+ return left
+}
diff --git a/api/turing/cluster/servicebuilder/service_builder_test.go b/api/turing/cluster/servicebuilder/service_builder_test.go
index a1c595a9a..7f40cf3ce 100644
--- a/api/turing/cluster/servicebuilder/service_builder_test.go
+++ b/api/turing/cluster/servicebuilder/service_builder_test.go
@@ -6,6 +6,7 @@ import (
"testing"
mlp "github.com/caraml-dev/mlp/api/client"
+ "github.com/caraml-dev/turing/api/turing/config"
"github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
@@ -42,7 +43,27 @@ type testSuiteNewService struct {
}
func TestNewEnricherService(t *testing.T) {
- sb := NewClusterServiceBuilder(resource.MustParse("2"), resource.MustParse("2Gi"), 30, testTopologySpreadConstraints)
+ userContainerCPULimitRequestFactor := 2.0
+ userContainerMemoryLimitRequestFactor := 1.5
+
+ sb := NewClusterServiceBuilder(
+ resource.MustParse("2"),
+ resource.MustParse("2Gi"),
+ 30,
+ testTopologySpreadConstraints,
+ &config.KnativeServiceDefaults{
+ QueueProxyResourcePercentage: 10,
+ UserContainerCPULimitRequestFactor: userContainerCPULimitRequestFactor,
+ UserContainerMemoryLimitRequestFactor: userContainerMemoryLimitRequestFactor,
+ },
+ )
+
+ cpuRequest := resource.MustParse("400m")
+ cpuLimit := cluster.ComputeResource(cpuRequest, userContainerCPULimitRequestFactor)
+
+ memoryRequest := resource.MustParse("256Mi")
+ memoryLimit := cluster.ComputeResource(memoryRequest, userContainerMemoryLimitRequestFactor)
+
testDataBasePath := filepath.Join("..", "..", "testdata", "cluster", "servicebuilder")
testInitialScale := 5
@@ -55,8 +76,10 @@ func TestNewEnricherService(t *testing.T) {
Name: "test-svc-turing-enricher-1",
Namespace: "test-project",
Image: "asia.gcr.io/gcp-project-id/echo:1.0.2",
- CPURequests: resource.MustParse("400m"),
- MemoryRequests: resource.MustParse("256Mi"),
+ CPURequests: cpuRequest,
+ CPULimit: &cpuLimit,
+ MemoryRequests: memoryRequest,
+ MemoryLimit: &memoryLimit,
Envs: []corev1.EnvVar{
{Name: "TEST_ENV", Value: "enricher"},
{Name: "GOOGLE_APPLICATION_CREDENTIALS", Value: "/var/secret/enricher-service-account.json"},
@@ -80,17 +103,15 @@ func TestNewEnricherService(t *testing.T) {
},
VolumeMounts: []corev1.VolumeMount{{Name: secretVolume, MountPath: secretMountPath}},
},
- IsClusterLocal: true,
- ContainerPort: 8080,
- MinReplicas: 1,
- MaxReplicas: 2,
- InitialScale: &testInitialScale,
- AutoscalingMetric: "concurrency",
- AutoscalingTarget: "1",
- TopologySpreadConstraints: testTopologySpreadConstraints,
- QueueProxyResourcePercentage: 10,
- UserContainerCPULimitRequestFactor: 0,
- UserContainerMemoryLimitRequestFactor: 1.5,
+ IsClusterLocal: true,
+ ContainerPort: 8080,
+ MinReplicas: 1,
+ MaxReplicas: 2,
+ InitialScale: &testInitialScale,
+ AutoscalingMetric: "concurrency",
+ AutoscalingTarget: "1",
+ TopologySpreadConstraints: testTopologySpreadConstraints,
+ QueueProxyResourcePercentage: 10,
},
},
"failure": {
@@ -111,7 +132,7 @@ func TestNewEnricherService(t *testing.T) {
Team: "test-team",
Labels: []mlp.Label{{Key: "custom-label-key", Value: "value-1"}},
}
- svc, err := sb.NewEnricherService(routerVersion, project, "secret", 10, 0, 1.5, data.initialScale)
+ svc, err := sb.NewEnricherService(routerVersion, project, "secret", data.initialScale)
if data.err == "" {
assert.NoError(t, err)
assert.Equal(t, data.expected, svc)
@@ -123,7 +144,27 @@ func TestNewEnricherService(t *testing.T) {
}
func TestNewEnsemblerService(t *testing.T) {
- sb := NewClusterServiceBuilder(resource.MustParse("2"), resource.MustParse("2Gi"), 30, testTopologySpreadConstraints)
+ userContainerCPULimitRequestFactor := 2.0
+ userContainerMemoryLimitRequestFactor := 1.5
+
+ sb := NewClusterServiceBuilder(
+ resource.MustParse("2"),
+ resource.MustParse("2Gi"),
+ 30,
+ testTopologySpreadConstraints,
+ &config.KnativeServiceDefaults{
+ QueueProxyResourcePercentage: 20,
+ UserContainerCPULimitRequestFactor: userContainerCPULimitRequestFactor,
+ UserContainerMemoryLimitRequestFactor: userContainerMemoryLimitRequestFactor,
+ },
+ )
+
+ cpuRequest := resource.MustParse("200m")
+ cpuLimit := cluster.ComputeResource(cpuRequest, userContainerCPULimitRequestFactor)
+
+ memoryRequest := resource.MustParse("1024Mi")
+ memoryLimit := cluster.ComputeResource(memoryRequest, userContainerMemoryLimitRequestFactor)
+
testDataBasePath := filepath.Join("..", "..", "testdata", "cluster", "servicebuilder")
testInitialScale := 5
@@ -136,8 +177,10 @@ func TestNewEnsemblerService(t *testing.T) {
Name: "test-svc-turing-ensembler-1",
Namespace: "test-project",
Image: "asia.gcr.io/gcp-project-id/echo:1.0.2",
- CPURequests: resource.MustParse("200m"),
- MemoryRequests: resource.MustParse("1024Mi"),
+ CPURequests: cpuRequest,
+ CPULimit: &cpuLimit,
+ MemoryRequests: memoryRequest,
+ MemoryLimit: &memoryLimit,
Envs: []corev1.EnvVar{
{Name: "TEST_ENV", Value: "ensembler"},
{Name: "GOOGLE_APPLICATION_CREDENTIALS", Value: "/var/secret/ensembler-service-account.json"},
@@ -160,17 +203,15 @@ func TestNewEnsemblerService(t *testing.T) {
},
VolumeMounts: []corev1.VolumeMount{{Name: secretVolume, MountPath: secretMountPath}},
},
- IsClusterLocal: true,
- ContainerPort: 8080,
- MinReplicas: 2,
- MaxReplicas: 3,
- AutoscalingMetric: "concurrency",
- AutoscalingTarget: "1",
- InitialScale: &testInitialScale,
- TopologySpreadConstraints: testTopologySpreadConstraints,
- QueueProxyResourcePercentage: 20,
- UserContainerCPULimitRequestFactor: 0,
- UserContainerMemoryLimitRequestFactor: 1.5,
+ IsClusterLocal: true,
+ ContainerPort: 8080,
+ MinReplicas: 2,
+ MaxReplicas: 3,
+ AutoscalingMetric: "concurrency",
+ AutoscalingTarget: "1",
+ InitialScale: &testInitialScale,
+ TopologySpreadConstraints: testTopologySpreadConstraints,
+ QueueProxyResourcePercentage: 20,
},
},
"success with ensembler docker type": {
@@ -180,8 +221,10 @@ func TestNewEnsemblerService(t *testing.T) {
Name: "test-svc-turing-ensembler-1",
Namespace: "test-project",
Image: "asia.gcr.io/gcp-project-id/echo:1.0.2",
- CPURequests: resource.MustParse("200m"),
- MemoryRequests: resource.MustParse("1024Mi"),
+ CPURequests: cpuRequest,
+ CPULimit: &cpuLimit,
+ MemoryRequests: memoryRequest,
+ MemoryLimit: &memoryLimit,
Envs: []corev1.EnvVar{
{Name: "TEST_ENV", Value: "ensembler"},
{Name: "GOOGLE_APPLICATION_CREDENTIALS", Value: "/var/secret/ensembler-service-account.json"},
@@ -204,16 +247,14 @@ func TestNewEnsemblerService(t *testing.T) {
},
VolumeMounts: []corev1.VolumeMount{{Name: secretVolume, MountPath: secretMountPath}},
},
- IsClusterLocal: true,
- ContainerPort: 8080,
- MinReplicas: 2,
- MaxReplicas: 3,
- AutoscalingMetric: "cpu",
- AutoscalingTarget: "90",
- TopologySpreadConstraints: testTopologySpreadConstraints,
- QueueProxyResourcePercentage: 20,
- UserContainerCPULimitRequestFactor: 0,
- UserContainerMemoryLimitRequestFactor: 1.5,
+ IsClusterLocal: true,
+ ContainerPort: 8080,
+ MinReplicas: 2,
+ MaxReplicas: 3,
+ AutoscalingMetric: "cpu",
+ AutoscalingTarget: "90",
+ TopologySpreadConstraints: testTopologySpreadConstraints,
+ QueueProxyResourcePercentage: 20,
},
},
"failure": {
@@ -233,7 +274,7 @@ func TestNewEnsemblerService(t *testing.T) {
Stream: "test-stream",
Team: "test-team",
}
- svc, err := sb.NewEnsemblerService(routerVersion, project, "secret", 20, 0, 1.5, data.initialScale)
+ svc, err := sb.NewEnsemblerService(routerVersion, project, "secret", data.initialScale)
if data.err == "" {
assert.NoError(t, err)
assert.Equal(t, data.expected, svc)
diff --git a/api/turing/config/config.go b/api/turing/config/config.go
index 2eb40bf7f..cc3ef5fbc 100644
--- a/api/turing/config/config.go
+++ b/api/turing/config/config.go
@@ -263,8 +263,9 @@ type KubernetesLabelConfigs struct {
// Knative services
type KnativeServiceDefaults struct {
QueueProxyResourcePercentage int
- UserContainerCPULimitRequestFactor float64 `json:"userContainerLimitCPURequestFactor"`
- UserContainerMemoryLimitRequestFactor float64 `json:"userContainerLimitMemoryRequestFactor"`
+ UserContainerCPULimitRequestFactor float64
+ UserContainerMemoryLimitRequestFactor float64
+ DefaultEnvVarsWithoutCPULimits []corev1.EnvVar
}
// SinglePageApplicationConfig holds configuration required for serving SPAs
diff --git a/api/turing/config/config_test.go b/api/turing/config/config_test.go
index c07180fde..a08ea9104 100644
--- a/api/turing/config/config_test.go
+++ b/api/turing/config/config_test.go
@@ -283,6 +283,12 @@ func TestLoad(t *testing.T) {
QueueProxyResourcePercentage: 20,
UserContainerCPULimitRequestFactor: 0,
UserContainerMemoryLimitRequestFactor: 1.25,
+ DefaultEnvVarsWithoutCPULimits: []corev1.EnvVar{
+ {
+ Name: "foo",
+ Value: "bar",
+ },
+ },
},
RouterDefaults: &config.RouterDefaults{
LogLevel: "INFO",
@@ -425,6 +431,12 @@ func TestLoad(t *testing.T) {
QueueProxyResourcePercentage: 20,
UserContainerCPULimitRequestFactor: 0,
UserContainerMemoryLimitRequestFactor: 1.25,
+ DefaultEnvVarsWithoutCPULimits: []corev1.EnvVar{
+ {
+ Name: "foo",
+ Value: "bar",
+ },
+ },
},
RouterDefaults: &config.RouterDefaults{
LogLevel: "INFO",
@@ -554,6 +566,12 @@ func TestLoad(t *testing.T) {
QueueProxyResourcePercentage: 20,
UserContainerCPULimitRequestFactor: 0,
UserContainerMemoryLimitRequestFactor: 1.25,
+ DefaultEnvVarsWithoutCPULimits: []corev1.EnvVar{
+ {
+ Name: "foo",
+ Value: "bar",
+ },
+ },
},
DeployConfig: &config.DeploymentConfig{
EnvironmentType: "dev",
diff --git a/api/turing/config/testdata/config-1.yaml b/api/turing/config/testdata/config-1.yaml
index ff2a7aeac..968217574 100644
--- a/api/turing/config/testdata/config-1.yaml
+++ b/api/turing/config/testdata/config-1.yaml
@@ -48,6 +48,9 @@ KnativeServiceDefaults:
QueueProxyResourcePercentage: 20
UserContainerCPULimitRequestFactor: 0
UserContainerMemoryLimitRequestFactor: 1.25
+ DefaultEnvVarsWithoutCPULimits:
+ - Name: foo
+ Value: bar
RouterDefaults:
FluentdConfig:
FlushIntervalSeconds: 60
diff --git a/api/turing/internal/testutils/validation.go b/api/turing/internal/testutils/validation.go
index 822c3fc4f..a3ed80f0c 100644
--- a/api/turing/internal/testutils/validation.go
+++ b/api/turing/internal/testutils/validation.go
@@ -1,7 +1,6 @@
package testutils
import (
- "encoding/json"
"fmt"
"reflect"
@@ -16,21 +15,7 @@ func CompareObjects(actual interface{}, expected interface{}) error {
allowUnexportedOn = reflect.ValueOf(actual).Elem().Interface()
}
if !cmp.Equal(actual, expected, cmp.AllowUnexported(allowUnexportedOn)) {
- actualString := fmt.Sprintf("%+v", actual)
- expectedString := fmt.Sprintf("%+v", expected)
-
- // Attempt to encode values to JSON, for logging
- jsonActual, err := json.Marshal(actual)
- if err == nil {
- actualString = string(jsonActual)
- }
- jsonExpected, err := json.Marshal(expected)
- if err == nil {
- expectedString = string(jsonExpected)
- }
-
- return fmt.Errorf("Did not get expected configuration.\nEXPECTED:\n%v\nGOT:\n%v",
- expectedString, actualString)
+ return fmt.Errorf(cmp.Diff(actual, expected, cmp.AllowUnexported(allowUnexportedOn)))
}
return nil
}
diff --git a/api/turing/models/resource_request.go b/api/turing/models/resource_request.go
index 0d3e3f044..ab4dcf764 100644
--- a/api/turing/models/resource_request.go
+++ b/api/turing/models/resource_request.go
@@ -16,6 +16,8 @@ type ResourceRequest struct {
// CPU request of inference service
CPURequest resource.Quantity `json:"cpu_request"`
+ // CPU limit of inference service
+ CPULimit *resource.Quantity `json:"cpu_limit,omitempty"`
// Memory request of inference service
MemoryRequest resource.Quantity `json:"memory_request"`
}
diff --git a/api/turing/service/router_deployment_service.go b/api/turing/service/router_deployment_service.go
index 269c708b8..734e2866f 100644
--- a/api/turing/service/router_deployment_service.go
+++ b/api/turing/service/router_deployment_service.go
@@ -66,9 +66,6 @@ type deploymentService struct {
sentryDSN string
routerDefaults *config.RouterDefaults
- // Knative service configs
- knativeServiceConfig *config.KnativeServiceDefaults
-
// Ensembler service image builder for real time ensemblers
ensemblerServiceImageBuilder imagebuilder.ImageBuilder
@@ -94,6 +91,7 @@ func NewDeploymentService(
resource.Quantity(cfg.DeployConfig.MaxMemory),
cfg.DeployConfig.MaxAllowedReplica,
cfg.DeployConfig.TopologySpreadConstraints,
+ cfg.KnativeServiceDefaults,
)
return &deploymentService{
@@ -101,7 +99,6 @@ func NewDeploymentService(
deploymentDeletionTimeout: cfg.DeployConfig.DeletionTimeout,
environmentType: cfg.DeployConfig.EnvironmentType,
routerDefaults: cfg.RouterDefaults,
- knativeServiceConfig: cfg.KnativeServiceDefaults,
ensemblerServiceImageBuilder: ensemblerServiceImageBuilder,
sentryEnabled: cfg.Sentry.Enabled,
sentryDSN: cfg.Sentry.DSN,
@@ -176,9 +173,6 @@ func (ds *deploymentService) DeployRouterVersion(
routerVersion, currRouterVersion, project, ds.environmentType,
secretName, experimentConfig,
ds.routerDefaults, ds.sentryEnabled, ds.sentryDSN,
- ds.knativeServiceConfig.QueueProxyResourcePercentage,
- ds.knativeServiceConfig.UserContainerCPULimitRequestFactor,
- ds.knativeServiceConfig.UserContainerMemoryLimitRequestFactor,
)
if err != nil {
return endpoint, err
@@ -277,9 +271,6 @@ func (ds *deploymentService) UndeployRouterVersion(
ctx, controller,
routerVersion, nil, project, ds.environmentType, "", nil,
ds.routerDefaults, ds.sentryEnabled, ds.sentryDSN,
- ds.knativeServiceConfig.QueueProxyResourcePercentage,
- ds.knativeServiceConfig.UserContainerCPULimitRequestFactor,
- ds.knativeServiceConfig.UserContainerMemoryLimitRequestFactor,
)
if err != nil {
return err
@@ -367,9 +358,6 @@ func (ds *deploymentService) createServices(
routerDefaults *config.RouterDefaults,
sentryEnabled bool,
sentryDSN string,
- knativeQueueProxyResourcePercentage int,
- userContainerCPULimitRequestFactor float64,
- userContainerMemoryLimitRequestFactor float64,
) ([]*cluster.KnativeService, error) {
services := []*cluster.KnativeService{}
namespace := servicebuilder.GetNamespace(project)
@@ -387,9 +375,7 @@ func (ds *deploymentService) createServices(
}
}
enricherSvc, err := ds.svcBuilder.NewEnricherService(
- routerVersion, project, secretName,
- knativeQueueProxyResourcePercentage, userContainerCPULimitRequestFactor,
- userContainerMemoryLimitRequestFactor, currEnricherReplicas,
+ routerVersion, project, secretName, currEnricherReplicas,
)
if err != nil {
return services, err
@@ -410,9 +396,7 @@ func (ds *deploymentService) createServices(
}
}
ensemblerSvc, err := ds.svcBuilder.NewEnsemblerService(
- routerVersion, project, secretName,
- knativeQueueProxyResourcePercentage, userContainerCPULimitRequestFactor,
- userContainerMemoryLimitRequestFactor, currEnsemblerReplicas,
+ routerVersion, project, secretName, currEnsemblerReplicas,
)
if err != nil {
return services, err
@@ -431,9 +415,7 @@ func (ds *deploymentService) createServices(
}
routerService, err := ds.svcBuilder.NewRouterService(
routerVersion, project, envType, secretName, experimentConfig,
- routerDefaults, sentryEnabled, sentryDSN,
- knativeQueueProxyResourcePercentage, userContainerCPULimitRequestFactor,
- userContainerMemoryLimitRequestFactor, currRouterReplicas,
+ routerDefaults, sentryEnabled, sentryDSN, currRouterReplicas,
)
if err != nil {
return services, err
diff --git a/api/turing/service/router_deployment_service_test.go b/api/turing/service/router_deployment_service_test.go
index 4f8873d11..b77961217 100644
--- a/api/turing/service/router_deployment_service_test.go
+++ b/api/turing/service/router_deployment_service_test.go
@@ -33,7 +33,8 @@ import (
// mockClusterServiceBuilder implements the servicebuilder.ClusterServiceBuilder interface
type mockClusterServiceBuilder struct {
- rv *models.RouterVersion
+ rv *models.RouterVersion
+ knativeServiceConfig *config.KnativeServiceDefaults
}
func (msb *mockClusterServiceBuilder) NewRouterEndpoint(
@@ -75,9 +76,6 @@ func (msb *mockClusterServiceBuilder) NewEnricherService(
rv *models.RouterVersion,
project *mlp.Project,
_ string,
- queueProxyResourcePercentage int,
- userContainerCPULimitRequestFactor float64,
- userContainerMemoryLimitRequestFactor float64,
_ *int,
) (*cluster.KnativeService, error) {
if rv != msb.rv {
@@ -88,9 +86,7 @@ func (msb *mockClusterServiceBuilder) NewEnricherService(
Name: fmt.Sprintf("%s-enricher-%d", rv.Router.Name, rv.Version),
Namespace: project.Name,
},
- QueueProxyResourcePercentage: queueProxyResourcePercentage,
- UserContainerCPULimitRequestFactor: userContainerCPULimitRequestFactor,
- UserContainerMemoryLimitRequestFactor: userContainerMemoryLimitRequestFactor,
+ QueueProxyResourcePercentage: msb.knativeServiceConfig.QueueProxyResourcePercentage,
}, nil
}
@@ -98,9 +94,6 @@ func (msb *mockClusterServiceBuilder) NewEnsemblerService(
rv *models.RouterVersion,
project *mlp.Project,
_ string,
- queueProxyResourcePercentage int,
- userContainerCPULimitRequestFactor float64,
- userContainerMemoryLimitRequestFactor float64,
_ *int,
) (*cluster.KnativeService, error) {
if rv != msb.rv {
@@ -111,9 +104,7 @@ func (msb *mockClusterServiceBuilder) NewEnsemblerService(
Name: fmt.Sprintf("%s-ensembler-%d", rv.Router.Name, rv.Version),
Namespace: project.Name,
},
- QueueProxyResourcePercentage: queueProxyResourcePercentage,
- UserContainerCPULimitRequestFactor: userContainerCPULimitRequestFactor,
- UserContainerMemoryLimitRequestFactor: userContainerMemoryLimitRequestFactor,
+ QueueProxyResourcePercentage: msb.knativeServiceConfig.QueueProxyResourcePercentage,
}, nil
}
@@ -126,9 +117,6 @@ func (msb *mockClusterServiceBuilder) NewRouterService(
routerDefaults *config.RouterDefaults,
sentryEnabled bool,
sentryDSN string,
- queueProxyResourcePercentage int,
- userContainerCPULimitRequestFactor float64,
- userContainerMemoryLimitRequestFactor float64,
_ *int,
) (*cluster.KnativeService, error) {
if rv != msb.rv {
@@ -150,9 +138,7 @@ func (msb *mockClusterServiceBuilder) NewRouterService(
Data: string(expConfig),
},
},
- QueueProxyResourcePercentage: queueProxyResourcePercentage,
- UserContainerCPULimitRequestFactor: userContainerCPULimitRequestFactor,
- UserContainerMemoryLimitRequestFactor: userContainerMemoryLimitRequestFactor,
+ QueueProxyResourcePercentage: msb.knativeServiceConfig.QueueProxyResourcePercentage,
}, nil
}
@@ -225,7 +211,14 @@ func TestDeployEndpoint(t *testing.T) {
Return(&policyv1.PodDisruptionBudget{}, nil)
// Create mock service builder
- svcBuilder := &mockClusterServiceBuilder{routerVersion}
+ svcBuilder := &mockClusterServiceBuilder{
+ rv: routerVersion,
+ knativeServiceConfig: &config.KnativeServiceDefaults{
+ QueueProxyResourcePercentage: 20,
+ UserContainerCPULimitRequestFactor: 1.75,
+ UserContainerMemoryLimitRequestFactor: 1.75,
+ },
+ }
// Create test endpoint service with mock controller and service builder
ds := &deploymentService{
@@ -238,11 +231,6 @@ func TestDeployEndpoint(t *testing.T) {
environmentType: envType,
sentryEnabled: true,
sentryDSN: "test:dsn",
- knativeServiceConfig: &config.KnativeServiceDefaults{
- QueueProxyResourcePercentage: 20,
- UserContainerCPULimitRequestFactor: 1.75,
- UserContainerMemoryLimitRequestFactor: 1.75,
- },
clusterControllers: map[string]cluster.Controller{
testEnv: controller,
},
@@ -304,18 +292,14 @@ func TestDeployEndpoint(t *testing.T) {
Name: fmt.Sprintf("%s-enricher-%d", routerVersion.Router.Name, routerVersion.Version),
Namespace: testNamespace,
},
- QueueProxyResourcePercentage: 20,
- UserContainerCPULimitRequestFactor: 1.75,
- UserContainerMemoryLimitRequestFactor: 1.75,
+ QueueProxyResourcePercentage: 20,
})
controller.AssertCalled(t, "DeployKnativeService", mock.Anything, &cluster.KnativeService{
BaseService: &cluster.BaseService{
Name: fmt.Sprintf("%s-ensembler-%d", routerVersion.Router.Name, routerVersion.Version),
Namespace: testNamespace,
},
- QueueProxyResourcePercentage: 20,
- UserContainerCPULimitRequestFactor: 1.75,
- UserContainerMemoryLimitRequestFactor: 1.75,
+ QueueProxyResourcePercentage: 20,
})
controller.AssertCalled(t, "ApplyConfigMap", mock.Anything, testNamespace,
&cluster.ConfigMap{Name: fmt.Sprintf("%s-fiber-config-%d", routerVersion.Router.Name, routerVersion.Version)})
@@ -337,9 +321,7 @@ func TestDeployEndpoint(t *testing.T) {
),
},
},
- QueueProxyResourcePercentage: 20,
- UserContainerCPULimitRequestFactor: 1.75,
- UserContainerMemoryLimitRequestFactor: 1.75,
+ QueueProxyResourcePercentage: 20,
})
controller.AssertCalled(t, "CreateSecret", mock.Anything, &cluster.Secret{
Name: fmt.Sprintf("%s-svc-acct-secret-%d", routerVersion.Router.Name, routerVersion.Version),
@@ -427,7 +409,14 @@ func TestDeleteEndpoint(t *testing.T) {
routerVersion := tu.GetRouterVersion(t, filePath)
// Create mock service builder
- svcBuilder := &mockClusterServiceBuilder{routerVersion}
+ svcBuilder := &mockClusterServiceBuilder{
+ rv: routerVersion,
+ knativeServiceConfig: &config.KnativeServiceDefaults{
+ QueueProxyResourcePercentage: 20,
+ UserContainerCPULimitRequestFactor: 1.75,
+ UserContainerMemoryLimitRequestFactor: 1.75,
+ },
+ }
// Create test endpoint service with mock controller and service builder
ds := &deploymentService{
@@ -437,11 +426,6 @@ func TestDeleteEndpoint(t *testing.T) {
},
deploymentTimeout: timeout,
deploymentDeletionTimeout: timeout,
- knativeServiceConfig: &config.KnativeServiceDefaults{
- QueueProxyResourcePercentage: 20,
- UserContainerCPULimitRequestFactor: 1.75,
- UserContainerMemoryLimitRequestFactor: 1.75,
- },
clusterControllers: map[string]cluster.Controller{
testEnv: controller,
},
@@ -779,6 +763,7 @@ func TestCreatePodDisruptionBudgets(t *testing.T) {
resource.MustParse("200Mi"),
10,
[]corev1.TopologySpreadConstraint{},
+ nil,
),
}
pdbs := ds.createPodDisruptionBudgets(tt.rv, &mlp.Project{Name: "ns"})
diff --git a/docs/.gitbook/assets/pyfunc_ensembler_config.png b/docs/.gitbook/assets/pyfunc_ensembler_config.png
index 7ffe61f86..739c8c2ef 100644
Binary files a/docs/.gitbook/assets/pyfunc_ensembler_config.png and b/docs/.gitbook/assets/pyfunc_ensembler_config.png differ
diff --git a/docs/.gitbook/assets/resources_panel.png b/docs/.gitbook/assets/resources_panel.png
index 9ac0f15ac..14f923f41 100644
Binary files a/docs/.gitbook/assets/resources_panel.png and b/docs/.gitbook/assets/resources_panel.png differ
diff --git a/docs/how-to/create-a-router/configure-enricher.md b/docs/how-to/create-a-router/configure-enricher.md
index ace7f1caa..7011aff66 100644
--- a/docs/how-to/create-a-router/configure-enricher.md
+++ b/docs/how-to/create-a-router/configure-enricher.md
@@ -47,6 +47,11 @@ Configure the resources required for the enricher. There are 3 required inputs,
**Min/Max Replicas**: Min/max number of replicas for your enricher. Scaling of the enricher based on traffic volume will be automatically done for you.
+**CPU Limit**: By default, Turing determines the CPU limits of all deployed components using platform-level configured
+values. These CPU limits is calculated as a factor of the user-defined CPU request value for each component (e.g. 2x
+of the CPU request value). However, you can override this platform-level configured value by setting this value
+explicitly on the UI (as seen above) or via the SDK.
+
Optionally, modify the autoscaling policy on the enricher.
![](../../.gitbook/assets/autoscaling_policy_panel.png)
diff --git a/docs/how-to/create-a-router/configure-ensembler.md b/docs/how-to/create-a-router/configure-ensembler.md
index 90a0d888c..17716afdf 100644
--- a/docs/how-to/create-a-router/configure-ensembler.md
+++ b/docs/how-to/create-a-router/configure-ensembler.md
@@ -83,6 +83,11 @@ Configure the resources required for the ensembler. There are 3 required inputs,
**Min/Max Replicas**: Min/max number of replicas for your ensembler. Scaling of the ensembler based on traffic volume will be automatically done for you.
+**CPU Limit**: By default, Turing determines the CPU limits of all deployed components using platform-level configured
+values. These CPU limits is calculated as a factor of the user-defined CPU request value for each component (e.g. 2x
+of the CPU request value). However, you can override this platform-level configured value by setting this value
+explicitly on the UI (as seen above) or via the SDK.
+
Optionally, modify the autoscaling policy on the ensembler.
![](../../.gitbook/assets/autoscaling_policy_panel.png)
@@ -125,6 +130,11 @@ registered in your current project. You'll also need to indicate your desired ti
**Min/Max Replicas**: Min/max number of replicas for your ensembler. Scaling of the ensembler based on traffic volume will be automatically done for you.
+**CPU Limit**: By default, Turing determines the CPU limits of all deployed components using platform-level configured
+values. These CPU limits is calculated as a factor of the user-defined CPU request value for each component (e.g. 2x
+of the CPU request value). However, you can override this platform-level configured value by setting this value
+explicitly on the UI (as seen above) or via the SDK.
+
Optionally, modify the autoscaling policy on the ensembler.
![](../../.gitbook/assets/autoscaling_policy_panel.png)
diff --git a/sdk/tests/conftest.py b/sdk/tests/conftest.py
index 5899a7114..e75cfd9a0 100644
--- a/sdk/tests/conftest.py
+++ b/sdk/tests/conftest.py
@@ -277,7 +277,7 @@ def generic_router_version_status():
@pytest.fixture
def generic_resource_request():
return turing.generated.models.ResourceRequest(
- min_replica=1, max_replica=3, cpu_request="100m", memory_request="512Mi"
+ min_replica=1, max_replica=3, cpu_request="100m", memory_request="512Mi", cpu_limit=None,
)
diff --git a/sdk/tests/router/config/router_ensembler_config_test.py b/sdk/tests/router/config/router_ensembler_config_test.py
index bb19f788b..878d4cfd1 100644
--- a/sdk/tests/router/config/router_ensembler_config_test.py
+++ b/sdk/tests/router/config/router_ensembler_config_test.py
@@ -600,7 +600,7 @@ def test_create_nop_router_ensembler_config_with_invalid_route(
image="test.io/just-a-test/turing-ensembler:0.0.0-build.0",
port=5120,
resource_request=turing.generated.models.ResourceRequest(
- cpu_request="100m", max_replica=3,
+ cpu_request="100m", cpu_limit=None, max_replica=3,
memory_request="512Mi", min_replica=1,
),
service_account="secret-name-for-google-service-account",
@@ -619,8 +619,8 @@ def test_create_nop_router_ensembler_config_with_invalid_route(
env=[turing.generated.models.EnvVar(name="env_name", value="env_val")],
project_id=77,
resource_request=turing.generated.models.ResourceRequest(
- cpu_request="100m", max_replica=3,
- memory_request="512Mi",min_replica=1,
+ cpu_request="100m", cpu_limit=None, max_replica=3,
+ memory_request="512Mi", min_replica=1,
),
timeout="500ms"
),
diff --git a/sdk/turing/generated/model/resource_request.py b/sdk/turing/generated/model/resource_request.py
index c352380f5..a76eef61b 100644
--- a/sdk/turing/generated/model/resource_request.py
+++ b/sdk/turing/generated/model/resource_request.py
@@ -60,6 +60,11 @@ class ResourceRequest(ModelNormal):
'pattern': r'^(\d{1,3}(\.\d{1,3})?)$|^(\d{2,5}m)$', # noqa: E501
},
},
+ ('cpu_limit',): {
+ 'regex': {
+ 'pattern': r'^(\d{1,3}(\.\d{1,3})?)$|^(\d{2,5}m)$', # noqa: E501
+ },
+ },
('memory_request',): {
'regex': {
'pattern': r'^\d+(Ei?|Pi?|Ti?|Gi?|Mi?|Ki?)?$', # noqa: E501
@@ -85,6 +90,7 @@ def openapi_types():
'min_replica': (int,), # noqa: E501
'max_replica': (int,), # noqa: E501
'cpu_request': (str,), # noqa: E501
+ 'cpu_limit': (str, none_type,), # noqa: E501
'memory_request': (str,), # noqa: E501
}
@@ -97,6 +103,7 @@ def discriminator():
'min_replica': 'min_replica', # noqa: E501
'max_replica': 'max_replica', # noqa: E501
'cpu_request': 'cpu_request', # noqa: E501
+ 'cpu_limit': 'cpu_limit', # noqa: E501
'memory_request': 'memory_request', # noqa: E501
}
@@ -149,6 +156,7 @@ def __init__(self, *args, **kwargs): # noqa: E501
min_replica (int): [optional] # noqa: E501
max_replica (int): [optional] # noqa: E501
cpu_request (str): [optional] # noqa: E501
+ cpu_limit (str, none_type): [optional] # noqa: E501
memory_request (str): [optional] # noqa: E501
"""
diff --git a/sdk/turing/router/config/resource_request.py b/sdk/turing/router/config/resource_request.py
index 45885b7d9..65a8db8f0 100644
--- a/sdk/turing/router/config/resource_request.py
+++ b/sdk/turing/router/config/resource_request.py
@@ -1,5 +1,5 @@
from dataclasses import dataclass, field
-from typing import ClassVar
+from typing import Optional
import turing.generated.models
from turing.generated.model_utils import OpenApiModel
@@ -11,11 +11,13 @@ class ResourceRequest:
max_replica: int
cpu_request: str
memory_request: str
+ cpu_limit: Optional[str] = None
_min_replica: int = field(init=False, repr=False)
_max_replica: int = field(init=False, repr=False)
_cpu_request: str = field(init=False, repr=False)
_memory_request: str = field(init=False, repr=False)
+ _cpu_limit: Optional[str] = field(init=False, repr=False, default=None)
@property
def min_replica(self) -> int:
@@ -45,6 +47,16 @@ def cpu_request(self) -> str:
def cpu_request(self, cpu_request: str):
self._cpu_request = cpu_request
+ @property
+ def cpu_limit(self) -> str:
+ return self._cpu_limit
+
+ @cpu_limit.setter
+ def cpu_limit(self, cpu_limit):
+ if type(cpu_limit) is property:
+ cpu_limit = ResourceRequest._cpu_limit
+ self._cpu_limit = cpu_limit
+
@property
def memory_request(self) -> str:
return self._memory_request
@@ -66,6 +78,7 @@ def to_open_api(self) -> OpenApiModel:
min_replica=self.min_replica,
max_replica=self.max_replica,
cpu_request=self.cpu_request,
+ cpu_limit=self.cpu_limit,
memory_request=self.memory_request,
)
diff --git a/ui/src/router/components/configuration/components/ResourcesConfigTable.js b/ui/src/router/components/configuration/components/ResourcesConfigTable.js
index 32ce7e2f8..136037c25 100644
--- a/ui/src/router/components/configuration/components/ResourcesConfigTable.js
+++ b/ui/src/router/components/configuration/components/ResourcesConfigTable.js
@@ -5,13 +5,19 @@ import { autoscalingPolicyOptions } from "../../form/components/autoscaling_poli
import { ConfigMultiSectionPanel } from "../../../../components/config_multi_section_panel/ConfigMultiSectionPanel"
const ResourcesSection = ({
- resourceRequest: { cpu_request, memory_request, min_replica, max_replica },
+ resourceRequest: { cpu_request, cpu_limit, memory_request, min_replica, max_replica },
}) => {
const items = [
{
title: "CPU Request",
description: cpu_request,
},
+ ...(cpu_limit !== undefined && cpu_limit !== "0" && cpu_limit !== "") ? [
+ {
+ title: "CPU Limit",
+ description: cpu_limit,
+ }
+ ] : [],
{
title: "Memory Request",
description: memory_request,
diff --git a/ui/src/router/components/form/components/CPULimitsFormGroup.js b/ui/src/router/components/form/components/CPULimitsFormGroup.js
new file mode 100644
index 000000000..4c10d0a63
--- /dev/null
+++ b/ui/src/router/components/form/components/CPULimitsFormGroup.js
@@ -0,0 +1,44 @@
+import React, { Fragment } from "react";
+import { FormLabelWithToolTip } from "@caraml-dev/ui-lib";
+import { EuiDescribedFormGroup, EuiFieldText, EuiFormRow } from "@elastic/eui";
+
+
+export const CPULimitsFormGroup = ({
+ resourcesConfig,
+ onChange,
+ errors,
+}) => {
+ return (
+