Skip to content

Commit

Permalink
Use the API backend for online validation (#201)
Browse files Browse the repository at this point in the history
Changes the way validations are made for plans and regions and uses API backend to do so.
- Enables new dedicated LavinMQ subscriptions plans
- Clean up nodes handling
- Removes former hardcoded plans
- Updated documentation
  • Loading branch information
tbroden84 authored May 3, 2023
1 parent da5c6e2 commit 71a5718
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 180 deletions.
25 changes: 11 additions & 14 deletions cloudamqp/data_source_cloudamqp_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ func dataSourceInstance() *schema.Resource {
Computed: true,
Description: "If default alarms set or not for the instance",
},
"backend": {
Type: schema.TypeString,
Computed: true,
Description: "Software backend used, determined by subscription plan",
},
},
}
}
Expand All @@ -123,14 +128,6 @@ func dataSourceInstanceRead(d *schema.ResourceData, meta interface{}) error {
if k == "vpc" {
err = d.Set("vpc_id", v.(map[string]interface{})["id"])
err = d.Set("vpc_subnet", v.(map[string]interface{})["subnet"])
} else if k == "nodes" {
plan := d.Get("plan").(string)
if is2020Plan(plan) {
nodes := numberOfNodes(plan)
err = d.Set(k, nodes)
} else {
err = d.Set(k, v)
}
} else {
err = d.Set(k, v)
}
Expand All @@ -141,6 +138,12 @@ func dataSourceInstanceRead(d *schema.ResourceData, meta interface{}) error {
}
}

if v, ok := d.Get("nodes").(int); ok && v > 0 {
d.Set("dedicated", true)
} else {
d.Set("dedicated", false)
}

if err = d.Set("host", data["hostname_external"].(string)); err != nil {
return fmt.Errorf("error setting host for resource %s: %s", d.Id(), err)
}
Expand All @@ -153,12 +156,6 @@ func dataSourceInstanceRead(d *schema.ResourceData, meta interface{}) error {
d.Set("no_default_alarms", false)
}

planType, _ := getPlanType(d.Get("plan").(string))
dedicated := planType == "dedicated"
if err = d.Set("dedicated", dedicated); err != nil {
return fmt.Errorf("error setting dedicated for resource %s: %s", d.Id(), err)
}

data = api.UrlInformation(data["url"].(string))
for k, v := range data {
if validateInstanceSchemaAttribute(k) {
Expand Down
139 changes: 43 additions & 96 deletions cloudamqp/resource_cloudamqp_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@ package cloudamqp

import (
"fmt"
"regexp"
"strconv"

"github.com/84codes/go-api/api"
"github.com/hashicorp/terraform-plugin-sdk/helper/customdiff"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)

func resourceInstance() *schema.Resource {
Expand All @@ -27,10 +24,9 @@ func resourceInstance() *schema.Resource {
Description: "Name of the instance",
},
"plan": {
Type: schema.TypeString,
Required: true,
Description: "Name of the plan, see documentation for valid plans",
ValidateFunc: validatePlanName(),
Type: schema.TypeString,
Required: true,
Description: "Name of the plan, see documentation for valid plans",
},
"region": {
Type: schema.TypeString,
Expand Down Expand Up @@ -121,14 +117,33 @@ func resourceInstance() *schema.Resource {
Default: false,
Description: "Keep associated VPC when deleting instance",
},
"backend": {
Type: schema.TypeString,
Computed: true,
Description: "Software backend used, determined by subscription plan",
},
},
CustomizeDiff: customdiff.All(
customdiff.ForceNewIfChange("plan", func(old, new, meta interface{}) bool {
// Recreate instance if changing plan type (from dedicated to shared or vice versa)
oldPlanType, _ := getPlanType(old.(string))
newPlanType, _ := getPlanType(new.(string))
api := meta.(*api.API)
oldPlanType, newPlanType := api.PlanTypes(old.(string), new.(string))
return !(oldPlanType == newPlanType)
}),
customdiff.ValidateChange("plan", func(old, new, meta interface{}) error {
if old == new {
return nil
}
api := meta.(*api.API)
return api.ValidatePlan(new.(string))
}),
customdiff.ValidateChange("region", func(old, new, meta interface{}) error {
if old == new {
return nil
}
api := meta.(*api.API)
return api.ValidateRegion(new.(string))
}),
),
}
}
Expand All @@ -147,23 +162,18 @@ func resourceCreate(d *schema.ResourceData, meta interface{}) error {
params[k] = false
}

if k == "nodes" {
// Remove keys from params
switch k {
case "nodes":
plan := d.Get("plan").(string)
if is2020Plan(plan) {
nodes := numberOfNodes(plan)
params[k] = nodes
} else if isSharedPlan(plan) {
if isSharedPlan(plan) || !isLegacyPlan(plan) {
delete(params, k)
}
}

if k == "vpc_id" {
case "vpc_id":
if d.Get(k).(int) == 0 {
delete(params, k)
}
}

if k == "vpc_subnet" {
case "vpc_subnet":
if d.Get(k) == "" {
delete(params, k)
}
Expand Down Expand Up @@ -191,16 +201,6 @@ func resourceRead(d *schema.ResourceData, meta interface{}) error {
if validateInstanceSchemaAttribute(k) {
if k == "vpc" {
err = d.Set("vpc_id", v.(map[string]interface{})["id"])
} else if k == "nodes" {
plan := d.Get("plan").(string)
if is2020Plan(plan) {
nodes := numberOfNodes(plan)
err = d.Set(k, nodes)
} else if isSharedPlan(plan) {
continue
} else {
err = d.Set(k, v)
}
} else {
err = d.Set(k, v)
}
Expand All @@ -211,6 +211,12 @@ func resourceRead(d *schema.ResourceData, meta interface{}) error {
}
}

if v, ok := d.Get("nodes").(int); ok && v > 0 {
d.Set("dedicated", true)
} else {
d.Set("dedicated", false)
}

if err = d.Set("host", data["hostname_external"].(string)); err != nil {
return fmt.Errorf("error setting host for resource %s: %s", d.Id(), err)
}
Expand All @@ -219,12 +225,6 @@ func resourceRead(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("error setting host for resource %s: %s", d.Id(), err)
}

planType, _ := getPlanType(d.Get("plan").(string))
dedicated := planType == "dedicated"
if err = d.Set("dedicated", dedicated); err != nil {
return fmt.Errorf("error setting dedicated for resource %s: %s", d.Id(), err)
}

data = api.UrlInformation(data["url"].(string))
for k, v := range data {
if validateInstanceSchemaAttribute(k) {
Expand All @@ -246,10 +246,7 @@ func resourceUpdate(d *schema.ResourceData, meta interface{}) error {
}
if k == "nodes" {
plan := d.Get("plan").(string)
if is2020Plan(plan) {
nodes := numberOfNodes(plan)
params[k] = nodes
} else if isSharedPlan(plan) {
if isSharedPlan(plan) || !isLegacyPlan(plan) {
delete(params, k)
}
}
Expand Down Expand Up @@ -280,84 +277,34 @@ func validateInstanceSchemaAttribute(key string) bool {
"tags",
"vhost",
"no_default_alarms",
"ready":
"ready",
"backend":
return true
}
return false
}

func getPlanType(plan string) (string, error) {
switch plan {
case "lemur", "tiger", "lemming":
return "shared", nil
// Legacy plans
case "bunny", "rabbit", "panda", "ape", "hippo", "lion",
// 2020 plans
"squirrel-1",
"hare-1", "hare-3",
"bunny-1", "bunny-3",
"rabbit-1", "rabbit-3", "rabbit-5",
"panda-1", "panda-3", "panda-5",
"ape-1", "ape-3", "ape-5",
"hippo-1", "hippo-3", "hippo-5",
"lion-1", "lion-3", "lion-5",
"rhino-1":
return "dedicated", nil
}
return "", fmt.Errorf("couldn't find a matching plan type for: %s", plan)
}

func validatePlanName() schema.SchemaValidateFunc {
return validation.StringInSlice([]string{
"lemur", "tiger", "lemming",
"bunny", "rabbit", "panda", "ape", "hippo", "lion",
"squirrel-1",
"hare-1", "hare-3",
"bunny-1", "bunny-3",
"rabbit-1", "rabbit-3", "rabbit-5",
"panda-1", "panda-3", "panda-5",
"ape-1", "ape-3", "ape-5",
"hippo-1", "hippo-3", "hippo-5",
"lion-1", "lion-3", "lion-5",
"rhino-1",
}, true)
}

func isSharedPlan(plan string) bool {
switch plan {
case
"lemur",
"tiger",
"lemming":
"lemming",
"ermine":
return true
}
return false
}

func is2020Plan(plan string) bool {
func isLegacyPlan(plan string) bool {
switch plan {
case
"squirrel-1",
"hare-1", "hare-3",
"bunny-1", "bunny-3",
"rabbit-1", "rabbit-3", "rabbit-5",
"panda-1", "panda-3", "panda-5",
"ape-1", "ape-3", "ape-5",
"hippo-1", "hippo-3", "hippo-5",
"lion-1", "lion-3", "lion-5",
"rhino-1":
"bunny", "rabbit", "panda", "ape", "hippo", "lion":
return true
}
return false
}

func numberOfNodes(plan string) int {
r := regexp.MustCompile("[135]")
match := r.FindString(plan)
nodes, _ := strconv.Atoi(match)
return nodes
}

func instanceCreateAttributeKeys() []string {
return []string{
"name",
Expand Down
2 changes: 2 additions & 0 deletions docs/data-sources/instance.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,5 @@ All attributes reference are computed
* `host` - The external hostname for the CloudAMQP instance.
* `host_internal` - The internal hostname for the CloudAMQP instance.
* `vhost` - The virtual host configured in Rabbit MQ.
* `dedicated` - Information if the CloudAMQP instance is shared or dedicated.
* `backend` - Information if the CloudAMQP instance runs either RabbitMQ or LavinMQ.
70 changes: 46 additions & 24 deletions docs/guides/info_plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,56 @@ description: |-

# Subscription plans

Table below shows subscription plans for CloudAMQP. `Lemur`is free of charge, for full price list see [cloudamqp](https://www.cloudamqp.com/plans.html). `Lemur`and `Tiger` are shared instances and share underlying hardware with other instances. They are also limited to which CloudAMQP provider resources that can be used. Further information on availability on each resource page.
Tables below shows general subscription plans for CloudAMQP for either [**RabbitMQ**](https://www.rabbitmq.com/) or [**LavinMQ**](https://lavinmq.com/), for full price list see [cloudamqp](https://www.cloudamqp.com/plans.html).

Name | Plan | Type | Nodes
---- | ---- | ---- | ----
Little lemur | lemur | shared
Tough Tiger | tiger | shared
Lemming (Beta) | lemming | shared
Sassy Squirrel | squirrel-1 | dedicated | 1
Big Bunny | bunny-1,3 | dedicated | 1,3
Roaring Rabbit | rabbit-1,3,5 | dedicated | 1,3,5
Power Panda | panda-1,3,5 | dedicated | 1,3,5
Awesome Ape | ape-1,3,5 | dedicated | 1,3,5
Heavy Hippo | hippo-1,3,5 | dedicated | 1,3,5
Loud Lion | lion-1,3,5 | dedicated | 1,3,5
Raging Rhino | rhino-1 | dedicated | 1
*Information can differ from your actually valid plans, e.g. your team have been given preview access to unreleased plans. To retrieve an up to date list check out [cloudamqp-docs](https://docs.cloudamqp.com/#plans)*

## Plans using RabbitMQ

`Lemur` and `Tiger` are shared instances and share underlying hardware with other instances. They are also limited to which CloudAMQP provider resources that can be used. Further information on availability on each resource page.

Name | Plan | Backend | Type | Nodes
---- | ---- | ---- | ---- | ----
Little lemur | lemur | rabbitmq | shared
Tough Tiger | tiger | rabbitmq | shared
Sassy Squirrel | squirrel-1 | rabbitmq | dedicated | 1
Big Bunny | bunny-1,3 | rabbitmq | dedicated | 1,3
Roaring Rabbit | rabbit-1,3,5 | rabbitmq | dedicated | 1,3,5
Power Panda | panda-1,3,5 | rabbitmq | dedicated | 1,3,5
Awesome Ape | ape-1,3,5 | rabbitmq | dedicated | 1,3,5
Heavy Hippo | hippo-1,3,5 | rabbitmq | dedicated | 1,3,5
Loud Lion | lion-1,3,5 | rabbitmq | dedicated | 1,3,5
Raging Rhino | rhino-1 | rabbitmq | dedicated | 1

## Plans using LavinMQ

`Lemming`and `Ermine` are shared instances and share underlying hardware with other instances. They are also limited to which CloudAMQP provider resources that can be used. Further information on availability on each resource page.

Name | Plan | Backend | Type | Nodes
---- | ---- | ---- | ---- | ----
Loyal Lemming | lemming | lavinmq | shared
Elegant Ermine | ermine | lavinmq | shared
Passionate Puffin | puffin-1 | lavinmq | dedicated | 1
Playful Penguin | penguin-1 | lavinmq | dedicated | 1
Lively Lynx | lynx-1 | lavinmq | dedicated | 1
Wild Wolverine | wolverine-1 | lavinmq | dedicated | 1
Remarkable Reindeer | reindeer-1 | lavinmq | dedicated | 1
Brave Bear | bear-1 | lavinmq | dedicated | 1
Outstanding Orca | orca-1 | lavinmq | dedicated | 1

<br>

# Legacy subscription plans

Table below shows deprecated subscription plans for CloudAMQP. Existing plans will still work, but there will not be possible to create new ones.

Name | Plan | Type
---- | ---- | ----
Little lemur | lemur | shared
Tough Tiger | tiger | shared
Big Bunny | bunny | dedicated
Roaring Rabbit | rabbit | dedicated
Power Panda | panda | dedicated
Awesome Ape | ape | dedicated
Heavy Hippo | hippo | dedicated
Loud Lion | lion | dedicated
Name | Plan | Backend | Type
---- | ---- | ---- | ----
Little lemur | lemur | rabbitmq | shared
Tough Tiger | tiger | rabbitmq | shared
Big Bunny | bunny | rabbitmq | dedicated
Roaring Rabbit | rabbit | rabbitmq | dedicated
Power Panda | panda | rabbitmq | dedicated
Awesome Ape | ape | rabbitmq | dedicated
Heavy Hippo | hippo | rabbitmq | dedicated
Loud Lion | lion | rabbitmq | dedicated
Loading

0 comments on commit 71a5718

Please sign in to comment.