diff --git a/docs/resources/driver.md b/docs/resources/driver.md index ae993111..5a6715e1 100644 --- a/docs/resources/driver.md +++ b/docs/resources/driver.md @@ -154,6 +154,7 @@ resource "rafay_driver" "driver" { ***Optional*** - `container` (Block List, Max: 1) Specify the container driver config (see [below for nested schema](#nestedblock--spec--config--container)) +- `function` (Block List, Max: 1) Configure the function driver configuration (see [below for nested schema](#nestedblock--spec--config--function)) - `http` (Block List, Max: 1) Specify the http driver config (see [below for nested schema](#nestedblock--spec--config--http)) - `max_retry_count` (Number) Specify the max retry count - `success_condition` (String) Specify the success condition @@ -286,6 +287,81 @@ resource "rafay_driver" "driver" { - `value` (Boolean) + +### Nested Schema for `spec.config.function` + +***Optional*** + +- `resources` (Block List, Max: 1) Resource requirements for the function container. When unset=true, no requests/limits are applied (see [below for nested schema](#nestedblock--spec--config--function--resources)) +- `hpa` (Block List, Max: 1) Horizontal Pod Autoscaler configuration for the function deployment (see [below for nested schema](#nestedblock--spec--config--function--hpa)) + + +### Nested Schema for `spec.config.function.resources` + +***Optional*** + +- `requests` (Block List, Max: 1) Resource requests for the function container (see [below for nested schema](#nestedblock--spec--config--function--resources--requests)) +- `limits` (Block List, Max: 1) Resource limits for the function container (see [below for nested schema](#nestedblock--spec--config--function--resources--limits)) +- `unset` (Block List, Max: 1) When true, no CPU/memory requests and limits are applied (see [below for nested schema](#nestedblock--spec--config--function--resources--unset)) + + +### Nested Schema for `spec.config.function.resources.requests` + +***Optional*** + +- `cpu` (String) Kubernetes quantity string for CPU (e.g. "500m", "1") +- `memory` (String) Kubernetes quantity string for memory (e.g. "256Mi", "1Gi") + + +### Nested Schema for `spec.config.function.resources.limits` + +***Optional*** + +- `cpu` (String) Kubernetes quantity string for CPU (e.g. "500m", "1") +- `memory` (String) Kubernetes quantity string for memory (e.g. "256Mi", "1Gi") + + +### Nested Schema for `spec.config.function.resources.unset` + +***Optional*** + +- `value` (Boolean) + + +### Nested Schema for `spec.config.function.hpa` + +***Optional*** + +- `enabled` (Block List, Max: 1) Enable HPA for the function deployment (see [below for nested schema](#nestedblock--spec--config--function--hpa--enabled)) +- `min_replicas` (Number) Minimum number of replicas for HPA +- `max_replicas` (Number) Maximum number of replicas for HPA +- `resource_metrics` (Block List) Resource metric sources (CPU/Memory) for autoscaling; each entry becomes a MetricSpec of type Resource (see [below for nested schema](#nestedblock--spec--config--function--hpa--resource_metrics)) + + +### Nested Schema for `spec.config.function.hpa.enabled` + +***Optional*** + +- `value` (Boolean) + + +### Nested Schema for `spec.config.function.hpa.resource_metrics` + +***Optional*** + +- `name` (String) Name of the resource (e.g. "cpu", "memory") +- `target` (Block List, Max: 1) Target value for the metric (see [below for nested schema](#nestedblock--spec--config--function--hpa--resource_metrics--target)) + + +### Nested Schema for `spec.config.function.hpa.resource_metrics.target` + +***Optional*** + +- `type` (String) Metric target type: "Utilization", "Value", or "AverageValue" +- `value` (String) Target value of the metric (Kubernetes quantity string, e.g. "500m") +- `average_value` (String) Target average value of the metric across pods (Kubernetes quantity string) +- `average_utilization` (Number) Target average utilization as a percentage of the requested resource (e.g. 80 for 80%) + ### Nested Schema for `spec.config.http` diff --git a/docs/resources/workflow_handler.md b/docs/resources/workflow_handler.md index 4f6cb160..4983003a 100644 --- a/docs/resources/workflow_handler.md +++ b/docs/resources/workflow_handler.md @@ -666,6 +666,8 @@ resource "rafay_workflow_handler" "workflow_handler" { - `memory_limit_mb` (String) Configure the Memory Limits as the maximum amount of a resource to be used by a function - `name` (String) Specify the name of the function - `num_replicas` (Number) Specify the number of replicas for the function +- `resources` (Block List, Max: 1) Resource requirements for the function container. When unset=true, no requests/limits are applied (see [below for nested schema](#nestedblock--spec--config--function--resources)) +- `hpa` (Block List, Max: 1) Horizontal Pod Autoscaler configuration for the function deployment (see [below for nested schema](#nestedblock--spec--config--function--hpa)) - `skip_build` (Block List, Max: 1) Skip the build process for the function (see [below for nested schema](#nestedblock--spec--config--function--skip_build)) - `source` (String) Specify the source of the function - `system_packages` (List of String) Specify the system packages for the function @@ -1059,6 +1061,81 @@ resource "rafay_workflow_handler" "workflow_handler" { - `toleration_seconds` (Number) - `value` (String) + + +### Nested Schema for `spec.config.function.resources` + +**_Optional_** + +- `requests` (Block List, Max: 1) Resource requests for the function container (see [below for nested schema](#nestedblock--spec--config--function--resources--requests)) +- `limits` (Block List, Max: 1) Resource limits for the function container (see [below for nested schema](#nestedblock--spec--config--function--resources--limits)) +- `unset` (Block List, Max: 1) When true, no CPU/memory requests and limits are applied (see [below for nested schema](#nestedblock--spec--config--function--resources--unset)) + + + +### Nested Schema for `spec.config.function.resources.requests` + +**_Optional_** + +- `cpu` (String) Kubernetes quantity string for CPU (e.g. "500m", "1") +- `memory` (String) Kubernetes quantity string for memory (e.g. "256Mi", "1Gi") + + + +### Nested Schema for `spec.config.function.resources.limits` + +**_Optional_** + +- `cpu` (String) Kubernetes quantity string for CPU (e.g. "500m", "1") +- `memory` (String) Kubernetes quantity string for memory (e.g. "256Mi", "1Gi") + + + +### Nested Schema for `spec.config.function.resources.unset` + +**_Optional_** + +- `value` (Boolean) + + + +### Nested Schema for `spec.config.function.hpa` + +**_Optional_** + +- `enabled` (Block List, Max: 1) Enable HPA for the function deployment (see [below for nested schema](#nestedblock--spec--config--function--hpa--enabled)) +- `min_replicas` (Number) Minimum number of replicas for HPA +- `max_replicas` (Number) Maximum number of replicas for HPA +- `resource_metrics` (Block List) Resource metric sources (CPU/Memory) for autoscaling; each entry becomes a MetricSpec of type Resource (see [below for nested schema](#nestedblock--spec--config--function--hpa--resource_metrics)) + + + +### Nested Schema for `spec.config.function.hpa.enabled` + +**_Optional_** + +- `value` (Boolean) + + + +### Nested Schema for `spec.config.function.hpa.resource_metrics` + +**_Optional_** + +- `name` (String) Name of the resource (e.g. "cpu", "memory") +- `target` (Block List, Max: 1) Target value for the metric (see [below for nested schema](#nestedblock--spec--config--function--hpa--resource_metrics--target)) + + + +### Nested Schema for `spec.config.function.hpa.resource_metrics.target` + +**_Optional_** + +- `type` (String) Metric target type: "Utilization", "Value", or "AverageValue" +- `value` (String) Target value of the metric (Kubernetes quantity string, e.g. "500m") +- `average_value` (String) Target average value of the metric across pods (Kubernetes quantity string) +- `average_utilization` (Number) Target average utilization as a percentage of the requested resource (e.g. 80 for 80%) + ### Nested Schema for `spec.config.function.skip_build` diff --git a/examples/resources/rafay_workflow_handler/resource.tf b/examples/resources/rafay_workflow_handler/resource.tf index bb171dd3..dad70048 100644 --- a/examples/resources/rafay_workflow_handler/resource.tf +++ b/examples/resources/rafay_workflow_handler/resource.tf @@ -112,4 +112,58 @@ resource "rafay_workflow_handler" "workflow_handler" { } } } +} + +# Example workflow handler with function driver, resources and HPA +resource "rafay_workflow_handler" "workflow_handler_function" { + metadata { + name = "${var.name}-function" + project = var.project + } + spec { + config { + type = "function" + timeout_seconds = 100 + max_retry_count = 3 + function { + name = "example-function" + image = "my-registry.io/example-function:latest" + skip_build { + value = true + } + num_replicas = 2 + resources { + requests { + cpu = "500m" + memory = "256Mi" + } + limits { + cpu = "1" + memory = "512Mi" + } + } + hpa { + enabled { + value = true + } + min_replicas = 1 + max_replicas = 10 + resource_metrics { + name = "cpu" + target { + type = "Utilization" + average_utilization = 80 + } + } + resource_metrics { + name = "memory" + target { + type = "AverageValue" + average_value = "512Mi" + } + } + } + } + } + } } \ No newline at end of file diff --git a/go.mod b/go.mod index 4b9cf863..0df15b0f 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.24.0 require ( github.com/RafaySystems/edge-common v1.24.1-0.20260210092640-f8449fb6a28f - github.com/RafaySystems/rafay-common v1.29.1-rc2.0.20260212054829-95edf0ffe8c1 + github.com/RafaySystems/rafay-common v1.29.1-rc2.0.20260211050557-17988cb8bba1 github.com/RafaySystems/rctl v1.29.1-0.20251223105806-82bdefd9f2be github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/go-yaml/yaml v2.1.0+incompatible @@ -28,7 +28,7 @@ require ( github.com/IBM/sarama v1.43.2 // indirect github.com/Masterminds/sprig v2.22.0+incompatible // indirect github.com/OneOfOne/xxhash v1.2.8 // indirect - github.com/RafaySystems/eaas-playground/proto v0.0.0-20251008151511-bf1cf36fe5bd // indirect + github.com/RafaySystems/eaas-playground/proto v0.0.0-20260209100653-efc264d6164b // indirect github.com/RafaySystems/paas-common v0.0.0-20250519095800-e92646adcd6e // indirect github.com/agnivade/levenshtein v1.1.1 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect diff --git a/go.sum b/go.sum index f9d835f1..8f0a3e75 100644 --- a/go.sum +++ b/go.sum @@ -18,14 +18,14 @@ github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8 github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/ProtonMail/go-crypto v1.1.0-alpha.2 h1:bkyFVUP+ROOARdgCiJzNQo2V2kiB97LyUpzH9P6Hrlg= github.com/ProtonMail/go-crypto v1.1.0-alpha.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= -github.com/RafaySystems/eaas-playground/proto v0.0.0-20251008151511-bf1cf36fe5bd h1:50smMqzYlYN0e7bgVLGBIX7Y1V9fUE+yUhkCoQIVgD4= -github.com/RafaySystems/eaas-playground/proto v0.0.0-20251008151511-bf1cf36fe5bd/go.mod h1:lAle7/tiVlakfiXdJKnsf6qNdLkoWS712T4pAXytSOQ= +github.com/RafaySystems/eaas-playground/proto v0.0.0-20260209100653-efc264d6164b h1:i5ukC9tueAvpXWy8jK+0UQIgd3tGBji3uteaPLA/SgI= +github.com/RafaySystems/eaas-playground/proto v0.0.0-20260209100653-efc264d6164b/go.mod h1:lAle7/tiVlakfiXdJKnsf6qNdLkoWS712T4pAXytSOQ= github.com/RafaySystems/edge-common v1.24.1-0.20260210092640-f8449fb6a28f h1:Gdp6e7EPmVSFXGW6MUyf9e+avbSKrz+8CJv98CWdS9c= github.com/RafaySystems/edge-common v1.24.1-0.20260210092640-f8449fb6a28f/go.mod h1:tbd1Z34RfnJ3umcDjf478+dh7hJ66ict3wra7YbQmiA= github.com/RafaySystems/paas-common v0.0.0-20250519095800-e92646adcd6e h1:SrznY+xkaQ4BkX0HXGa0upur7RAtR6UsTsdMNEgBreY= github.com/RafaySystems/paas-common v0.0.0-20250519095800-e92646adcd6e/go.mod h1:vDR0S28Q+hwE/5wO0L/Ohn9CZwaI/o2QuPupJ2iLI7k= -github.com/RafaySystems/rafay-common v1.29.1-rc2.0.20260212054829-95edf0ffe8c1 h1:fNHNkwU6aVzayQ7fi8HudbUQ3+22dVloYkbA53NrNec= -github.com/RafaySystems/rafay-common v1.29.1-rc2.0.20260212054829-95edf0ffe8c1/go.mod h1:oW9Qjzh48Sq9pBYPYbmzAC73PwehEwEI9PBVpdX9OaI= +github.com/RafaySystems/rafay-common v1.29.1-rc2.0.20260211050557-17988cb8bba1 h1:u8r3Ljr7hpx4PDAcjpI0PbjL2x9mY+Bx61RlIKPT2t8= +github.com/RafaySystems/rafay-common v1.29.1-rc2.0.20260211050557-17988cb8bba1/go.mod h1:odgdwYI8Syng0UOhwSgAchFp8yE3lZ3h8w3dqEq9AC0= github.com/RafaySystems/rctl v1.29.1-0.20251223105806-82bdefd9f2be h1:J8aFJgB2guG+pyRmnRGA8yoPM08qBS/8juNGMoltb9U= github.com/RafaySystems/rctl v1.29.1-0.20251223105806-82bdefd9f2be/go.mod h1:Ja881zvwJU7FeufWgAYQdXhOTYhonzwOy81JBTLVXwQ= github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv2QzDdQ= diff --git a/rafay/resource_driver.go b/rafay/resource_driver.go index 867156f6..a6a6d450 100644 --- a/rafay/resource_driver.go +++ b/rafay/resource_driver.go @@ -280,6 +280,18 @@ func expandDriverConfig(p []any) *eaaspb.DriverConfig { driverConfig.Http = expandWorkflowHandlerHttpConfig(v) } + if v, ok := in["function"].([]any); ok && len(v) > 0 { + driverConfig.Function = expandWorkflowHandlerFunctionConfig(v) + } + + if v, ok := in["polling_config"].([]any); ok && len(v) > 0 { + driverConfig.PollingConfig = expandPollingConfig(v) + } + + if h, ok := in["timeout_seconds"].(int); ok { + driverConfig.TimeoutSeconds = int64(h) + } + return &driverConfig } diff --git a/rafay/resource_workflow_handler.go b/rafay/resource_workflow_handler.go index 7028f4e4..535b7d40 100644 --- a/rafay/resource_workflow_handler.go +++ b/rafay/resource_workflow_handler.go @@ -17,7 +17,9 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "google.golang.org/protobuf/types/known/structpb" + autoscalingv2 "k8s.io/api/autoscaling/v2" corev1 "k8s.io/api/core/v1" + k8sresource "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -148,7 +150,6 @@ func resourceWorkflowHandlerRead(ctx context.Context, d *schema.ResourceData, m return diag.FromErr(err) } return diags - } func resourceWorkflowHandlerUpdate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics { @@ -187,7 +188,6 @@ func resourceWorkflowHandlerDelete(ctx context.Context, d *schema.ResourceData, Name: cc.Metadata.Name, Project: cc.Metadata.Project, }) - if err != nil { return diag.FromErr(err) } @@ -820,9 +820,134 @@ func expandWorkflowHandlerFunctionConfig(p []any) *eaaspb.FunctionDriverConfig { fdc.InactivityTimeoutSeconds = int64(inactivityTimeoutSeconds) } + if resources, ok := in["resources"].([]any); ok && len(resources) > 0 { + fdc.Resources = expandDriverResources(resources) + } + + if hpa, ok := in["hpa"].([]any); ok && len(hpa) > 0 { + fdc.Hpa = expandFunctionHPAConfig(hpa) + } + return &fdc } +func expandDriverResourceValues(p []any) *eaaspb.DriverResourceValues { + if len(p) == 0 || p[0] == nil { + return nil + } + in := p[0].(map[string]any) + drv := &eaaspb.DriverResourceValues{} + if cpu, ok := in["cpu"].(string); ok && len(cpu) > 0 { + drv.Cpu = cpu + } + if memory, ok := in["memory"].(string); ok && len(memory) > 0 { + drv.Memory = memory + } + return drv +} + +func expandDriverResources(p []any) *eaaspb.DriverResources { + if len(p) == 0 || p[0] == nil { + return nil + } + in := p[0].(map[string]any) + dr := &eaaspb.DriverResources{} + if requests, ok := in["requests"].([]any); ok && len(requests) > 0 { + dr.Requests = expandDriverResourceValues(requests) + } + if limits, ok := in["limits"].([]any); ok && len(limits) > 0 { + dr.Limits = expandDriverResourceValues(limits) + } + if unset, ok := in["unset"].([]any); ok && len(unset) > 0 { + dr.Unset = expandBoolValue(unset) + } + return dr +} + +func expandFunctionHPAConfig(p []any) *eaaspb.FunctionHPAConfig { + if len(p) == 0 || p[0] == nil { + return nil + } + in := p[0].(map[string]any) + hpa := &eaaspb.FunctionHPAConfig{} + if enabled, ok := in["enabled"].([]any); ok && len(enabled) > 0 { + hpa.Enabled = expandBoolValue(enabled) + } + if minReplicas, ok := in["min_replicas"].(int); ok && minReplicas >= 0 { + hpa.MinReplicas = uint32(minReplicas) + } + if maxReplicas, ok := in["max_replicas"].(int); ok && maxReplicas >= 0 { + hpa.MaxReplicas = uint32(maxReplicas) + } + if resourceMetrics, ok := in["resource_metrics"].([]any); ok && len(resourceMetrics) > 0 { + hpa.ResourceMetrics = expandResourceMetrics(resourceMetrics) + } + return hpa +} + +func expandMetricTarget(in map[string]any) *autoscalingv2.MetricTarget { + if in == nil { + return nil + } + mt := &autoscalingv2.MetricTarget{} + if t, ok := in["type"].(string); ok && len(t) > 0 { + mt.Type = autoscalingv2.MetricTargetType(t) + } + if v, ok := in["value"].(string); ok && len(v) > 0 { + q, err := k8sresource.ParseQuantity(v) + if err == nil { + mt.Value = &q + } + } + if av, ok := in["average_value"].(string); ok && len(av) > 0 { + q, err := k8sresource.ParseQuantity(av) + if err == nil { + mt.AverageValue = &q + } + } + if au, ok := in["average_utilization"].(int); ok { + au32 := int32(au) + mt.AverageUtilization = &au32 + } + return mt +} + +func expandResourceMetricSource(in map[string]any) *autoscalingv2.ResourceMetricSource { + if in == nil { + return nil + } + rms := &autoscalingv2.ResourceMetricSource{} + if name, ok := in["name"].(string); ok && len(name) > 0 { + rms.Name = corev1.ResourceName(name) + } + if target, ok := in["target"].([]any); ok && len(target) > 0 { + if tmap, ok := target[0].(map[string]any); ok { + if mt := expandMetricTarget(tmap); mt != nil { + rms.Target = *mt + } + } + } + return rms +} + +func expandResourceMetrics(p []any) []*autoscalingv2.ResourceMetricSource { + if len(p) == 0 { + return nil + } + out := make([]*autoscalingv2.ResourceMetricSource, 0, len(p)) + for i := range p { + if p[i] == nil { + continue + } + if m, ok := p[i].(map[string]any); ok { + if rms := expandResourceMetricSource(m); rms != nil { + out = append(out, rms) + } + } + } + return out +} + func expandWorkflowHandlerOutputs(p string) (*structpb.Struct, error) { if len(p) == 0 { return nil, nil @@ -1473,9 +1598,101 @@ func flattenWorkflowHandlerFunctionConfig(in *eaaspb.FunctionDriverConfig, p []a v, _ = obj["image_pull_credentials"].([]any) obj["image_pull_credentials"] = flattenImagePullCredentials(in.ImagePullCredentials, v) + v, _ = obj["resources"].([]any) + obj["resources"] = flattenDriverResources(in.Resources, v) + + v, _ = obj["hpa"].([]any) + obj["hpa"] = flattenFunctionHPAConfig(in.Hpa, v) + + return []any{obj} +} + +func flattenDriverResourceValues(in *eaaspb.DriverResourceValues, p []any) []any { + if in == nil { + return nil + } + obj := make(map[string]any) + if len(p) != 0 && p[0] != nil { + obj = p[0].(map[string]any) + } + obj["cpu"] = in.Cpu + obj["memory"] = in.Memory + return []any{obj} +} + +func flattenDriverResources(in *eaaspb.DriverResources, p []any) []any { + if in == nil { + return nil + } + obj := make(map[string]any) + if len(p) != 0 && p[0] != nil { + obj = p[0].(map[string]any) + } + var v []any + v, _ = obj["requests"].([]any) + obj["requests"] = flattenDriverResourceValues(in.Requests, v) + v, _ = obj["limits"].([]any) + obj["limits"] = flattenDriverResourceValues(in.Limits, v) + obj["unset"] = flattenBoolValue(in.Unset) return []any{obj} } +func flattenFunctionHPAConfig(in *eaaspb.FunctionHPAConfig, p []any) []any { + if in == nil { + return nil + } + obj := make(map[string]any) + if len(p) != 0 && p[0] != nil { + obj = p[0].(map[string]any) + } + obj["enabled"] = flattenBoolValue(in.Enabled) + obj["min_replicas"] = in.MinReplicas + obj["max_replicas"] = in.MaxReplicas + var v []any + v, _ = obj["resource_metrics"].([]any) + obj["resource_metrics"] = flattenResourceMetrics(in.ResourceMetrics, v) + return []any{obj} +} + +func flattenMetricTarget(in *autoscalingv2.MetricTarget) map[string]any { + if in == nil { + return nil + } + obj := make(map[string]any) + obj["type"] = string(in.Type) + if in.Value != nil { + obj["value"] = in.Value.String() + } + if in.AverageValue != nil { + obj["average_value"] = in.AverageValue.String() + } + if in.AverageUtilization != nil { + obj["average_utilization"] = int(*in.AverageUtilization) + } + return obj +} + +func flattenResourceMetricSource(in *autoscalingv2.ResourceMetricSource) map[string]any { + if in == nil { + return nil + } + obj := make(map[string]any) + obj["name"] = string(in.Name) + obj["target"] = []any{flattenMetricTarget(&in.Target)} + return obj +} + +func flattenResourceMetrics(in []*autoscalingv2.ResourceMetricSource, p []any) []any { + if len(in) == 0 { + return nil + } + out := make([]any, len(in)) + for i, rms := range in { + out[i] = flattenResourceMetricSource(rms) + } + return out +} + func flattenContainerWorkflowHandlerVolumeOptions(input []*eaaspb.ContainerDriverVolumeOptions, p []any) []any { if len(input) == 0 { return nil