Skip to content

Commit

Permalink
Merge pull request #2086 from terraform-providers/devspace
Browse files Browse the repository at this point in the history
New resource: DevSpace Controller for AKS cluster
  • Loading branch information
katbyte committed Oct 29, 2018
2 parents e5bddd7 + e2c0f46 commit ab8acd7
Show file tree
Hide file tree
Showing 14 changed files with 2,204 additions and 0 deletions.
12 changes: 12 additions & 0 deletions azurerm/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
"github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2017-12-01/postgresql"
"github.com/Azure/azure-sdk-for-go/services/preview/apimanagement/mgmt/2018-06-01-preview/apimanagement"
"github.com/Azure/azure-sdk-for-go/services/preview/authorization/mgmt/2018-01-01-preview/authorization"
"github.com/Azure/azure-sdk-for-go/services/preview/devspaces/mgmt/2018-06-01-preview/devspaces"
"github.com/Azure/azure-sdk-for-go/services/preview/dns/mgmt/2018-03-01-preview/dns"
"github.com/Azure/azure-sdk-for-go/services/preview/monitor/mgmt/2018-03-01/insights"
"github.com/Azure/azure-sdk-for-go/services/preview/msi/mgmt/2015-08-31-preview/msi"
Expand All @@ -61,6 +62,7 @@ import (
"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2017-10-01/storage"
"github.com/Azure/azure-sdk-for-go/services/trafficmanager/mgmt/2017-05-01/trafficmanager"
"github.com/Azure/azure-sdk-for-go/services/web/mgmt/2018-02-01/web"

mainStorage "github.com/Azure/azure-sdk-for-go/storage"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/adal"
Expand Down Expand Up @@ -162,6 +164,9 @@ type ArmClient struct {
devTestVirtualMachinesClient dtl.VirtualMachinesClient
devTestVirtualNetworksClient dtl.VirtualNetworksClient

// DevSpace
devSpaceControllerClient devspaces.ControllersClient

// Databases
mysqlConfigurationsClient mysql.ConfigurationsClient
mysqlDatabasesClient mysql.DatabasesClient
Expand Down Expand Up @@ -515,6 +520,7 @@ func getArmClient(c *authentication.Config) (*ArmClient, error) {
client.registerDatabases(endpoint, c.SubscriptionID, auth, sender)
client.registerDataLakeStoreClients(endpoint, c.SubscriptionID, auth)
client.registerDeviceClients(endpoint, c.SubscriptionID, auth)
client.registerDevSpaceClients(endpoint, c.SubscriptionID, auth)
client.registerDevTestClients(endpoint, c.SubscriptionID, auth)
client.registerDNSClients(endpoint, c.SubscriptionID, auth)
client.registerEventGridClients(endpoint, c.SubscriptionID, auth)
Expand Down Expand Up @@ -843,6 +849,12 @@ func (c *ArmClient) registerDevTestClients(endpoint, subscriptionId string, auth
c.devTestVirtualNetworksClient = devTestVirtualNetworksClient
}

func (c *ArmClient) registerDevSpaceClients(endpoint, subscriptionId string, auth autorest.Authorizer) {
controllersClient := devspaces.NewControllersClientWithBaseURI(endpoint, subscriptionId)
c.configureClient(&controllersClient.Client, auth)
c.devSpaceControllerClient = controllersClient
}

func (c *ArmClient) registerDNSClients(endpoint, subscriptionId string, auth autorest.Authorizer) {
dn := dns.NewRecordSetsClientWithBaseURI(endpoint, subscriptionId)
c.configureClient(&dn.Client, auth)
Expand Down
26 changes: 26 additions & 0 deletions azurerm/helpers/validate/devspace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package validate

import (
"regexp"

"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
)

func DevSpaceName() schema.SchemaValidateFunc {
return func(i interface{}, k string) (s []string, es []error) {
// Length should be between 3 and 31.
if s, es = validation.StringLenBetween(3, 31)(i, k); len(es) > 0 {
return s, es
}

// Naming rule.
regexStr := "^[a-zA-Z0-9](-?[a-zA-Z0-9])*$"
errMsg := "DevSpace name can only include alphanumeric characters, hyphens."
if s, es = validation.StringMatch(regexp.MustCompile(regexStr), errMsg)(i, k); len(es) > 0 {
return s, es
}

return s, es
}
}
31 changes: 31 additions & 0 deletions azurerm/helpers/validate/devspace_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package validate

import "testing"

func TestValidateDevSpaceName(t *testing.T) {
validNames := []string{
"valid-name",
"valid02-name",
"validName1",
}
for _, v := range validNames {
_, errors := DevSpaceName()(v, "valid")
if len(errors) != 0 {
t.Fatalf("%q should be a valid DevSpace Name: %q", v, errors)
}
}

invalidNames := []string{
"invalid!",
"!@£",
"-invalid",
"double-hyphen--invalid",
"invalid_name",
}
for _, v := range invalidNames {
_, errors := DevSpaceName()(v, "invalid")
if len(errors) == 0 {
t.Fatalf("%q should be an invalid DevSpace Name", v)
}
}
}
1 change: 1 addition & 0 deletions azurerm/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ func Provider() terraform.ResourceProvider {
"azurerm_data_lake_store": resourceArmDataLakeStore(),
"azurerm_data_lake_store_file": resourceArmDataLakeStoreFile(),
"azurerm_data_lake_store_firewall_rule": resourceArmDataLakeStoreFirewallRule(),
"azurerm_devspace_controller": resourceArmDevSpaceController(),
"azurerm_dev_test_lab": resourceArmDevTestLab(),
"azurerm_dev_test_policy": resourceArmDevTestPolicy(),
"azurerm_dev_test_linux_virtual_machine": resourceArmDevTestLinuxVirtualMachine(),
Expand Down
271 changes: 271 additions & 0 deletions azurerm/resource_arm_devspace_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
package azurerm

import (
"fmt"
"log"

"github.com/Azure/azure-sdk-for-go/services/preview/devspaces/mgmt/2018-06-01-preview/devspaces"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func resourceArmDevSpaceController() *schema.Resource {
return &schema.Resource{
Create: resourceArmDevSpaceControllerCreate,
Read: resourceArmDevSpaceControllerRead,
Update: resourceArmDevSpaceControllerUpdate,
Delete: resourceArmDevSpaceControllerDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validate.DevSpaceName(),
},

"location": locationSchema(),

"resource_group_name": resourceGroupNameSchema(),

"sku": {
Type: schema.TypeList,
Required: true,
ForceNew: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{
"S1",
}, false),
},
"tier": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{
string(devspaces.Standard),
}, false),
},
},
},
},

"host_suffix": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.NoZeroValues,
},

"target_container_host_resource_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: azure.ValidateResourceID,
},

"target_container_host_credentials_base64": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
Sensitive: true,
ValidateFunc: validation.NoZeroValues,
},

"tags": tagsSchema(),

"data_plane_fqdn": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

func resourceArmDevSpaceControllerCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).devSpaceControllerClient
ctx := meta.(*ArmClient).StopContext

log.Printf("[INFO] preparing arguments for DevSpace Controller creation")

name := d.Get("name").(string)
location := azureRMNormalizeLocation(d.Get("location").(string))
resGroupName := d.Get("resource_group_name").(string)
tags := d.Get("tags").(map[string]interface{})

sku := expandDevSpaceControllerSku(d)

hostSuffix := d.Get("host_suffix").(string)
tarCHResId := d.Get("target_container_host_resource_id").(string)
tarCHCredBase64 := d.Get("target_container_host_credentials_base64").(string)

controller := devspaces.Controller{
Location: &location,
Tags: expandTags(tags),
Sku: sku,
ControllerProperties: &devspaces.ControllerProperties{
HostSuffix: &hostSuffix,
TargetContainerHostResourceID: &tarCHResId,
TargetContainerHostCredentialsBase64: &tarCHCredBase64,
},
}

future, err := client.Create(ctx, resGroupName, name, controller)
if err != nil {
return fmt.Errorf("Error creating DevSpace Controller %q (Resource Group %q): %+v", name, resGroupName, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("Error waiting for creation of DevSpace Controller %q (Resource Group %q): %+v", name, resGroupName, err)
}

result, err := client.Get(ctx, resGroupName, name)
if err != nil {
return fmt.Errorf("Error retrieving DevSpace %q (Resource Group %q): %+v", name, resGroupName, err)
}

if result.ID == nil {
return fmt.Errorf("Cannot read DevSpace Controller %q (Resource Group %q) ID", name, resGroupName)
}
d.SetId(*result.ID)

return resourceArmDevSpaceControllerRead(d, meta)
}

func resourceArmDevSpaceControllerRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).devSpaceControllerClient
ctx := meta.(*ArmClient).StopContext

id, err := parseAzureResourceID(d.Id())
if err != nil {
return err
}
resGroupName := id.ResourceGroup
name := id.Path["controllers"]

result, err := client.Get(ctx, resGroupName, name)
if err != nil {
if utils.ResponseWasNotFound(result.Response) {
log.Printf("[DEBUG] DevSpace Controller %q was not found in Resource Group %q - removing from state!", name, resGroupName)
d.SetId("")
return nil
}

return fmt.Errorf("Error making Read request on DevSpace Controller Lab %q (Resource Group %q): %+v", name, resGroupName, err)
}

d.Set("name", result.Name)
d.Set("resource_group_name", resGroupName)
if location := result.Location; location != nil {
d.Set("location", azureRMNormalizeLocation(*location))
}

d.Set("sku", flattenDevSpaceControllerSku(result.Sku))

if props := result.ControllerProperties; props != nil {
if props.HostSuffix != nil {
d.Set("host_suffix", props.HostSuffix)
}

if props.DataPlaneFqdn != nil {
d.Set("data_plane_fqdn", props.DataPlaneFqdn)
}

if props.TargetContainerHostResourceID != nil {
d.Set("target_container_host_resource_id", props.TargetContainerHostResourceID)
}
}

flattenAndSetTags(d, result.Tags)

return nil
}

func resourceArmDevSpaceControllerUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).devSpaceControllerClient
ctx := meta.(*ArmClient).StopContext

log.Printf("[INFO] preparing arguments for DevSpace Controller updating")

name := d.Get("name").(string)
resGroupName := d.Get("resource_group_name").(string)
tags := d.Get("tags").(map[string]interface{})

params := devspaces.ControllerUpdateParameters{
Tags: expandTags(tags),
}

result, err := client.Update(ctx, resGroupName, name, params)
if err != nil {
return fmt.Errorf("Error updating DevSpace Controller %q (Resource Group %q): %+v", name, resGroupName, err)
}

if result.ID == nil {
return fmt.Errorf("Cannot read DevSpace Controller %q (Resource Group %q) ID", name, resGroupName)
}
d.SetId(*result.ID)

return resourceArmDevSpaceControllerRead(d, meta)
}

func resourceArmDevSpaceControllerDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*ArmClient).devSpaceControllerClient
ctx := meta.(*ArmClient).StopContext

id, err := parseAzureResourceID(d.Id())
if err != nil {
return err
}
resGroupName := id.ResourceGroup
name := id.Path["controllers"]

future, err := client.Delete(ctx, resGroupName, name)
if err != nil {
return fmt.Errorf("Error deleting DevSpace Controller %q (Resource Group %q): %+v", name, resGroupName, err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("Error waiting for the deletion of DevSpace Controller %q (Resource Group %q): %+v", name, resGroupName, err)
}

return nil
}

func expandDevSpaceControllerSku(d *schema.ResourceData) *devspaces.Sku {
if _, ok := d.GetOk("sku"); !ok {
return nil
}

skuConfigs := d.Get("sku").([]interface{})
skuConfig := skuConfigs[0].(map[string]interface{})
skuName := skuConfig["name"].(string)
skuTier := devspaces.SkuTier(skuConfig["tier"].(string))

return &devspaces.Sku{
Name: &skuName,
Tier: skuTier,
}
}

func flattenDevSpaceControllerSku(skuObj *devspaces.Sku) []interface{} {
if skuObj == nil {
return []interface{}{}
}

skuConfig := make(map[string]interface{}, 0)
skuConfig["name"] = *skuObj.Name
skuConfig["tier"] = skuObj.Tier

return []interface{}{skuConfig}
}
Loading

0 comments on commit ab8acd7

Please sign in to comment.