Skip to content

Commit

Permalink
Edge node support for HDInsight (#4550)
Browse files Browse the repository at this point in the history
  • Loading branch information
dintskirveli authored and mbfrahry committed Nov 12, 2019
1 parent 85aad9b commit e0a5de8
Show file tree
Hide file tree
Showing 6 changed files with 554 additions and 93 deletions.
99 changes: 95 additions & 4 deletions azurerm/common_hdinsight.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package azurerm

import (
"context"
"fmt"
"log"
"time"

"github.com/Azure/azure-sdk-for-go/services/preview/hdinsight/mgmt/2018-06-01-preview/hdinsight"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags"
Expand Down Expand Up @@ -36,13 +39,13 @@ func hdinsightClusterUpdate(clusterKind string, readFunc schema.ReadFunc) schema
}
}

if d.HasChange("roles") {
if d.HasChange("roles.0.worker_node") {
log.Printf("[DEBUG] Resizing the HDInsight %q Cluster", clusterKind)
rolesRaw := d.Get("roles").([]interface{})
roles := rolesRaw[0].(map[string]interface{})
headNodes := roles["worker_node"].([]interface{})
headNode := headNodes[0].(map[string]interface{})
targetInstanceCount := headNode["target_instance_count"].(int)
workerNodes := roles["worker_node"].([]interface{})
workerNode := workerNodes[0].(map[string]interface{})
targetInstanceCount := workerNode["target_instance_count"].(int)
params := hdinsight.ClusterResizeParameters{
TargetInstanceCount: utils.Int32(int32(targetInstanceCount)),
}
Expand All @@ -57,6 +60,50 @@ func hdinsightClusterUpdate(clusterKind string, readFunc schema.ReadFunc) schema
}
}

// The API can add an edge node but can't remove them without force newing the resource. We'll check for adding here
// and can come back to removing if that functionality gets added. https://feedback.azure.com/forums/217335-hdinsight/suggestions/5663773-start-stop-cluster-hdinsight?page=3&per_page=20
if clusterKind == "Hadoop" {
if d.HasChange("roles.0.edge_node") {
log.Printf("[DEBUG] Detected change in edge nodes")
edgeNodeRaw := d.Get("roles.0.edge_node").([]interface{})
edgeNodeConfig := edgeNodeRaw[0].(map[string]interface{})
applicationsClient := meta.(*ArmClient).HDInsight.ApplicationsClient

oldEdgeNodeCount, newEdgeNodeCount := d.GetChange("roles.0.edge_node.0.target_instance_count")
oldEdgeNodeInt := oldEdgeNodeCount.(int)
newEdgeNodeInt := newEdgeNodeCount.(int)

// Note: API currently doesn't support updating number of edge nodes
// if anything in the edge nodes changes, delete edge nodes then recreate them
if oldEdgeNodeInt != 0 {
err := deleteHDInsightEdgeNodes(ctx, applicationsClient, resourceGroup, name)
if err != nil {
return err
}
}

if newEdgeNodeInt != 0 {
err = createHDInsightEdgeNodes(ctx, applicationsClient, resourceGroup, name, edgeNodeConfig)
if err != nil {
return err
}
}

// we can't rely on the use of the Future here due to the node being successfully completed but now the cluster is applying those changes.
log.Printf("[DEBUG] Waiting for Hadoop Cluster to %q (Resource Group %q) to finish applying edge node", name, resourceGroup)
stateConf := &resource.StateChangeConf{
Pending: []string{"AzureVMConfiguration", "Accepted", "HdInsightConfiguration"},
Target: []string{"Running"},
Refresh: hdInsightWaitForReadyRefreshFunc(ctx, client, resourceGroup, name),
Timeout: 60 * time.Minute,
MinTimeout: 15 * time.Second,
}
if _, err := stateConf.WaitForState(); err != nil {
return fmt.Errorf("Error waiting for HDInsight Cluster %q (Resource Group %q) to be running: %s", name, resourceGroup, err)
}
}
}

return readFunc(d, meta)
}
}
Expand Down Expand Up @@ -179,3 +226,47 @@ func flattenHDInsightRoles(d *schema.ResourceData, input *hdinsight.ComputeProfi
result,
}
}

func createHDInsightEdgeNodes(ctx context.Context, client *hdinsight.ApplicationsClient, resourceGroup string, name string, input map[string]interface{}) error {
installScriptActions := expandHDInsightApplicationEdgeNodeInstallScriptActions(input["install_script_action"].([]interface{}))

application := hdinsight.Application{
Properties: &hdinsight.ApplicationProperties{
ComputeProfile: &hdinsight.ComputeProfile{
Roles: &[]hdinsight.Role{{
Name: utils.String("edgenode"),
HardwareProfile: &hdinsight.HardwareProfile{
VMSize: utils.String(input["vm_size"].(string)),
},
TargetInstanceCount: utils.Int32(int32(input["target_instance_count"].(int))),
}},
},
InstallScriptActions: installScriptActions,
ApplicationType: utils.String("CustomApplication"),
},
}
future, err := client.Create(ctx, resourceGroup, name, name, application)
if err != nil {
return fmt.Errorf("Error creating edge nodes for HDInsight Hadoop Cluster %q (Resource Group %q): %+v", name, resourceGroup, err)
}

if err := future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("Error waiting for creation of edge node for HDInsight Hadoop Cluster %q (Resource Group %q): %+v", name, resourceGroup, err)
}

return nil
}

func deleteHDInsightEdgeNodes(ctx context.Context, client *hdinsight.ApplicationsClient, resourceGroup string, name string) error {
future, err := client.Delete(ctx, resourceGroup, name, name)

if err != nil {
return fmt.Errorf("Error deleting edge nodes for HDInsight Hadoop Cluster %q (Resource Group %q): %+v", name, resourceGroup, err)
}

if err := future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("Error waiting for deletion of edge nodes for HDInsight Hadoop Cluster %q (Resource Group %q): %+v", name, resourceGroup, err)
}

return nil
}
182 changes: 94 additions & 88 deletions azurerm/helpers/azure/hdinsight.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/hashicorp/go-getter/helper/url"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)
Expand Down Expand Up @@ -216,97 +217,102 @@ type HDInsightNodeDefinition struct {
FixedTargetInstanceCount *int32
}

func ValidateSchemaHDInsightNodeDefinitionVMSize() schema.SchemaValidateFunc {
return validation.StringInSlice([]string{
// short of deploying every VM Sku for every node type for every HDInsight Cluster
// this is the list I've (@tombuildsstuff) found for valid SKU's from an endpoint in the Portal
// using another SKU causes a bad request from the API - as such this is a best effort UX
"ExtraSmall",
"Small",
"Medium",
"Large",
"ExtraLarge",
"A5",
"A6",
"A7",
"A8",
"A9",
"A10",
"A11",
"Standard_A1_V2",
"Standard_A2_V2",
"Standard_A2m_V2",
"Standard_A3",
"Standard_A4_V2",
"Standard_A4m_V2",
"Standard_A8_V2",
"Standard_A8m_V2",
"Standard_D1",
"Standard_D2",
"Standard_D3",
"Standard_D4",
"Standard_D11",
"Standard_D12",
"Standard_D13",
"Standard_D14",
"Standard_D1_V2",
"Standard_D2_V2",
"Standard_D3_V2",
"Standard_D4_V2",
"Standard_D5_V2",
"Standard_D11_V2",
"Standard_D12_V2",
"Standard_D13_V2",
"Standard_D14_V2",
"Standard_DS1_V2",
"Standard_DS2_V2",
"Standard_DS3_V2",
"Standard_DS4_V2",
"Standard_DS5_V2",
"Standard_DS11_V2",
"Standard_DS12_V2",
"Standard_DS13_V2",
"Standard_DS14_V2",
"Standard_E2_V3",
"Standard_E4_V3",
"Standard_E8_V3",
"Standard_E16_V3",
"Standard_E20_V3",
"Standard_E32_V3",
"Standard_E64_V3",
"Standard_E64i_V3",
"Standard_E2s_V3",
"Standard_E4s_V3",
"Standard_E8s_V3",
"Standard_E16s_V3",
"Standard_E20s_V3",
"Standard_E32s_V3",
"Standard_E64s_V3",
"Standard_E64is_V3",
"Standard_G1",
"Standard_G2",
"Standard_G3",
"Standard_G4",
"Standard_G5",
"Standard_F2s_V2",
"Standard_F4s_V2",
"Standard_F8s_V2",
"Standard_F16s_V2",
"Standard_F32s_V2",
"Standard_F64s_V2",
"Standard_F72s_V2",
"Standard_GS1",
"Standard_GS2",
"Standard_GS3",
"Standard_GS4",
"Standard_GS5",
"Standard_NC24",
}, true)
}

func SchemaHDInsightNodeDefinition(schemaLocation string, definition HDInsightNodeDefinition) *schema.Schema {
result := map[string]*schema.Schema{
"vm_size": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{
// short of deploying every VM Sku for every node type for every HDInsight Cluster
// this is the list I've (@tombuildsstuff) found for valid SKU's from an endpoint in the Portal
// using another SKU causes a bad request from the API - as such this is a best effort UX
"ExtraSmall",
"Small",
"Medium",
"Large",
"ExtraLarge",
"A5",
"A6",
"A7",
"A8",
"A9",
"A10",
"A11",
"Standard_A1_V2",
"Standard_A2_V2",
"Standard_A2m_V2",
"Standard_A3",
"Standard_A4_V2",
"Standard_A4m_V2",
"Standard_A8_V2",
"Standard_A8m_V2",
"Standard_D1",
"Standard_D2",
"Standard_D3",
"Standard_D4",
"Standard_D11",
"Standard_D12",
"Standard_D13",
"Standard_D14",
"Standard_D1_V2",
"Standard_D2_V2",
"Standard_D3_V2",
"Standard_D4_V2",
"Standard_D5_V2",
"Standard_D11_V2",
"Standard_D12_V2",
"Standard_D13_V2",
"Standard_D14_V2",
"Standard_DS1_V2",
"Standard_DS2_V2",
"Standard_DS3_V2",
"Standard_DS4_V2",
"Standard_DS5_V2",
"Standard_DS11_V2",
"Standard_DS12_V2",
"Standard_DS13_V2",
"Standard_DS14_V2",
"Standard_E2_V3",
"Standard_E4_V3",
"Standard_E8_V3",
"Standard_E16_V3",
"Standard_E20_V3",
"Standard_E32_V3",
"Standard_E64_V3",
"Standard_E64i_V3",
"Standard_E2s_V3",
"Standard_E4s_V3",
"Standard_E8s_V3",
"Standard_E16s_V3",
"Standard_E20s_V3",
"Standard_E32s_V3",
"Standard_E64s_V3",
"Standard_E64is_V3",
"Standard_G1",
"Standard_G2",
"Standard_G3",
"Standard_G4",
"Standard_G5",
"Standard_F2s_V2",
"Standard_F4s_V2",
"Standard_F8s_V2",
"Standard_F16s_V2",
"Standard_F32s_V2",
"Standard_F64s_V2",
"Standard_F72s_V2",
"Standard_GS1",
"Standard_GS2",
"Standard_GS3",
"Standard_GS4",
"Standard_GS5",
"Standard_NC24",
}, true),
Type: schema.TypeString,
Required: true,
ForceNew: true,
DiffSuppressFunc: suppress.CaseDifference,
ValidateFunc: ValidateSchemaHDInsightNodeDefinitionVMSize(),
},
"username": {
Type: schema.TypeString,
Expand Down
Loading

0 comments on commit e0a5de8

Please sign in to comment.