diff --git a/azurerm/provider.go b/azurerm/provider.go index 9cfaf6ee6a9f..f0fcfffa251e 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -384,6 +384,7 @@ func Provider() terraform.ResourceProvider { "azurerm_storage_share": resourceArmStorageShare(), "azurerm_storage_table": resourceArmStorageTable(), "azurerm_stream_analytics_job": resourceArmStreamAnalyticsJob(), + "azurerm_stream_analytics_function_javascript_udf": resourceArmStreamAnalyticsFunctionUDF(), "azurerm_subnet_network_security_group_association": resourceArmSubnetNetworkSecurityGroupAssociation(), "azurerm_subnet_route_table_association": resourceArmSubnetRouteTableAssociation(), "azurerm_subnet": resourceArmSubnet(), diff --git a/azurerm/resource_arm_stream_analytics_function_javascript_udf.go b/azurerm/resource_arm_stream_analytics_function_javascript_udf.go new file mode 100644 index 000000000000..8a6e86634a92 --- /dev/null +++ b/azurerm/resource_arm_stream_analytics_function_javascript_udf.go @@ -0,0 +1,303 @@ +package azurerm + +import ( + "fmt" + "log" + + "github.com/hashicorp/terraform/helper/validation" + + "github.com/Azure/azure-sdk-for-go/services/streamanalytics/mgmt/2016-03-01/streamanalytics" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmStreamAnalyticsFunctionUDF() *schema.Resource { + return &schema.Resource{ + Create: resourceArmStreamAnalyticsFunctionUDFCreateUpdate, + Read: resourceArmStreamAnalyticsFunctionUDFRead, + Update: resourceArmStreamAnalyticsFunctionUDFCreateUpdate, + Delete: resourceArmStreamAnalyticsFunctionUDFDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "stream_analytics_job_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "resource_group_name": resourceGroupNameSchema(), + + "input": { + Type: schema.TypeList, + Required: true, + MinItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "any", + "datetime", + "array", + "bigint", + "float", + "nvarchar(max)", + "record", + }, false), + }, + }, + }, + }, + + "output": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "any", + "datetime", + "array", + "bigint", + "float", + "nvarchar(max)", + "record", + }, false), + }, + }, + }, + }, + + "script": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + // TODO: JS diff suppress func?! + }, + }, + } +} + +func resourceArmStreamAnalyticsFunctionUDFCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).streamAnalyticsFunctionsClient + ctx := meta.(*ArmClient).StopContext + + log.Printf("[INFO] preparing arguments for Azure Stream Analytics Function Javascript UDF creation.") + name := d.Get("name").(string) + jobName := d.Get("stream_analytics_job_name").(string) + resourceGroup := d.Get("resource_group_name").(string) + + if requireResourcesToBeImported && d.IsNewResource() { + existing, err := client.Get(ctx, resourceGroup, jobName, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing Stream Analytics Function Javascript UDF %q (Job %q / Resource Group %q): %s", name, jobName, resourceGroup, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_stream_analytics_function_javascript_udf", *existing.ID) + } + } + + script := d.Get("script").(string) + inputsRaw := d.Get("input").([]interface{}) + inputs := expandStreamAnalyticsFunctionInputs(inputsRaw) + + outputRaw := d.Get("output").([]interface{}) + output := expandStreamAnalyticsFunctionOutput(outputRaw) + + function := streamanalytics.Function{ + Properties: &streamanalytics.ScalarFunctionProperties{ + Type: streamanalytics.TypeScalar, + ScalarFunctionConfiguration: &streamanalytics.ScalarFunctionConfiguration{ + Binding: &streamanalytics.JavaScriptFunctionBinding{ + Type: streamanalytics.TypeMicrosoftStreamAnalyticsJavascriptUdf, + JavaScriptFunctionBindingProperties: &streamanalytics.JavaScriptFunctionBindingProperties{ + Script: utils.String(script), + }, + }, + Inputs: inputs, + Output: output, + }, + }, + } + + if d.IsNewResource() { + if _, err := client.CreateOrReplace(ctx, function, resourceGroup, jobName, name, "", ""); err != nil { + return fmt.Errorf("Error Creating Stream Analytics Function Javascript UDF %q (Job %q / Resource Group %q): %+v", name, jobName, resourceGroup, err) + } + + read, err := client.Get(ctx, resourceGroup, jobName, name) + if err != nil { + return fmt.Errorf("Error retrieving Stream Analytics Function Javascript UDF %q (Job %q / Resource Group %q): %+v", name, jobName, resourceGroup, err) + } + if read.ID == nil { + return fmt.Errorf("Cannot read ID of Stream Analytics Function Javascript UDF %q (Job %q / Resource Group %q)", name, jobName, resourceGroup) + } + + d.SetId(*read.ID) + } else { + if _, err := client.Update(ctx, function, resourceGroup, jobName, name, ""); err != nil { + return fmt.Errorf("Error Updating Stream Analytics Function Javascript UDF %q (Job %q / Resource Group %q): %+v", name, jobName, resourceGroup, err) + } + } + + return resourceArmStreamAnalyticsFunctionUDFRead(d, meta) +} + +func resourceArmStreamAnalyticsFunctionUDFRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).streamAnalyticsFunctionsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + jobName := id.Path["streamingjobs"] + name := id.Path["functions"] + + resp, err := client.Get(ctx, resourceGroup, jobName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[DEBUG] Function Javascript UDF %q was not found in Stream Analytics Job %q / Resource Group %q - removing from state!", name, jobName, resourceGroup) + d.SetId("") + return nil + } + + return fmt.Errorf("Error retrieving Stream Function Javascript UDF %q (Stream Analytics Job %q / Resource Group %q): %+v", name, jobName, resourceGroup, err) + } + + d.Set("name", name) + d.Set("resource_group_name", resourceGroup) + d.Set("stream_analytics_job_name", jobName) + + if props := resp.Properties; props != nil { + scalarProps, ok := props.AsScalarFunctionProperties() + if !ok { + return fmt.Errorf("Error converting Props to a Scalar Function") + } + + binding, ok := scalarProps.Binding.AsJavaScriptFunctionBinding() + if !ok { + return fmt.Errorf("Error converting Binding to a JavaScript Function Binding") + } + + if bindingProps := binding.JavaScriptFunctionBindingProperties; bindingProps != nil { + d.Set("script", bindingProps.Script) + } + + if err := d.Set("input", flattenStreamAnalyticsFunctionInputs(scalarProps.Inputs)); err != nil { + return fmt.Errorf("Error flattening `input`: %+v", err) + } + + if err := d.Set("output", flattenStreamAnalyticsFunctionOutput(scalarProps.Output)); err != nil { + return fmt.Errorf("Error flattening `output`: %+v", err) + } + } + + return nil +} + +func resourceArmStreamAnalyticsFunctionUDFDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).streamAnalyticsFunctionsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + jobName := id.Path["streamingjobs"] + name := id.Path["functions"] + + if resp, err := client.Delete(ctx, resourceGroup, jobName, name); err != nil { + if !response.WasNotFound(resp.Response) { + return fmt.Errorf("Error deleting Function Javascript UDF %q (Stream Analytics Job %q / Resource Group %q) %+v", name, jobName, resourceGroup, err) + } + } + + return nil +} + +func expandStreamAnalyticsFunctionInputs(input []interface{}) *[]streamanalytics.FunctionInput { + outputs := make([]streamanalytics.FunctionInput, 0) + + for _, raw := range input { + v := raw.(map[string]interface{}) + variableType := v["type"].(string) + outputs = append(outputs, streamanalytics.FunctionInput{ + DataType: utils.String(variableType), + }) + } + + return &outputs +} + +func flattenStreamAnalyticsFunctionInputs(input *[]streamanalytics.FunctionInput) []interface{} { + if input == nil { + return []interface{}{} + } + + outputs := make([]interface{}, 0) + + for _, v := range *input { + var variableType string + if v.DataType != nil { + variableType = *v.DataType + } + + outputs = append(outputs, map[string]interface{}{ + "type": variableType, + }) + } + + return outputs +} + +func expandStreamAnalyticsFunctionOutput(input []interface{}) *streamanalytics.FunctionOutput { + output := input[0].(map[string]interface{}) + + dataType := output["type"].(string) + return &streamanalytics.FunctionOutput{ + DataType: utils.String(dataType), + } +} + +func flattenStreamAnalyticsFunctionOutput(input *streamanalytics.FunctionOutput) []interface{} { + if input == nil { + return []interface{}{} + } + + var variableType string + if input.DataType != nil { + variableType = *input.DataType + } + + return []interface{}{ + map[string]interface{}{ + "type": variableType, + }, + } +} diff --git a/azurerm/resource_arm_stream_analytics_function_javascript_udf_test.go b/azurerm/resource_arm_stream_analytics_function_javascript_udf_test.go new file mode 100644 index 000000000000..dd44d507cc77 --- /dev/null +++ b/azurerm/resource_arm_stream_analytics_function_javascript_udf_test.go @@ -0,0 +1,253 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" +) + +func TestAccAzureRMStreamAnalyticsFunctionJavaScriptUDF_basic(t *testing.T) { + resourceName := "azurerm_stream_analytics_function_javascript_udf.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStreamAnalyticsFunctionJavaScriptUDFDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStreamAnalyticsFunctionJavaScriptUDF_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStreamAnalyticsFunctionJavaScriptUDFExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMStreamAnalyticsFunctionJavaScriptUDF_requiresImport(t *testing.T) { + if !requireResourcesToBeImported { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azurerm_stream_analytics_function_javascript_udf.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStreamAnalyticsFunctionJavaScriptUDFDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStreamAnalyticsFunctionJavaScriptUDF_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStreamAnalyticsFunctionJavaScriptUDFExists(resourceName), + ), + }, + { + Config: testAccAzureRMStreamAnalyticsFunctionJavaScriptUDF_requiresImport(ri, location), + ExpectError: testRequiresImportError("azurerm_stream_analytics_function_javascript_udf"), + }, + }, + }) +} + +func TestAccAzureRMStreamAnalyticsFunctionJavaScriptUDF_inputs(t *testing.T) { + resourceName := "azurerm_stream_analytics_function_javascript_udf.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMStreamAnalyticsFunctionJavaScriptUDFDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMStreamAnalyticsFunctionJavaScriptUDF_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStreamAnalyticsFunctionJavaScriptUDFExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + { + Config: testAccAzureRMStreamAnalyticsFunctionJavaScriptUDF_inputs(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMStreamAnalyticsFunctionJavaScriptUDFExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMStreamAnalyticsFunctionJavaScriptUDFExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + name := rs.Primary.Attributes["name"] + jobName := rs.Primary.Attributes["stream_analytics_job_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + conn := testAccProvider.Meta().(*ArmClient).streamAnalyticsFunctionsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := conn.Get(ctx, resourceGroup, jobName, name) + if err != nil { + return fmt.Errorf("Bad: Get on streamAnalyticsFunctionsClient: %+v", err) + } + + if resp.StatusCode == http.StatusNotFound { + return fmt.Errorf("Bad: Function JavaScript UDF %q (Stream Analytics Job %q / Resource Group %q) does not exist", name, jobName, resourceGroup) + } + + return nil + } +} + +func testCheckAzureRMStreamAnalyticsFunctionJavaScriptUDFDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*ArmClient).streamAnalyticsOutputsClient + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_stream_analytics_function_javascript_udf" { + continue + } + + name := rs.Primary.Attributes["name"] + jobName := rs.Primary.Attributes["stream_analytics_job_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := conn.Get(ctx, resourceGroup, jobName, name) + if err != nil { + return nil + } + + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("Stream Analytics Function JavaScript UDF still exists:\n%#v", resp.OutputProperties) + } + } + + return nil +} + +func testAccAzureRMStreamAnalyticsFunctionJavaScriptUDF_basic(rInt int, location string) string { + template := testAccAzureRMStreamAnalyticsFunctionJavaScriptUDF_template(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_stream_analytics_function_javascript_udf" "test" { + name = "acctestinput-%d" + stream_analytics_job_name = "${azurerm_stream_analytics_job.test.name}" + resource_group_name = "${azurerm_stream_analytics_job.test.resource_group_name}" + script = <