From 878d02161da893e6d61bfcecf27ac6f707e089db Mon Sep 17 00:00:00 2001 From: jackofallops Date: Thu, 7 Sep 2023 13:55:15 +0200 Subject: [PATCH 1/5] add schemas and structs for scale rules types --- .../containerapps/helpers/container_apps.go | 203 +++++++++++++++++- 1 file changed, 198 insertions(+), 5 deletions(-) diff --git a/internal/services/containerapps/helpers/container_apps.go b/internal/services/containerapps/helpers/container_apps.go index f088b5508df8..95b28a3f67e3 100644 --- a/internal/services/containerapps/helpers/container_apps.go +++ b/internal/services/containerapps/helpers/container_apps.go @@ -669,11 +669,15 @@ func ContainerAppEnvironmentDaprMetadataSchema() *pluginsdk.Schema { } type ContainerTemplate struct { - Containers []Container `tfschema:"container"` - Suffix string `tfschema:"revision_suffix"` - MinReplicas int `tfschema:"min_replicas"` - MaxReplicas int `tfschema:"max_replicas"` - Volumes []ContainerVolume `tfschema:"volume"` + Containers []Container `tfschema:"container"` + Suffix string `tfschema:"revision_suffix"` + MinReplicas int `tfschema:"min_replicas"` + MaxReplicas int `tfschema:"max_replicas"` + AzureQueueScaleRules []AzureQueueScaleRule `tfschema:"azure_queue_scale_rule"` + CustomScaleRules []CustomScaleRule `tfschema:"custom_scale_rule"` + HTTPScaleRules []HTTPScaleRule `tfschema:"http_scale_rule"` + TCPScaleRules []TCPScaleRule `tfschema:"tcp_scale_rule"` + Volumes []ContainerVolume `tfschema:"volume"` } func ContainerTemplateSchema() *pluginsdk.Schema { @@ -701,6 +705,14 @@ func ContainerTemplateSchema() *pluginsdk.Schema { Description: "The maximum number of replicas for this container.", }, + "azure_queue_scale_rule": AzureQueueScaleRuleSchema(), + + "custom_scale_rule": CustomScaleRuleSchema(), + + "http_scale_rule": HTTPScaleRuleSchema(), + + "tcp_scale_rule": TCPScaleRuleSchema(), + "volume": ContainerVolumeSchema(), "revision_suffix": { @@ -2347,3 +2359,184 @@ func ContainerAppProbesRemoved(metadata sdk.ResourceMetaData) bool { return !(hasLiveness || hasReadiness || hasStartup) } + +type AzureQueueScaleRule struct { + Name string `tfschema:"name"` + QueueLength string `tfschema:"queue_length"` + QueueName string `tfschema:"queue_name"` + AuthSecretRef string `tfschema:"authentication_secret_reference"` + AuthTriggerParam string `tfschema:"authentication_trigger_parameter"` +} + +func AzureQueueScaleRuleSchema() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "queue_length": { + Type: pluginsdk.TypeInt, + Required: true, + ValidateFunc: validation.IntAtLeast(1), + }, + + "queue_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "authentication_secret_reference": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validate.SecretName, + }, + + "authentication_trigger_parameter": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + } +} + +type CustomScaleRule struct { + Metadata map[string]string `tfschema:"metadata"` + CustomRuleType string `tfschema:"custom_rule_type"` + AuthSecretRef string `tfschema:"authentication_secret_reference"` + AuthTriggerParam string `tfschema:"authentication_trigger_parameter"` +} + +func CustomScaleRuleSchema() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "metadata": { + Type: pluginsdk.TypeMap, + Required: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "custom_rule_type": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, // Note - this can be any KEDA compatible source in a user's environment + }, + + "authentication_secret_reference": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validate.SecretName, + }, + + "authentication_trigger_parameter": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + } +} + +type HTTPScaleRule struct { + Metadata map[string]string `tfschema:"metadata"` + AuthSecretRef string `tfschema:"authentication_secret_reference"` + AuthTriggerParam string `tfschema:"authentication_trigger_parameter"` +} + +func HTTPScaleRuleSchema() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "metadata": { + Type: pluginsdk.TypeMap, + Required: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "authentication_secret_reference": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validate.SecretName, + }, + + "authentication_trigger_parameter": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + } +} + +type TCPScaleRule struct { + Metadata map[string]string `tfschema:"metadata"` + AuthSecretRef string `tfschema:"authentication_secret_reference"` + AuthTriggerParam string `tfschema:"authentication_trigger_parameter"` +} + +func TCPScaleRuleSchema() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "metadata": { + Type: pluginsdk.TypeMap, + Required: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + }, + + "authentication_secret_reference": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validate.SecretName, + }, + + "authentication_trigger_parameter": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + } +} From f4fd1d86ed263651f3ab9b0e532f9abb0416568c Mon Sep 17 00:00:00 2001 From: jackofallops Date: Thu, 14 Sep 2023 11:54:03 +0200 Subject: [PATCH 2/5] add scale rules with auth --- .../container_app_resource_test.go | 199 +++++++++++++ .../containerapps/helpers/container_apps.go | 275 ++++++++++++++++-- 2 files changed, 450 insertions(+), 24 deletions(-) diff --git a/internal/services/containerapps/container_app_resource_test.go b/internal/services/containerapps/container_app_resource_test.go index ade9bad6aac0..8dd2e7e271fb 100644 --- a/internal/services/containerapps/container_app_resource_test.go +++ b/internal/services/containerapps/container_app_resource_test.go @@ -303,6 +303,57 @@ func TestAccContainerAppResource_secretRemoveWithAddShouldFail(t *testing.T) { }) } +func TestAccContainerAppResource_scaleRules(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_container_app", "test") + r := ContainerAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.scaleRules(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccContainerAppResource_scaleRulesUpdate(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_container_app", "test") + r := ContainerAppResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.scaleRules(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.scaleRulesUpdate(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basicWithRetainedSecret(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func (r ContainerAppResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := containerapps.ParseContainerAppID(state.ID) if err != nil { @@ -342,6 +393,33 @@ resource "azurerm_container_app" "test" { `, r.template(data), data.RandomInteger) } +func (r ContainerAppResource) basicWithRetainedSecret(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_container_app" "test" { + name = "acctest-capp-%[2]d" + resource_group_name = azurerm_resource_group.test.name + container_app_environment_id = azurerm_container_app_environment.test.id + revision_mode = "Single" + + secret { + name = "queue-auth-secret" + value = "VGhpcyBJcyBOb3QgQSBHb29kIFBhc3N3b3JkCg==" + } + + template { + container { + name = "acctest-cont-%[2]d" + image = "jackofallops/azure-containerapps-python-acctest:v0.0.1" + cpu = 0.25 + memory = "0.5Gi" + } + } +} +`, r.template(data), data.RandomInteger) +} + func (r ContainerAppResource) withSystemIdentity(data acceptance.TestData) string { return fmt.Sprintf(` %s @@ -1325,6 +1403,127 @@ resource "azurerm_container_app" "test" { `, r.templatePlusExtras(data), data.RandomInteger, revisionSuffix) } +func (r ContainerAppResource) scaleRules(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_container_app" "test" { + name = "acctest-capp-%[2]d" + resource_group_name = azurerm_resource_group.test.name + container_app_environment_id = azurerm_container_app_environment.test.id + revision_mode = "Single" + + secret { + name = "queue-auth-secret" + value = "VGhpcyBJcyBOb3QgQSBHb29kIFBhc3N3b3JkCg==" + } + + template { + container { + name = "acctest-cont-%[2]d" + image = "jackofallops/azure-containerapps-python-acctest:v0.0.1" + cpu = 0.25 + memory = "0.5Gi" + } + + azure_queue_scale_rule { + name = "azq-1" + queue_name = "foo" + queue_length = 10 + + authentication_secret_reference = "queue-auth-secret" + authentication_trigger_parameter = "blah" + } + + custom_scale_rule { + name = "csr-1" + custom_rule_type = "azure-monitor" + metadata = { + foo = "bar" + } + } + + http_scale_rule { + name = "http-1" + concurrent_requests = "100" + } + + tcp_scale_rule { + name = "tcp-1" + concurrent_requests = "1000" + } + } +} +`, r.template(data), data.RandomInteger) +} + +func (r ContainerAppResource) scaleRulesUpdate(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_container_app" "test" { + name = "acctest-capp-%[2]d" + resource_group_name = azurerm_resource_group.test.name + container_app_environment_id = azurerm_container_app_environment.test.id + revision_mode = "Single" + + secret { + name = "queue-auth-secret" + value = "VGhpcyBJcyBOb3QgQSBHb29kIFBhc3N3b3JkCg==" + } + + template { + container { + name = "acctest-cont-%[2]d" + image = "jackofallops/azure-containerapps-python-acctest:v0.0.1" + cpu = 0.25 + memory = "0.5Gi" + } + + azure_queue_scale_rule { + name = "azq-1" + queue_name = "foo" + queue_length = 10 + + authentication_secret_reference = "queue-auth-secret" + authentication_trigger_parameter = "blah" + } + + azure_queue_scale_rule { + name = "azq-2" + queue_name = "bar" + queue_length = 20 + + authentication_secret_reference = "queue-auth-secret" + authentication_trigger_parameter = "blah2" + } + + custom_scale_rule { + name = "csr-1" + custom_rule_type = "rabbitmq" + metadata = { + foo = "bar" + } + } + + http_scale_rule { + name = "http-1" + concurrent_requests = "200" + + authentication_secret_reference = "queue-auth-secret" + } + + tcp_scale_rule { + name = "tcp-1" + concurrent_requests = "1000" + + authentication_secret_reference = "queue-auth-secret" + } + } +} +`, r.template(data), data.RandomInteger) +} + func (ContainerAppResource) template(data acceptance.TestData) string { return ContainerAppEnvironmentResource{}.basic(data) } diff --git a/internal/services/containerapps/helpers/container_apps.go b/internal/services/containerapps/helpers/container_apps.go index 95b28a3f67e3..3cacab4b2e9f 100644 --- a/internal/services/containerapps/helpers/container_apps.go +++ b/internal/services/containerapps/helpers/container_apps.go @@ -783,6 +783,14 @@ func ExpandContainerAppTemplate(input []ContainerTemplate, metadata sdk.Resource template.Scale.MinReplicas = pointer.To(int64(config.MinReplicas)) } + if rules := config.expandContainerAppScaleRules(); len(rules) != 0 { + if template.Scale == nil { + template.Scale = &containerapps.Scale{} + } + + template.Scale.Rules = pointer.To(rules) + } + if config.Suffix != "" { if metadata.ResourceData.HasChange("template.0.revision_suffix") { template.RevisionSuffix = pointer.To(config.Suffix) @@ -805,6 +813,7 @@ func FlattenContainerAppTemplate(input *containerapps.Template) []ContainerTempl if scale := input.Scale; scale != nil { result.MaxReplicas = int(pointer.From(scale.MaxReplicas)) result.MinReplicas = int(pointer.From(scale.MinReplicas)) + result.flattenContainerAppScaleRules(scale.Rules) } return []ContainerTemplate{result} @@ -2362,7 +2371,7 @@ func ContainerAppProbesRemoved(metadata sdk.ResourceMetaData) bool { type AzureQueueScaleRule struct { Name string `tfschema:"name"` - QueueLength string `tfschema:"queue_length"` + QueueLength int `tfschema:"queue_length"` QueueName string `tfschema:"queue_name"` AuthSecretRef string `tfschema:"authentication_secret_reference"` AuthTriggerParam string `tfschema:"authentication_trigger_parameter"` @@ -2394,13 +2403,13 @@ func AzureQueueScaleRuleSchema() *pluginsdk.Schema { "authentication_secret_reference": { Type: pluginsdk.TypeString, - Optional: true, + Required: true, ValidateFunc: validate.SecretName, }, "authentication_trigger_parameter": { Type: pluginsdk.TypeString, - Optional: true, + Required: true, ValidateFunc: validation.StringIsNotEmpty, }, }, @@ -2409,6 +2418,7 @@ func AzureQueueScaleRuleSchema() *pluginsdk.Schema { } type CustomScaleRule struct { + Name string `tfschema:"name"` Metadata map[string]string `tfschema:"metadata"` CustomRuleType string `tfschema:"custom_rule_type"` AuthSecretRef string `tfschema:"authentication_secret_reference"` @@ -2436,9 +2446,22 @@ func CustomScaleRuleSchema() *pluginsdk.Schema { }, "custom_rule_type": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, // Note - this can be any KEDA compatible source in a user's environment + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "activemq", "artemis-queue", "kafka", "pulsar", "aws-cloudwatch", + "aws-dynamodb", "aws-dynamodb-streams", "aws-kinesis-stream", "aws-sqs-queue", + "azure-app-insights", "azure-blob", "azure-data-explorer", "azure-eventhub", + "azure-log-analytics", "azure-monitor", "azure-pipelines", "azure-servicebus", + "azure-queue", "cassandra", "cpu", "cron", "datadog", "elasticsearch", "external", + "external-push", "gcp-stackdriver", "gcp-storage", "gcp-pubsub", "graphite", "http", + "huawei-cloudeye", "ibmmq", "influxdb", "kubernetes-workload", "liiklus", "memory", + "metrics-api", "mongodb", "mssql", "mysql", "nats-jetstream", "stan", "tcp", "new-relic", + "openstack-metric", "openstack-swift", "postgresql", "predictkube", "prometheus", + "rabbitmq", "redis", "redis-cluster", "redis-sentinel", "redis-streams", + "redis-cluster-streams", "redis-sentinel-streams", "selenium-grid", + "solace-event-queue", "github-runner", + }, false), // Note - this can be any KEDA compatible source in a user's environment }, "authentication_secret_reference": { @@ -2458,9 +2481,10 @@ func CustomScaleRuleSchema() *pluginsdk.Schema { } type HTTPScaleRule struct { - Metadata map[string]string `tfschema:"metadata"` - AuthSecretRef string `tfschema:"authentication_secret_reference"` - AuthTriggerParam string `tfschema:"authentication_trigger_parameter"` + Name string `tfschema:"name"` + ConcurrentRequests string `tfschema:"concurrent_requests"` + AuthSecretRef string `tfschema:"authentication_secret_reference"` + AuthTriggerParam string `tfschema:"authentication_trigger_parameter"` } func HTTPScaleRuleSchema() *pluginsdk.Schema { @@ -2475,12 +2499,10 @@ func HTTPScaleRuleSchema() *pluginsdk.Schema { ValidateFunc: validation.StringIsNotEmpty, }, - "metadata": { - Type: pluginsdk.TypeMap, - Required: true, - Elem: &pluginsdk.Schema{ - Type: pluginsdk.TypeString, - }, + "concurrent_requests": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: nil, // TODO - str value for int }, "authentication_secret_reference": { @@ -2500,9 +2522,10 @@ func HTTPScaleRuleSchema() *pluginsdk.Schema { } type TCPScaleRule struct { - Metadata map[string]string `tfschema:"metadata"` - AuthSecretRef string `tfschema:"authentication_secret_reference"` - AuthTriggerParam string `tfschema:"authentication_trigger_parameter"` + Name string `tfschema:"name"` + ConcurrentRequests string `tfschema:"concurrent_requests"` + AuthSecretRef string `tfschema:"authentication_secret_reference"` + AuthTriggerParam string `tfschema:"authentication_trigger_parameter"` } func TCPScaleRuleSchema() *pluginsdk.Schema { @@ -2517,12 +2540,10 @@ func TCPScaleRuleSchema() *pluginsdk.Schema { ValidateFunc: validation.StringIsNotEmpty, }, - "metadata": { - Type: pluginsdk.TypeMap, - Required: true, - Elem: &pluginsdk.Schema{ - Type: pluginsdk.TypeString, - }, + "concurrent_requests": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: nil, // TODO Validation for int as str }, "authentication_secret_reference": { @@ -2540,3 +2561,209 @@ func TCPScaleRuleSchema() *pluginsdk.Schema { }, } } + +func (c *ContainerTemplate) expandContainerAppScaleRules() []containerapps.ScaleRule { + if len(c.AzureQueueScaleRules) == 0 && len(c.CustomScaleRules) == 0 && len(c.HTTPScaleRules) == 0 && len(c.TCPScaleRules) == 0 { + return nil + } + result := make([]containerapps.ScaleRule, 0) + for _, v := range c.AzureQueueScaleRules { + r := containerapps.ScaleRule{ + Name: pointer.To(v.Name), + AzureQueue: &containerapps.QueueScaleRule{ + QueueLength: pointer.To(int64(v.QueueLength)), + QueueName: pointer.To(v.QueueName), + Auth: &[]containerapps.ScaleRuleAuth{}, + }, + } + + if v.AuthSecretRef != "" || v.AuthTriggerParam != "" { + auth := containerapps.ScaleRuleAuth{} + if v.AuthTriggerParam != "" { + auth.TriggerParameter = pointer.To(v.AuthTriggerParam) + } + + if v.AuthSecretRef != "" { + auth.SecretRef = pointer.To(v.AuthSecretRef) + } + + r.AzureQueue.Auth = pointer.To([]containerapps.ScaleRuleAuth{auth}) + } + result = append(result, r) + } + + for _, v := range c.CustomScaleRules { + r := containerapps.ScaleRule{ + Name: pointer.To(v.Name), + Custom: &containerapps.CustomScaleRule{ + Metadata: &v.Metadata, + Type: pointer.To(v.CustomRuleType), + }, + } + + if v.AuthSecretRef != "" || v.AuthTriggerParam != "" { + auth := containerapps.ScaleRuleAuth{} + if v.AuthTriggerParam != "" { + auth.TriggerParameter = pointer.To(v.AuthTriggerParam) + } + + if v.AuthSecretRef != "" { + auth.SecretRef = pointer.To(v.AuthSecretRef) + } + + r.Custom.Auth = pointer.To([]containerapps.ScaleRuleAuth{auth}) + } + result = append(result, r) + } + + for _, v := range c.HTTPScaleRules { + metaData := make(map[string]string, 0) + metaData["concurrentRequests"] = v.ConcurrentRequests + r := containerapps.ScaleRule{ + Name: pointer.To(v.Name), + HTTP: &containerapps.HTTPScaleRule{ + Metadata: pointer.To(metaData), + }, + } + + if v.AuthSecretRef != "" || v.AuthTriggerParam != "" { + auth := containerapps.ScaleRuleAuth{} + if v.AuthTriggerParam != "" { + auth.TriggerParameter = pointer.To(v.AuthTriggerParam) + } + + if v.AuthSecretRef != "" { + auth.SecretRef = pointer.To(v.AuthSecretRef) + } + + r.HTTP.Auth = pointer.To([]containerapps.ScaleRuleAuth{auth}) + } + result = append(result, r) + } + + for _, v := range c.TCPScaleRules { + metaData := make(map[string]string, 0) + metaData["concurrentRequests"] = v.ConcurrentRequests + r := containerapps.ScaleRule{ + Name: pointer.To(v.Name), + Tcp: &containerapps.TcpScaleRule{ + Metadata: pointer.To(metaData), + }, + } + + if v.AuthSecretRef != "" || v.AuthTriggerParam != "" { + auth := containerapps.ScaleRuleAuth{} + if v.AuthTriggerParam != "" { + auth.TriggerParameter = pointer.To(v.AuthTriggerParam) + } + + if v.AuthSecretRef != "" { + auth.SecretRef = pointer.To(v.AuthSecretRef) + } + + r.Tcp.Auth = pointer.To([]containerapps.ScaleRuleAuth{auth}) + } + result = append(result, r) + } + + return result +} + +func (c *ContainerTemplate) flattenContainerAppScaleRules(input *[]containerapps.ScaleRule) { + if input != nil && len(*input) != 0 { + rules := *input + azureQueueScaleRules := make([]AzureQueueScaleRule, 0) + customScaleRules := make([]CustomScaleRule, 0) + httpScaleRules := make([]HTTPScaleRule, 0) + tcpScaleRules := make([]TCPScaleRule, 0) + for _, v := range rules { + if q := v.AzureQueue; q != nil { + rule := AzureQueueScaleRule{ + Name: pointer.From(v.Name), + QueueLength: int(pointer.From(q.QueueLength)), + QueueName: pointer.From(q.QueueName), + } + + if q.Auth != nil && len(*q.Auth) != 0 { + auth := (*q.Auth)[0] + rule.AuthSecretRef = pointer.From(auth.SecretRef) + rule.AuthTriggerParam = pointer.From(auth.TriggerParameter) + } + + azureQueueScaleRules = append(azureQueueScaleRules, rule) + continue + } + + if r := v.Custom; r != nil { + rule := CustomScaleRule{ + Name: pointer.From(v.Name), + Metadata: pointer.From(r.Metadata), + CustomRuleType: pointer.From(r.Type), + AuthSecretRef: "", + AuthTriggerParam: "", + } + + if r.Auth != nil && len(*r.Auth) != 0 { + auth := (*r.Auth)[0] + rule.AuthSecretRef = pointer.From(auth.SecretRef) + rule.AuthTriggerParam = pointer.From(auth.TriggerParameter) + } + + customScaleRules = append(customScaleRules, rule) + continue + } + + if r := v.HTTP; r != nil { + metaData := pointer.From(r.Metadata) + concurrentReqs := "" + + if m, ok := metaData["concurrentRequests"]; ok { + concurrentReqs = m + } + + rule := HTTPScaleRule{ + Name: pointer.From(v.Name), + ConcurrentRequests: concurrentReqs, + AuthSecretRef: "", + AuthTriggerParam: "", + } + + if r.Auth != nil && len(*r.Auth) != 0 { + auth := (*r.Auth)[0] + rule.AuthSecretRef = pointer.From(auth.SecretRef) + rule.AuthTriggerParam = pointer.From(auth.TriggerParameter) + } + + httpScaleRules = append(httpScaleRules, rule) + continue + } + + if r := v.Tcp; r != nil { + metaData := pointer.From(r.Metadata) + concurrentReqs := "" + + if m, ok := metaData["concurrentRequests"]; ok { + concurrentReqs = m + } + + rule := TCPScaleRule{ + Name: pointer.From(v.Name), + ConcurrentRequests: concurrentReqs, + } + + if r.Auth != nil && len(*r.Auth) != 0 { + auth := (*r.Auth)[0] + rule.AuthSecretRef = pointer.From(auth.SecretRef) + rule.AuthTriggerParam = pointer.From(auth.TriggerParameter) + } + tcpScaleRules = append(tcpScaleRules, rule) + continue + } + } + + c.AzureQueueScaleRules = azureQueueScaleRules + c.CustomScaleRules = customScaleRules + c.HTTPScaleRules = httpScaleRules + c.TCPScaleRules = tcpScaleRules + } +} From ebd476d94b539534c4a1779f9e31d67c750896f5 Mon Sep 17 00:00:00 2001 From: jackofallops Date: Fri, 15 Sep 2023 11:40:31 +0200 Subject: [PATCH 3/5] rework to cater for multiple auth blocks --- .../container_app_resource_test.go | 34 +- .../containerapps/helpers/container_apps.go | 472 +++++++++++++----- 2 files changed, 384 insertions(+), 122 deletions(-) diff --git a/internal/services/containerapps/container_app_resource_test.go b/internal/services/containerapps/container_app_resource_test.go index 8dd2e7e271fb..e4a1e48a5266 100644 --- a/internal/services/containerapps/container_app_resource_test.go +++ b/internal/services/containerapps/container_app_resource_test.go @@ -1431,8 +1431,10 @@ resource "azurerm_container_app" "test" { queue_name = "foo" queue_length = 10 - authentication_secret_reference = "queue-auth-secret" - authentication_trigger_parameter = "blah" + authentication { + secret_name = "queue-auth-secret" + trigger_parameter = "password" + } } custom_scale_rule { @@ -1485,8 +1487,10 @@ resource "azurerm_container_app" "test" { queue_name = "foo" queue_length = 10 - authentication_secret_reference = "queue-auth-secret" - authentication_trigger_parameter = "blah" + authentication { + secret_name = "queue-auth-secret" + trigger_parameter = "password" + } } azure_queue_scale_rule { @@ -1494,30 +1498,44 @@ resource "azurerm_container_app" "test" { queue_name = "bar" queue_length = 20 - authentication_secret_reference = "queue-auth-secret" - authentication_trigger_parameter = "blah2" + authentication { + secret_name = "queue-auth-secret" + trigger_parameter = "another_password" + } } custom_scale_rule { name = "csr-1" custom_rule_type = "rabbitmq" + metadata = { foo = "bar" } + + authentication { + secret_name = "queue-auth-secret" + trigger_parameter = "password" + } } http_scale_rule { name = "http-1" concurrent_requests = "200" - authentication_secret_reference = "queue-auth-secret" + authentication { + secret_name = "queue-auth-secret" + trigger_parameter = "password" + } } tcp_scale_rule { name = "tcp-1" concurrent_requests = "1000" - authentication_secret_reference = "queue-auth-secret" + authentication { + secret_name = "queue-auth-secret" + trigger_parameter = "password" + } } } } diff --git a/internal/services/containerapps/helpers/container_apps.go b/internal/services/containerapps/helpers/container_apps.go index 3cacab4b2e9f..4bfe4013e0ac 100644 --- a/internal/services/containerapps/helpers/container_apps.go +++ b/internal/services/containerapps/helpers/container_apps.go @@ -746,12 +746,19 @@ func ContainerTemplateSchemaComputed() *pluginsdk.Schema { Description: "The maximum number of replicas for this container.", }, - "volume": ContainerVolumeSchema(), + "azure_queue_scale_rule": AzureQueueScaleRuleSchemaComputed(), + + "custom_scale_rule": CustomScaleRuleSchemaComputed(), + + "http_scale_rule": HTTPScaleRuleSchemaComputed(), + + "tcp_scale_rule": TCPScaleRuleSchemaComputed(), + + "volume": ContainerVolumeSchemaComputed(), "revision_suffix": { - Type: pluginsdk.TypeString, - Computed: true, - Description: "The suffix for the revision. This value must be unique for the lifetime of the Resource. If omitted the service will use a hash function to create one.", + Type: pluginsdk.TypeString, + Computed: true, }, }, }, @@ -1095,6 +1102,31 @@ func ContainerVolumeSchema() *pluginsdk.Schema { } } +func ContainerVolumeSchemaComputed() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "storage_type": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "storage_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + } +} + func expandContainerAppVolumes(input []ContainerVolume) *[]containerapps.Volume { if input == nil { return nil @@ -2370,11 +2402,10 @@ func ContainerAppProbesRemoved(metadata sdk.ResourceMetaData) bool { } type AzureQueueScaleRule struct { - Name string `tfschema:"name"` - QueueLength int `tfschema:"queue_length"` - QueueName string `tfschema:"queue_name"` - AuthSecretRef string `tfschema:"authentication_secret_reference"` - AuthTriggerParam string `tfschema:"authentication_trigger_parameter"` + Name string `tfschema:"name"` + QueueLength int `tfschema:"queue_length"` + QueueName string `tfschema:"queue_name"` + Authentications []ScaleRuleAuthentication `tfschema:"authentication"` } func AzureQueueScaleRuleSchema() *pluginsdk.Schema { @@ -2401,16 +2432,67 @@ func AzureQueueScaleRuleSchema() *pluginsdk.Schema { ValidateFunc: validation.StringIsNotEmpty, }, - "authentication_secret_reference": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validate.SecretName, + "authentication": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "secret_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.SecretName, + }, + + "trigger_parameter": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + }, + }, + }, + } +} +func AzureQueueScaleRuleSchemaComputed() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, }, - "authentication_trigger_parameter": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, + "queue_length": { + Type: pluginsdk.TypeInt, + Computed: true, + }, + + "queue_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "authentication": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "secret_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "trigger_parameter": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, }, }, }, @@ -2418,11 +2500,10 @@ func AzureQueueScaleRuleSchema() *pluginsdk.Schema { } type CustomScaleRule struct { - Name string `tfschema:"name"` - Metadata map[string]string `tfschema:"metadata"` - CustomRuleType string `tfschema:"custom_rule_type"` - AuthSecretRef string `tfschema:"authentication_secret_reference"` - AuthTriggerParam string `tfschema:"authentication_trigger_parameter"` + Name string `tfschema:"name"` + Metadata map[string]string `tfschema:"metadata"` + CustomRuleType string `tfschema:"custom_rule_type"` + Authentications []ScaleRuleAuthentication `tfschema:"authentication"` } func CustomScaleRuleSchema() *pluginsdk.Schema { @@ -2464,16 +2545,68 @@ func CustomScaleRuleSchema() *pluginsdk.Schema { }, false), // Note - this can be any KEDA compatible source in a user's environment }, - "authentication_secret_reference": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: validate.SecretName, + "authentication": { + Type: pluginsdk.TypeList, + Optional: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "secret_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.SecretName, + }, + + "trigger_parameter": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, }, + }, + }, + } +} - "authentication_trigger_parameter": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: validation.StringIsNotEmpty, +func CustomScaleRuleSchemaComputed() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "metadata": { + Type: pluginsdk.TypeMap, + Computed: true, + }, + + "custom_rule_type": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "authentication": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "secret_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "trigger_parameter": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, }, }, }, @@ -2481,10 +2614,9 @@ func CustomScaleRuleSchema() *pluginsdk.Schema { } type HTTPScaleRule struct { - Name string `tfschema:"name"` - ConcurrentRequests string `tfschema:"concurrent_requests"` - AuthSecretRef string `tfschema:"authentication_secret_reference"` - AuthTriggerParam string `tfschema:"authentication_trigger_parameter"` + Name string `tfschema:"name"` + ConcurrentRequests string `tfschema:"concurrent_requests"` + Authentications []ScaleRuleAuthentication `tfschema:"authentication"` } func HTTPScaleRuleSchema() *pluginsdk.Schema { @@ -2505,16 +2637,63 @@ func HTTPScaleRuleSchema() *pluginsdk.Schema { ValidateFunc: nil, // TODO - str value for int }, - "authentication_secret_reference": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: validate.SecretName, + "authentication": { + Type: pluginsdk.TypeList, + Optional: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "secret_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.SecretName, + }, + + "trigger_parameter": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, }, + }, + }, + } +} - "authentication_trigger_parameter": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: validation.StringIsNotEmpty, +func HTTPScaleRuleSchemaComputed() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "concurrent_requests": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "authentication": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "secret_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "trigger_parameter": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, }, }, }, @@ -2522,10 +2701,9 @@ func HTTPScaleRuleSchema() *pluginsdk.Schema { } type TCPScaleRule struct { - Name string `tfschema:"name"` - ConcurrentRequests string `tfschema:"concurrent_requests"` - AuthSecretRef string `tfschema:"authentication_secret_reference"` - AuthTriggerParam string `tfschema:"authentication_trigger_parameter"` + Name string `tfschema:"name"` + ConcurrentRequests string `tfschema:"concurrent_requests"` + Authentications []ScaleRuleAuthentication `tfschema:"authentication"` } func TCPScaleRuleSchema() *pluginsdk.Schema { @@ -2546,22 +2724,74 @@ func TCPScaleRuleSchema() *pluginsdk.Schema { ValidateFunc: nil, // TODO Validation for int as str }, - "authentication_secret_reference": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: validate.SecretName, + "authentication": { + Type: pluginsdk.TypeList, + Optional: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "secret_name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.SecretName, + }, + + "trigger_parameter": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, }, + }, + }, + } +} - "authentication_trigger_parameter": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: validation.StringIsNotEmpty, +func TCPScaleRuleSchemaComputed() *pluginsdk.Schema { + return &pluginsdk.Schema{ + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "concurrent_requests": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "authentication": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "secret_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "trigger_parameter": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, }, }, }, } } +type ScaleRuleAuthentication struct { + SecretRef string `tfschema:"secret_name"` + TriggerParam string `tfschema:"trigger_parameter"` +} + func (c *ContainerTemplate) expandContainerAppScaleRules() []containerapps.ScaleRule { if len(c.AzureQueueScaleRules) == 0 && len(c.CustomScaleRules) == 0 && len(c.HTTPScaleRules) == 0 && len(c.TCPScaleRules) == 0 { return nil @@ -2573,22 +2803,20 @@ func (c *ContainerTemplate) expandContainerAppScaleRules() []containerapps.Scale AzureQueue: &containerapps.QueueScaleRule{ QueueLength: pointer.To(int64(v.QueueLength)), QueueName: pointer.To(v.QueueName), - Auth: &[]containerapps.ScaleRuleAuth{}, }, } - if v.AuthSecretRef != "" || v.AuthTriggerParam != "" { - auth := containerapps.ScaleRuleAuth{} - if v.AuthTriggerParam != "" { - auth.TriggerParameter = pointer.To(v.AuthTriggerParam) + auths := make([]containerapps.ScaleRuleAuth, 0) + for _, a := range v.Authentications { + auth := containerapps.ScaleRuleAuth{ + TriggerParameter: pointer.To(a.TriggerParam), + SecretRef: pointer.To(a.SecretRef), } + auths = append(auths, auth) + } - if v.AuthSecretRef != "" { - auth.SecretRef = pointer.To(v.AuthSecretRef) - } + r.AzureQueue.Auth = pointer.To(auths) - r.AzureQueue.Auth = pointer.To([]containerapps.ScaleRuleAuth{auth}) - } result = append(result, r) } @@ -2601,18 +2829,17 @@ func (c *ContainerTemplate) expandContainerAppScaleRules() []containerapps.Scale }, } - if v.AuthSecretRef != "" || v.AuthTriggerParam != "" { - auth := containerapps.ScaleRuleAuth{} - if v.AuthTriggerParam != "" { - auth.TriggerParameter = pointer.To(v.AuthTriggerParam) + auths := make([]containerapps.ScaleRuleAuth, 0) + for _, a := range v.Authentications { + auth := containerapps.ScaleRuleAuth{ + TriggerParameter: pointer.To(a.TriggerParam), + SecretRef: pointer.To(a.SecretRef), } + auths = append(auths, auth) + } - if v.AuthSecretRef != "" { - auth.SecretRef = pointer.To(v.AuthSecretRef) - } + r.Custom.Auth = pointer.To(auths) - r.Custom.Auth = pointer.To([]containerapps.ScaleRuleAuth{auth}) - } result = append(result, r) } @@ -2626,18 +2853,17 @@ func (c *ContainerTemplate) expandContainerAppScaleRules() []containerapps.Scale }, } - if v.AuthSecretRef != "" || v.AuthTriggerParam != "" { - auth := containerapps.ScaleRuleAuth{} - if v.AuthTriggerParam != "" { - auth.TriggerParameter = pointer.To(v.AuthTriggerParam) + auths := make([]containerapps.ScaleRuleAuth, 0) + for _, a := range v.Authentications { + auth := containerapps.ScaleRuleAuth{ + TriggerParameter: pointer.To(a.TriggerParam), + SecretRef: pointer.To(a.SecretRef), } + auths = append(auths, auth) + } - if v.AuthSecretRef != "" { - auth.SecretRef = pointer.To(v.AuthSecretRef) - } + r.HTTP.Auth = pointer.To(auths) - r.HTTP.Auth = pointer.To([]containerapps.ScaleRuleAuth{auth}) - } result = append(result, r) } @@ -2651,18 +2877,17 @@ func (c *ContainerTemplate) expandContainerAppScaleRules() []containerapps.Scale }, } - if v.AuthSecretRef != "" || v.AuthTriggerParam != "" { - auth := containerapps.ScaleRuleAuth{} - if v.AuthTriggerParam != "" { - auth.TriggerParameter = pointer.To(v.AuthTriggerParam) + auths := make([]containerapps.ScaleRuleAuth, 0) + for _, a := range v.Authentications { + auth := containerapps.ScaleRuleAuth{ + TriggerParameter: pointer.To(a.TriggerParam), + SecretRef: pointer.To(a.SecretRef), } + auths = append(auths, auth) + } - if v.AuthSecretRef != "" { - auth.SecretRef = pointer.To(v.AuthSecretRef) - } + r.Tcp.Auth = pointer.To(auths) - r.Tcp.Auth = pointer.To([]containerapps.ScaleRuleAuth{auth}) - } result = append(result, r) } @@ -2684,30 +2909,39 @@ func (c *ContainerTemplate) flattenContainerAppScaleRules(input *[]containerapps QueueName: pointer.From(q.QueueName), } - if q.Auth != nil && len(*q.Auth) != 0 { - auth := (*q.Auth)[0] - rule.AuthSecretRef = pointer.From(auth.SecretRef) - rule.AuthTriggerParam = pointer.From(auth.TriggerParameter) + authentications := make([]ScaleRuleAuthentication, 0) + if auths := q.Auth; auths != nil { + for _, a := range *auths { + authentications = append(authentications, ScaleRuleAuthentication{ + SecretRef: pointer.From(a.SecretRef), + TriggerParam: pointer.From(a.TriggerParameter), + }) + } } + rule.Authentications = authentications + azureQueueScaleRules = append(azureQueueScaleRules, rule) continue } if r := v.Custom; r != nil { rule := CustomScaleRule{ - Name: pointer.From(v.Name), - Metadata: pointer.From(r.Metadata), - CustomRuleType: pointer.From(r.Type), - AuthSecretRef: "", - AuthTriggerParam: "", + Name: pointer.From(v.Name), + Metadata: pointer.From(r.Metadata), + CustomRuleType: pointer.From(r.Type), } - if r.Auth != nil && len(*r.Auth) != 0 { - auth := (*r.Auth)[0] - rule.AuthSecretRef = pointer.From(auth.SecretRef) - rule.AuthTriggerParam = pointer.From(auth.TriggerParameter) + authentications := make([]ScaleRuleAuthentication, 0) + if auths := r.Auth; auths != nil { + for _, a := range *auths { + authentications = append(authentications, ScaleRuleAuthentication{ + SecretRef: pointer.From(a.SecretRef), + TriggerParam: pointer.From(a.TriggerParameter), + }) + } } + rule.Authentications = authentications customScaleRules = append(customScaleRules, rule) continue @@ -2724,16 +2958,20 @@ func (c *ContainerTemplate) flattenContainerAppScaleRules(input *[]containerapps rule := HTTPScaleRule{ Name: pointer.From(v.Name), ConcurrentRequests: concurrentReqs, - AuthSecretRef: "", - AuthTriggerParam: "", } - if r.Auth != nil && len(*r.Auth) != 0 { - auth := (*r.Auth)[0] - rule.AuthSecretRef = pointer.From(auth.SecretRef) - rule.AuthTriggerParam = pointer.From(auth.TriggerParameter) + authentications := make([]ScaleRuleAuthentication, 0) + if auths := r.Auth; auths != nil { + for _, a := range *auths { + authentications = append(authentications, ScaleRuleAuthentication{ + SecretRef: pointer.From(a.SecretRef), + TriggerParam: pointer.From(a.TriggerParameter), + }) + } } + rule.Authentications = authentications + httpScaleRules = append(httpScaleRules, rule) continue } @@ -2751,11 +2989,17 @@ func (c *ContainerTemplate) flattenContainerAppScaleRules(input *[]containerapps ConcurrentRequests: concurrentReqs, } - if r.Auth != nil && len(*r.Auth) != 0 { - auth := (*r.Auth)[0] - rule.AuthSecretRef = pointer.From(auth.SecretRef) - rule.AuthTriggerParam = pointer.From(auth.TriggerParameter) + authentications := make([]ScaleRuleAuthentication, 0) + if auths := r.Auth; auths != nil { + for _, a := range *auths { + authentications = append(authentications, ScaleRuleAuthentication{ + SecretRef: pointer.From(a.SecretRef), + TriggerParam: pointer.From(a.TriggerParameter), + }) + } } + rule.Authentications = authentications + tcpScaleRules = append(tcpScaleRules, rule) continue } From 9185e1b17eb6ba076da18dbac7b2a024bd5e7f9c Mon Sep 17 00:00:00 2001 From: jackofallops Date: Fri, 15 Sep 2023 11:52:12 +0200 Subject: [PATCH 4/5] add docs update for scale rules --- .../containerapps/helpers/container_apps.go | 3 + website/docs/r/container_app.html.markdown | 60 +++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/internal/services/containerapps/helpers/container_apps.go b/internal/services/containerapps/helpers/container_apps.go index 4bfe4013e0ac..95d7a1ce218d 100644 --- a/internal/services/containerapps/helpers/container_apps.go +++ b/internal/services/containerapps/helpers/container_apps.go @@ -2584,6 +2584,9 @@ func CustomScaleRuleSchemaComputed() *pluginsdk.Schema { "metadata": { Type: pluginsdk.TypeMap, Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, }, "custom_rule_type": { diff --git a/website/docs/r/container_app.html.markdown b/website/docs/r/container_app.html.markdown index c5ab110a8469..5394f1f43521 100644 --- a/website/docs/r/container_app.html.markdown +++ b/website/docs/r/container_app.html.markdown @@ -97,12 +97,72 @@ A `template` block supports the following: * `min_replicas` - (Optional) The minimum number of replicas for this container. +* `azure_queue_scale_rule` - (Optional) One or more `azure_queue_scale_rule` blocks as defined below. + +* `custom_scale_rule` - (Optional) One or more `custom_scale_rule` blocks as defined below. + +* `http_scale_rule` - (Optional) One or more `http_scale_rule` blocks as defined below. + +* `tcp_scale_rule` - (Optional) One or more `tcp_scale_rule` blocks as defined below. + * `revision_suffix` - (Optional) The suffix for the revision. This value must be unique for the lifetime of the Resource. If omitted the service will use a hash function to create one. * `volume` - (Optional) A `volume` block as detailed below. --- +An `azure_queue_scale_rule` block supports the following: + +* `name` - (Required) The name of the Scaling Rule + +* `queue_name` - (Required) The name of the Azure Queue + +* `queue_length` - (Required) The value of the length of the queue to trigger scaling actions. + +* `authentication` - (Required) One or more `authentication` blocks as defined below. + +--- + +A `custom_scale_rule` block supports the following: + +* `name` - (Required) The name of the Scaling Rule + +* `custom_rule_type` - (Required) The Custom rule type. Possible values include: `activemq`, `artemis-queue`, `kafka`, `pulsar`, `aws-cloudwatch`, `aws-dynamodb`, `aws-dynamodb-streams`, `aws-kinesis-stream`, `aws-sqs-queue`, `azure-app-insights`, `azure-blob`, `azure-data-explorer`, `azure-eventhub`, `azure-log-analytics`, `azure-monitor`, `azure-pipelines`, `azure-servicebus`, `azure-queue`, `cassandra`, `cpu`, `cron`, `datadog`, `elasticsearch`, `external`, `external-push`, `gcp-stackdriver`, `gcp-storage`, `gcp-pubsub`, `graphite`, `http`, `huawei-cloudeye`, `ibmmq`, `influxdb`, `kubernetes-workload`, `liiklus`, `memory`, `metrics-api`, `mongodb`, `mssql`, `mysql`, `nats-jetstream`, `stan`, `tcp`, `new-relic`, `openstack-metric`, `openstack-swift`, `postgresql`, `predictkube`, `prometheus`, `rabbitmq`, `redis`, `redis-cluster`, `redis-sentinel`, `redis-streams`, `redis-cluster-streams`, `redis-sentinel-streams`, `selenium-grid`,`solace-event-queue`, and `github-runner`. + +* `metadata`- (Required) - A map of string key-value pairs to configure the Custom Scale Rule. + +* `authentication` - (Optional) Zero or more `authentication` blocks as defined below. + +--- + +A `http_scale_rule` block supports the following: + +* `name` - (Required) The name of the Scaling Rule + +* `concurrent_requests`- (Required) - The number of concurrent requests to trigger scaling. + +* `authentication` - (Optional) Zero or more `authentication` blocks as defined below. + +--- + +A `tcp_scale_rule` block supports the following: + +* `name` - (Required) The name of the Scaling Rule + +* `concurrent_requests`- (Required) - The number of concurrent requests to trigger scaling. + +* `authentication` - (Optional) Zero or more `authentication` blocks as defined below. + +--- + +An `authentication` block supports the following: + +* `secret_name` - (Required) The name of the Container App Secret to use for this Scale Rule Authentication. + +* `trigger_parameter` - (Required) The Trigger Parameter name to use the supply the value retrieved from the `secret_name`. + +--- + A `volume` block supports the following: * `name` - (Required) The name of the volume. From c6c8beaaf95dcb2a256e70511b99428481a00c4f Mon Sep 17 00:00:00 2001 From: jackofallops Date: Mon, 18 Sep 2023 10:36:11 +0200 Subject: [PATCH 5/5] add validation for concurrent_requests --- .../containerapps/helpers/container_apps.go | 4 +- .../containerapps/validate/validate.go | 21 +++++++++ .../containerapps/validate/validate_test.go | 46 +++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/internal/services/containerapps/helpers/container_apps.go b/internal/services/containerapps/helpers/container_apps.go index 95d7a1ce218d..8584503f08ff 100644 --- a/internal/services/containerapps/helpers/container_apps.go +++ b/internal/services/containerapps/helpers/container_apps.go @@ -2637,7 +2637,7 @@ func HTTPScaleRuleSchema() *pluginsdk.Schema { "concurrent_requests": { Type: pluginsdk.TypeString, Required: true, - ValidateFunc: nil, // TODO - str value for int + ValidateFunc: validate.ContainerAppScaleRuleConcurrentRequests, }, "authentication": { @@ -2724,7 +2724,7 @@ func TCPScaleRuleSchema() *pluginsdk.Schema { "concurrent_requests": { Type: pluginsdk.TypeString, Required: true, - ValidateFunc: nil, // TODO Validation for int as str + ValidateFunc: validate.ContainerAppScaleRuleConcurrentRequests, }, "authentication": { diff --git a/internal/services/containerapps/validate/validate.go b/internal/services/containerapps/validate/validate.go index b6147f522772..8c43ef0fac75 100644 --- a/internal/services/containerapps/validate/validate.go +++ b/internal/services/containerapps/validate/validate.go @@ -6,6 +6,7 @@ package validate import ( "fmt" "regexp" + "strconv" "strings" ) @@ -138,3 +139,23 @@ func ContainerAppContainerName(i interface{}, k string) (warnings []string, erro } return } + +func ContainerAppScaleRuleConcurrentRequests(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) + return + } + + c, err := strconv.Atoi(v) + if err != nil { + errors = append(errors, fmt.Errorf("expected %s to be a string representation of an integer, got %+v", k, v)) + return + } + + if c <= 0 { + errors = append(errors, fmt.Errorf("value for %s must be at least `1`, got %d", k, c)) + } + + return +} diff --git a/internal/services/containerapps/validate/validate_test.go b/internal/services/containerapps/validate/validate_test.go index 183e2c93f8c2..44203f9dc88f 100644 --- a/internal/services/containerapps/validate/validate_test.go +++ b/internal/services/containerapps/validate/validate_test.go @@ -274,3 +274,49 @@ func TestValidateInitTimeout(t *testing.T) { } } } + +func TestContainerAppScaleRuleConcurrentRequests(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + { + Input: "5", + Valid: true, + }, + { + Input: "m", + Valid: false, + }, + { + Input: "6d", + Valid: false, + }, + { + Input: "10s", + Valid: false, + }, + { + Input: "1h", + Valid: false, + }, + { + Input: "1200s", + Valid: false, + }, + { + Input: "-1", + Valid: false, + }, + } + + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := ContainerAppScaleRuleConcurrentRequests(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t for %s", tc.Valid, valid, tc.Input) + } + } +}