From 60c255420667ca38af8df2a05504903462da9e7d Mon Sep 17 00:00:00 2001 From: becelot Date: Mon, 19 Jun 2023 12:44:14 +0200 Subject: [PATCH] feat: adds ipam resources - supernet, network, instance (#22) --- cancom/services/ipam/provider.go | 5 +- cancom/services/ipam/resource_instance.go | 158 +++++++++ cancom/services/ipam/resource_network.go | 190 ++++++++++ cancom/services/ipam/resource_supernet.go | 159 +++++++++ client/client.go | 2 +- client/services/ipam/ipam.go | 334 +++++++++++++++++- client/services/ipam/models.go | 118 ++++++- docs/resources/cmsmgw_gateway.md.tmplx | 1 + docs/resources/ipam_host.md | 22 +- docs/resources/ipam_instance.md | 50 +++ docs/resources/ipam_network | 53 +++ docs/resources/ipam_network.md | 36 ++ docs/resources/ipam_supernet.md | 48 +++ examples/services/ipam/resources/ipam_host.tf | 20 ++ .../services/ipam/resources/ipam_instance.tf | 6 + .../services/ipam/resources/ipam_network.tf | 17 + .../services/ipam/resources/ipam_supernet.tf | 18 +- templates/resources/ipam_host.md.tmpl | 4 +- templates/resources/ipam_instance.md.tmpl | 18 + templates/resources/ipam_network.tmpl | 16 + templates/resources/ipam_supernet.md.tmpl | 16 + 21 files changed, 1265 insertions(+), 26 deletions(-) create mode 100644 cancom/services/ipam/resource_instance.go create mode 100644 cancom/services/ipam/resource_network.go create mode 100644 cancom/services/ipam/resource_supernet.go create mode 100644 docs/resources/ipam_instance.md create mode 100644 docs/resources/ipam_network create mode 100644 docs/resources/ipam_network.md create mode 100644 docs/resources/ipam_supernet.md create mode 100644 examples/services/ipam/resources/ipam_host.tf create mode 100644 examples/services/ipam/resources/ipam_instance.tf create mode 100644 examples/services/ipam/resources/ipam_network.tf create mode 100644 templates/resources/ipam_instance.md.tmpl create mode 100644 templates/resources/ipam_network.tmpl create mode 100644 templates/resources/ipam_supernet.md.tmpl diff --git a/cancom/services/ipam/provider.go b/cancom/services/ipam/provider.go index 123dc9d..5b44b68 100644 --- a/cancom/services/ipam/provider.go +++ b/cancom/services/ipam/provider.go @@ -20,7 +20,10 @@ func New() Provider { ServiceName: "ipam", ProviderSchema: &schema.Provider{ ResourcesMap: map[string]*schema.Resource{ - "host": resourceHost(), + "host": resourceHost(), + "instance": resourceInstance(), + "supernet": resourceSupernet(), + "network": resourceNetwork(), }, }, } diff --git a/cancom/services/ipam/resource_instance.go b/cancom/services/ipam/resource_instance.go new file mode 100644 index 0000000..b61521a --- /dev/null +++ b/cancom/services/ipam/resource_instance.go @@ -0,0 +1,158 @@ +package ipam + +import ( + "context" + + "github.com/cancom/terraform-provider-cancom/client" + client_ipam "github.com/cancom/terraform-provider-cancom/client/services/ipam" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceInstance() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceInstanceCreate, + ReadContext: resourceInstanceRead, + UpdateContext: resourceInstanceUpdate, + DeleteContext: resourceInstanceDelete, + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name_tag": { + Type: schema.TypeString, + Computed: false, + Required: true, + }, + "managed_by": { + Type: schema.TypeString, + Computed: false, + Required: true, + }, + "description": { + Type: schema.TypeString, + Computed: false, + Optional: true, + }, + "release_wait_time": { + Type: schema.TypeString, + Optional: true, + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + }, + "updated_at": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceInstanceRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*client.Client) + + c.HostURL = c.ServiceURLs["ip-management"] + var diags diag.Diagnostics + + id := d.Id() + + resp, err := (*client_ipam.Client)(c).GetInstance(id) + + if err != nil { + return diag.FromErr(err) + } + + d.Set("name_tag", resp.NameTag) + d.Set("managed_by", resp.ManagedBy) + d.Set("description", resp.Description) + d.Set("release_wait_time", resp.ReleaseWaitTime) + d.Set("created_at", resp.CreatedAt) + d.Set("updated_at", resp.UpdatedAt) + d.Set("id", resp.ID) + + return diags +} + +func resourceInstanceCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*client.Client) + + c.HostURL = c.ServiceURLs["ip-management"] + + var diags diag.Diagnostics + + instance := &client_ipam.InstanceCreateRequest{ + NameTag: d.Get("name_tag").(string), + ManagedBy: d.Get("managed_by").(string), + ReleaseWaitTime: d.Get("release_wait_time").(string), + Description: d.Get("description").(string), + Source: "CANCOM-TF", + } + + resp, err := (*client_ipam.Client)(c).CreateInstance(instance) + + if err != nil { + return diag.FromErr(err) + } + + id := resp.ID + + d.SetId(id) + + resourceInstanceRead(ctx, d, m) + + return diags + +} + +func resourceInstanceUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*client.Client) + + c.HostURL = c.ServiceURLs["ip-management"] + + var diags diag.Diagnostics + + id := d.Id() + + instance := &client_ipam.InstanceUpdateRequest{ + NameTag: d.Get("name_tag").(string), + ManagedBy: d.Get("managed_by").(string), + ReleaseWaitTime: d.Get("release_wait_time").(string), + Description: d.Get("description").(string), + Source: "CANCOM-TF", + } + + _, err := (*client_ipam.Client)(c).UpdateInstance(id, instance) + + if err != nil { + return diag.FromErr(err) + } + + d.SetId(id) + + resourceInstanceRead(ctx, d, m) + + return diags +} + +func resourceInstanceDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*client.Client) + + c.HostURL = c.ServiceURLs["ip-management"] + + var diags diag.Diagnostics + + id := d.Id() + + err := (*client_ipam.Client)(c).DeleteInstance(id) + + if err != nil { + return diag.FromErr(err) + } + + d.SetId("") + + return diags +} diff --git a/cancom/services/ipam/resource_network.go b/cancom/services/ipam/resource_network.go new file mode 100644 index 0000000..fb3c58a --- /dev/null +++ b/cancom/services/ipam/resource_network.go @@ -0,0 +1,190 @@ +package ipam + +import ( + "context" + + "github.com/cancom/terraform-provider-cancom/client" + client_ipam "github.com/cancom/terraform-provider-cancom/client/services/ipam" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceNetwork() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceNetworkCreate, + ReadContext: resourceNetworkRead, + UpdateContext: resourceNetworkUpdate, + DeleteContext: resourceNetworkDelete, + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "supernet_id": { + Type: schema.TypeString, + Computed: false, + Required: true, + }, + "name_tag": { + Type: schema.TypeString, + Computed: false, + Required: true, + }, + "description": { + Type: schema.TypeString, + Computed: false, + Optional: true, + }, + "request": { + Type: schema.TypeString, + Computed: false, + Required: true, + }, + "host_assign": { + Type: schema.TypeBool, + Computed: false, + Optional: true, + }, + "prefix_str": { + Type: schema.TypeString, + Computed: true, + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + }, + "updated_at": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceNetworkRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*client.Client) + + c.HostURL = c.ServiceURLs["ip-management"] + var diags diag.Diagnostics + + id := d.Id() + + resp, err := (*client_ipam.Client)(c).GetNetwork(id) + + if err != nil { + return diag.FromErr(err) + } + + d.Set("name_tag", resp.NameTag) + d.Set("supernet_id", resp.SupernetId) + d.Set("description", resp.Description) + d.Set("prefix_str", resp.PrefixStr) + d.Set("host_assign", resp.HostAssign) + d.Set("created_at", resp.CreatedAt) + d.Set("updated_at", resp.UpdatedAt) + d.Set("id", resp.ID) + + return diags +} + +func resourceNetworkCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*client.Client) + + c.HostURL = c.ServiceURLs["ip-management"] + + var diags diag.Diagnostics + + network := &client_ipam.NetworkCreateRequest{ + SupernetId: d.Get("supernet_id").(string), + Request: d.Get("request").(string), + NameTag: d.Get("name_tag").(string), + Description: d.Get("description").(string), + HostAssign: false, + //Source: "CANCOM-TF" there is a bug in the backend preventing this currently + } + + resp, err := (*client_ipam.Client)(c).CreateNetwork(network) + + if err != nil { + return diag.FromErr(err) + } + + id := resp.ID + + d.SetId(id) + + // if hostAssign is true we must enable ist in an update to th network we just assigned + if d.Get("host_assign").(bool) { + update := &client_ipam.NetworkUpdateRequest{ + HostAssign: true, + //Source: "CANCOM-TF", + } + _, update_err := (*client_ipam.Client)(c).UpdateNetwork(id, update) + if update_err != nil { + return diag.FromErr(update_err) + } + } + + resourceNetworkRead(ctx, d, m) + + return diags + +} + +func resourceNetworkUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*client.Client) + + c.HostURL = c.ServiceURLs["ip-management"] + + var diags diag.Diagnostics + + id := d.Id() + + network := &client_ipam.NetworkUpdateRequest{ + NameTag: d.Get("name_tag").(string), + Description: d.Get("description").(string), + HostAssign: d.Get("host_assign").(bool), + //Source: "CANCOM-TF", + } + + _, err := (*client_ipam.Client)(c).UpdateNetwork(id, network) + + if err != nil { + return diag.FromErr(err) + } + + d.SetId(id) + + resourceNetworkRead(ctx, d, m) + + return diags +} + +func resourceNetworkDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*client.Client) + + c.HostURL = c.ServiceURLs["ip-management"] + + var diags diag.Diagnostics + + id := d.Id() + + // first try to disable host assignment if it is enabled. + network := &client_ipam.NetworkUpdateRequest{ + HostAssign: false, + //Source: "CANCOM-TF", + } + _, err := (*client_ipam.Client)(c).UpdateNetwork(id, network) + if err != nil { + return diag.FromErr(err) + } + + err = (*client_ipam.Client)(c).DeleteNetwork(id) + if err != nil { + return diag.FromErr(err) + } + + d.SetId("") + + return diags +} diff --git a/cancom/services/ipam/resource_supernet.go b/cancom/services/ipam/resource_supernet.go new file mode 100644 index 0000000..c198f3d --- /dev/null +++ b/cancom/services/ipam/resource_supernet.go @@ -0,0 +1,159 @@ +package ipam + +import ( + "context" + + "github.com/cancom/terraform-provider-cancom/client" + client_ipam "github.com/cancom/terraform-provider-cancom/client/services/ipam" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceSupernet() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceSupernetCreate, + ReadContext: resourceSupernetRead, + UpdateContext: resourceSupernetUpdate, + DeleteContext: resourceSupernetDelete, + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "instance_id": { + Type: schema.TypeString, + Computed: false, + Required: true, + }, + "name_tag": { + Type: schema.TypeString, + Computed: false, + Required: true, + }, + "description": { + Type: schema.TypeString, + Computed: false, + Optional: true, + }, + "supernet_cidr": { + Type: schema.TypeString, + Computed: false, + Required: true, + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + }, + "updated_at": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceSupernetRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*client.Client) + + c.HostURL = c.ServiceURLs["ip-management"] + var diags diag.Diagnostics + + id := d.Id() + + resp, err := (*client_ipam.Client)(c).GetSupernet(id) + + if err != nil { + return diag.FromErr(err) + } + + d.Set("name_tag", resp.NameTag) + d.Set("instance_id", resp.InstanceId) + d.Set("supernet_cidr", resp.SupernetCidr) + d.Set("description", resp.Description) + d.Set("created_at", resp.CreatedAt) + d.Set("updated_at", resp.UpdatedAt) + d.Set("id", resp.ID) + + return diags +} + +func resourceSupernetCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*client.Client) + + c.HostURL = c.ServiceURLs["ip-management"] + + var diags diag.Diagnostics + + supernet := &client_ipam.SupernetCreateRequest{ + InstanceId: d.Get("instance_id").(string), + NameTag: d.Get("name_tag").(string), + Description: d.Get("description").(string), + SupernetCidr: d.Get("supernet_cidr").(string), + Source: "CANCOM-TF", + } + + resp, err := (*client_ipam.Client)(c).CreateSupernet(supernet) + + if err != nil { + return diag.FromErr(err) + } + + id := resp.ID + + d.SetId(id) + + resourceSupernetRead(ctx, d, m) + + return diags + +} + +func resourceSupernetUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*client.Client) + + c.HostURL = c.ServiceURLs["ip-management"] + + var diags diag.Diagnostics + + id := d.Id() + + supernet := &client_ipam.SupernetUpdateRequest{ + NameTag: d.Get("name_tag").(string), + SupernetCidr: d.Get("supernet_cidr").(string), + InstanceId: d.Get("instance_id").(string), + Description: d.Get("description").(string), + Source: "CANCOM-TF", + } + + _, err := (*client_ipam.Client)(c).UpdateSupernet(id, supernet) + + if err != nil { + return diag.FromErr(err) + } + + d.SetId(id) + + resourceSupernetRead(ctx, d, m) + + return diags +} + +func resourceSupernetDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + c := m.(*client.Client) + + c.HostURL = c.ServiceURLs["ip-management"] + + var diags diag.Diagnostics + + id := d.Id() + + err := (*client_ipam.Client)(c).DeleteSupernet(id) + + if err != nil { + return diag.FromErr(err) + } + + d.SetId("") + + return diags +} diff --git a/client/client.go b/client/client.go index fc04c37..efbf7db 100644 --- a/client/client.go +++ b/client/client.go @@ -51,7 +51,7 @@ func (c *Client) DoRequest(req *http.Request) ([]byte, error) { return nil, err } - if resp.StatusCode != http.StatusOK { + if (resp.StatusCode != http.StatusOK) && (resp.StatusCode != 201) { return nil, fmt.Errorf("status: %d, body: %s", resp.StatusCode, body) } diff --git a/client/services/ipam/ipam.go b/client/services/ipam/ipam.go index 4f90a30..85f5aa3 100644 --- a/client/services/ipam/ipam.go +++ b/client/services/ipam/ipam.go @@ -3,6 +3,7 @@ package client_ipam import ( //"bytes" "encoding/json" + "errors" "fmt" "math/rand" "net/http" @@ -14,10 +15,329 @@ import ( type Client client.Client +// -----------------------------Network--------------------------------- + +// GetNetwork - Returns a specifc Network +func (c *Client) GetNetwork(networkId string) (*Network, error) { + req, err := http.NewRequest("GET", fmt.Sprintf("%s/v1/Networks/%s", c.HostURL, networkId), nil) + if err != nil { + return nil, err + } + + body, err := (*client.Client)(c).DoRequest(req) + if err != nil { + return nil, err + } + + network := Network{} + err = json.Unmarshal(body, &network) + if err != nil { + return nil, err + } + + return &network, nil +} + +// CreateNetwork - Assign a new network from a given supernet or supernetPool +func (c *Client) CreateNetwork(network *NetworkCreateRequest) (*Network, error) { + rb, err := json.Marshal(network) + if err != nil { + return nil, err + } + + rand.Seed(time.Now().UnixNano()) + n := rand.Intn(5) // n will be between 0 and 10 + time.Sleep(time.Duration(n) * time.Second) + + req, err := http.NewRequest("POST", fmt.Sprintf("%s/v1/Networks/assign", c.HostURL), strings.NewReader(string(rb))) + if err != nil { + return nil, err + } + + body, err := (*client.Client)(c).DoRequest(req) + if err != nil { + return nil, err + } + + newNetwork := Network{} + err = json.Unmarshal(body, &newNetwork) + if err != nil { + return nil, err + } + time.Sleep(4 * time.Second) + return &newNetwork, nil + +} + +// UpdateNetwork - Update a network +func (c *Client) UpdateNetwork(networkId string, network *NetworkUpdateRequest) (*Network, error) { + rb, err := json.Marshal(network) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PUT", fmt.Sprintf("%s/v1/Networks/%s", c.HostURL, networkId), strings.NewReader(string(rb))) + if err != nil { + return nil, err + } + + body, err := (*client.Client)(c).DoRequest(req) + if err != nil { + return nil, err + } + + newNetwork := Network{} + err = json.Unmarshal(body, &newNetwork) + if err != nil { + return nil, err + } + time.Sleep(4 * time.Second) + return &newNetwork, nil +} + +// DeleteNetwork - Deleting a network releases it to the pool +func (c *Client) DeleteNetwork(networkId string) error { + req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/v1/Networks/release/%s", c.HostURL, networkId), nil) + if err != nil { + return err + } + + body, err := (*client.Client)(c).DoRequest(req) + if err != nil { + return err + } + + delResponse := NetworkDeleteResponse{} + err = json.Unmarshal(body, &delResponse) + if err != nil { + return err + } + + //if delResponse.Message != "released successfully" { + // return errors.New("message: " + delResponse.Message) + //} + + return nil +} + +// -----------------------------Supernet--------------------------------- + +// GetSupernet - Returns a specifc Supernet +func (c *Client) GetSupernet(supernetId string) (*Supernet, error) { + req, err := http.NewRequest("GET", fmt.Sprintf("%s/v1/Networks/%s", c.HostURL, supernetId), nil) + if err != nil { + return nil, err + } + + body, err := (*client.Client)(c).DoRequest(req) + if err != nil { + return nil, err + } + + supernet := Supernet{} + err = json.Unmarshal(body, &supernet) + if err != nil { + return nil, err + } + + return &supernet, nil +} + +// CreateSupernet - Create new supernet +func (c *Client) CreateSupernet(supernet *SupernetCreateRequest) (*Supernet, error) { + rb, err := json.Marshal(supernet) + if err != nil { + return nil, err + } + + //return nil, errors.New("message create supernet") + // sleep for a random time to resolve potential race condition problem + rand.Seed(time.Now().UnixNano()) + n := rand.Intn(5) // n will be between 0 and 10 + time.Sleep(time.Duration(n) * time.Second) + + req, err := http.NewRequest("POST", fmt.Sprintf("%s/v1/Networks", c.HostURL), strings.NewReader(string(rb))) + if err != nil { + return nil, err + } + + body, err := (*client.Client)(c).DoRequest(req) + if err != nil { + return nil, err + } + + newSupernet := Supernet{} + err = json.Unmarshal(body, &newSupernet) + if err != nil { + return nil, err + } + time.Sleep(4 * time.Second) + return &newSupernet, nil + +} + +// UpdateSupernet - Updates a supernet +func (c *Client) UpdateSupernet(supernetId string, supernet *SupernetUpdateRequest) (*Supernet, error) { + rb, err := json.Marshal(supernet) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PUT", fmt.Sprintf("%s/v1/Networks/%s", c.HostURL, supernetId), strings.NewReader(string(rb))) + if err != nil { + return nil, err + } + + body, err := (*client.Client)(c).DoRequest(req) + if err != nil { + return nil, err + } + + newSupernet := Supernet{} + err = json.Unmarshal(body, &newSupernet) + if err != nil { + return nil, err + } + time.Sleep(4 * time.Second) + return &newSupernet, nil +} + +// DeleteSupernet - Delete a Supernet +func (c *Client) DeleteSupernet(supernetId string) error { + req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/v1/Networks/%s", c.HostURL, supernetId), nil) + if err != nil { + return err + } + + body, err := (*client.Client)(c).DoRequest(req) + if err != nil { + return err + } + + delResponse := SupernetDeleteResponse{} + err = json.Unmarshal(body, &delResponse) + if err != nil { + return err + } + + //if delResponse.Message != "released successfully" { + // return errors.New("message: " + delResponse.Message) + //} + + return nil +} + +// -----------------------------Instance--------------------------------- + +// GetInstance - Returns a specifc Instance +func (c *Client) GetInstance(instanceId string) (*Instance, error) { + req, err := http.NewRequest("GET", fmt.Sprintf("%s/v1/Instances/%s", c.HostURL, instanceId), nil) + if err != nil { + return nil, err + } + + body, err := (*client.Client)(c).DoRequest(req) + if err != nil { + return nil, err + } + + instance := Instance{} + err = json.Unmarshal(body, &instance) + if err != nil { + return nil, err + } + + return &instance, nil +} + +// CreateInstance - Create new instance +func (c *Client) CreateInstance(instance *InstanceCreateRequest) (*Instance, error) { + rb, err := json.Marshal(instance) + if err != nil { + return nil, err + } + // sleep for a random time to resolve potential race condition problem + rand.Seed(time.Now().UnixNano()) + n := rand.Intn(5) // n will be between 0 and 10 + time.Sleep(time.Duration(n) * time.Second) + + //return nil, errors.New("message: " + c.HostURL) //for debugging + + req, err := http.NewRequest("POST", fmt.Sprintf("%s/v1/Instances", c.HostURL), strings.NewReader(string(rb))) + if err != nil { + return nil, err + } + + body, err := (*client.Client)(c).DoRequest(req) + if err != nil { + return nil, err + } + + newInstance := Instance{} + err = json.Unmarshal(body, &newInstance) + if err != nil { + return nil, err + } + time.Sleep(4 * time.Second) + return &newInstance, nil + +} + +// UpdateInstance - Updates an instance +func (c *Client) UpdateInstance(instanceId string, instance *InstanceUpdateRequest) (*Instance, error) { + rb, err := json.Marshal(instance) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PUT", fmt.Sprintf("%s/v1/Instances/%s", c.HostURL, instanceId), strings.NewReader(string(rb))) + if err != nil { + return nil, err + } + + body, err := (*client.Client)(c).DoRequest(req) + if err != nil { + return nil, err + } + + newInstance := Instance{} + err = json.Unmarshal(body, &newInstance) + if err != nil { + return nil, err + } + time.Sleep(4 * time.Second) + return &newInstance, nil +} + +// DeleteInstance - Delete an Instance +func (c *Client) DeleteInstance(instanceId string) error { + req, err := http.NewRequest("DELETE", fmt.Sprintf("%s/v1/Instances/%s", c.HostURL, instanceId), nil) + if err != nil { + return err + } + + body, err := (*client.Client)(c).DoRequest(req) + if err != nil { + return err + } + + delResponse := InstanceDeleteResponse{} + err = json.Unmarshal(body, &delResponse) + if err != nil { + return err + } + + //if delResponse.Message != "released successfully" { + // return errors.New("message: " + delResponse.Message) + //} + + return nil +} + +// -------------------------------------Host-------------------------------------------------- + // GetHost - Returns a specifc host address func (c *Client) GetHost(hostId string) (*Host, error) { - //return nil, errors.New(c.HostURL) - req, err := http.NewRequest("GET", fmt.Sprintf("%s/v1/Hosts/%s", c.HostURL, hostId), nil) if err != nil { return nil, err @@ -105,8 +425,14 @@ func (c *Client) DeleteHost(hostId string) error { return err } - if string(body) != "Deleted host" { - return nil //errors.New(string(body)) + delResponse := HostDeleteResponse{} + err = json.Unmarshal(body, &delResponse) + if err != nil { + return err + } + // included to find a sporadic problem leading to resources not released in the backend + if delResponse.Message != "released successfully" { + return errors.New("message: " + delResponse.Message) } return nil diff --git a/client/services/ipam/models.go b/client/services/ipam/models.go index fa7dc44..52911a0 100644 --- a/client/services/ipam/models.go +++ b/client/services/ipam/models.go @@ -1,14 +1,5 @@ package client_ipam -type Gateway struct { - ID string `json:"id"` - Name string `json:"name"` -} - -type SupernetCreateRequest struct { - Name string `json:"name"` -} - type Host struct { ID string `json:"crn,omitempty"` Operation string `json:"operation"` @@ -41,3 +32,112 @@ type HostUpdateRequest struct { Description string `json:"description"` Source string `json:"source"` } + +type HostDeleteResponse struct { + ID string `json:"crn,omitempty"` + Message string `json:"message"` +} + +type Instance struct { + ID string `json:"crn,omitempty"` + Description string `json:"description"` + NameTag string `json:"nameTag"` + ManagedBy string `json:"managedBy"` + ReleaseWaitTime string `json:"releaseWaitTime"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` + Source string `json:"source"` +} + +type InstanceCreateRequest struct { + NameTag string `json:"nameTag"` + ManagedBy string `json:"managedBy"` + Description string `json:"description"` + ReleaseWaitTime string `json:"releaseWaitTime"` + Source string `json:"source"` +} + +type InstanceUpdateRequest struct { + NameTag string `json:"nameTag"` + ManagedBy string `json:"managedBy"` + ID string `json:"crn,omitempty"` + Description string `json:"description"` + ReleaseWaitTime string `json:"releaseWaitTime"` + Source string `json:"source"` +} + +type InstanceDeleteResponse struct { + ID string `json:"crn,omitempty"` + Message string `json:"message"` +} + +type Supernet struct { + ID string `json:"crn,omitempty"` + InstanceId string `json:"parent"` + Description string `json:"description"` + NameTag string `json:"nameTag"` + SupernetCidr string `json:"supernetCidr"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` + Source string `json:"source"` +} + +type SupernetCreateRequest struct { + ID string `json:"crn,omitempty"` + InstanceId string `json:"parent"` + Description string `json:"description"` + NameTag string `json:"nameTag"` + SupernetCidr string `json:"supernetCidr"` + Source string `json:"source"` +} + +type SupernetUpdateRequest struct { + ID string `json:"crn,omitempty"` + InstanceId string `json:"parent"` + Description string `json:"description"` + NameTag string `json:"nameTag"` + SupernetCidr string `json:"supernetCidr"` + Source string `json:"source"` +} + +type SupernetDeleteResponse struct { + ID string `json:"crn,omitempty"` + Message string `json:"message"` +} + +type Network struct { + ID string `json:"crn,omitempty"` + SupernetId string `json:"parent"` + Description string `json:"description"` + NameTag string `json:"nameTag"` + PrefixStr string `json:"prefix_str"` + HostAssign bool `json:"hostAssign"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` + Source string `json:"source"` + //HostAssign bool `json:hostAssign` +} + +type NetworkCreateRequest struct { + ID string `json:"crn,omitempty"` + SupernetId string `json:"supernetCrn"` + Request string `json:"request"` + Description string `json:"description"` + NameTag string `json:"nameTag"` + HostAssign bool `json:"hostAssign"` + //Source string `json:"source"` + //HostAssign bool `json:hostAssign` +} + +type NetworkUpdateRequest struct { + ID string `json:"crn,omitempty"` + Description string `json:"description"` + NameTag string `json:"nameTag"` + //Source string `json:"source"` + HostAssign bool `json:"hostAssign"` +} + +type NetworkDeleteResponse struct { + ID string `json:"crn,omitempty"` + Message string `json:"message"` +} diff --git a/docs/resources/cmsmgw_gateway.md.tmplx b/docs/resources/cmsmgw_gateway.md.tmplx index de7e236..535601f 100644 --- a/docs/resources/cmsmgw_gateway.md.tmplx +++ b/docs/resources/cmsmgw_gateway.md.tmplx @@ -12,4 +12,5 @@ description: |- {{ tffile "examples/services/cmsmgw/resources/cmsmgw_gateway.tf" }} + {{ .SchemaMarkdown | trimspace }} diff --git a/docs/resources/ipam_host.md b/docs/resources/ipam_host.md index 41f74f1..0284180 100644 --- a/docs/resources/ipam_host.md +++ b/docs/resources/ipam_host.md @@ -8,17 +8,31 @@ description: |- You manage networks and enable them for host-assignments by using the portal or the network resource of this provider. --- -# cancom_ipam_supernet (Resource) +# cancom_ipam_host (Resource) ## Example Usage ```terraform -resource "cancom_ipam_host" "host" { +resource "cancom_ipam_host" "" { network_crn = "crn:KmBRNWt87RV26jJfZMeJus::ip-management:network:F4tfeFwx2i7HFjNrFop49z" - name_tag = "hostname.cancom.de" - qualifier = "" + name_tag = "" + qualifier = "" description = "" } + + +# the following examples creates two assignments using resources created within the same stack +resource "cancom_ipam_host" "host-assignment-01" { + network_crn = cancom_ipam_network.tfnetwork01.id + name_tag = "host-assignment-01" + description = "assignment creaed by terraform" +} + +resource "cancom_ipam_host" "host-assignment-02" { + network_crn = cancom_ipam_network.tfnetwork01.id + name_tag = "host-assignment-02" + description = "assignment creaed by terraform" +} ``` diff --git a/docs/resources/ipam_instance.md b/docs/resources/ipam_instance.md new file mode 100644 index 0000000..5f58a24 --- /dev/null +++ b/docs/resources/ipam_instance.md @@ -0,0 +1,50 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "CANCOM: ipam_host Resource" +subcategory: "" +description: |- + IPAM host allowes you to assign host ip-addresses from a network that must be enabled for host assignment. The network is + identified by it´s id (crn). + You manage networks and enable them for host-assignments by using the portal or the network resource of this provider. +--- + +# cancom_ipam_supernet (Resource) + +## Example Usage + +```terraform +resource "cancom_ipam_supernet" "" { + instance_id = "" + name_tag = "" + supernet_cidr = "" + description = "" +} + +# example with id from instance within same stack +resource "cancom_ipam_supernet" "tfsupernet01" { + instance_id = cancom_ipam_instance.tfcreated01.id + name_tag = "supernet-from-tf" + supernet_cidr = "10.100.0.0/16" + description = "supernet created by terraform" +} +``` + + + +## Schema + +### Required + +- `managed_by` (String) +- `name_tag` (String) + +### Optional + +- `description` (String) +- `release_wait_time` (String) + +### Read-Only + +- `created_at` (String) +- `id` (String) The ID of this resource. +- `updated_at` (String) diff --git a/docs/resources/ipam_network b/docs/resources/ipam_network new file mode 100644 index 0000000..6fddc4e --- /dev/null +++ b/docs/resources/ipam_network @@ -0,0 +1,53 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "CANCOM: ipam_network Resource" +subcategory: "" +description: |- + IPAM networks are assigned by the service from supernets. +--- + +# cancom_ipam_network (Resource) + +## Example Usage + +```terraform +resource "cancom_ipam_network" "" { + supernet_id = "" + name_tag = "" + request = "" + host_assign = "" + description = "" +} + + +# example with supernet created within the same stack +resource "cancom_ipam_network" "tfnetwork01" { + supernet_id = cancom_ipam_supernet.tfsupernet01.id + name_tag = "network-from-tf" + request = "/25" + host_assign = true + description = "erstellt über terraform" +} +``` + + + +## Schema + +### Required + +- `name_tag` (String) +- `request` (String) +- `supernet_id` (String) + +### Optional + +- `description` (String) +- `host_assign` (Boolean) + +### Read-Only + +- `created_at` (String) +- `id` (String) The ID of this resource. +- `prefix_str` (String) +- `updated_at` (String) diff --git a/docs/resources/ipam_network.md b/docs/resources/ipam_network.md new file mode 100644 index 0000000..d96039e --- /dev/null +++ b/docs/resources/ipam_network.md @@ -0,0 +1,36 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "cancom_ipam_network Resource - terraform-provider-cancom" +subcategory: "" +description: |- + +--- + +# cancom_ipam_network (Resource) + + + + + + +## Schema + +### Required + +- `name_tag` (String) +- `request` (String) +- `supernet_id` (String) + +### Optional + +- `description` (String) +- `host_assign` (Boolean) + +### Read-Only + +- `created_at` (String) +- `id` (String) The ID of this resource. +- `prefix_str` (String) +- `updated_at` (String) + + diff --git a/docs/resources/ipam_supernet.md b/docs/resources/ipam_supernet.md new file mode 100644 index 0000000..0825572 --- /dev/null +++ b/docs/resources/ipam_supernet.md @@ -0,0 +1,48 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "CANCOM: ipam_host Resource" +subcategory: "" +description: |- + IPAM Supernets are larger aggregates that are further subneted by the service. +--- + +# cancom_ipam_supernet (Resource) + +## Example Usage + +```terraform +resource "cancom_ipam_supernet" "" { + instance_id = "" + name_tag = "" + supernet_cidr = "" + description = "" +} + +# example with id from instance within same stack +resource "cancom_ipam_supernet" "tfsupernet01" { + instance_id = cancom_ipam_instance.tfcreated01.id + name_tag = "supernet-from-tf" + supernet_cidr = "10.100.0.0/16" + description = "supernet created by terraform" +} +``` + + + +## Schema + +### Required + +- `instance_id` (String) +- `name_tag` (String) +- `supernet_cidr` (String) + +### Optional + +- `description` (String) + +### Read-Only + +- `created_at` (String) +- `id` (String) The ID of this resource. +- `updated_at` (String) diff --git a/examples/services/ipam/resources/ipam_host.tf b/examples/services/ipam/resources/ipam_host.tf new file mode 100644 index 0000000..80dff2f --- /dev/null +++ b/examples/services/ipam/resources/ipam_host.tf @@ -0,0 +1,20 @@ +resource "cancom_ipam_host" "" { + network_crn = "crn:KmBRNWt87RV26jJfZMeJus::ip-management:network:F4tfeFwx2i7HFjNrFop49z" + name_tag = "" + qualifier = "" + description = "" +} + + +# the following examples creates two assignments using resources created within the same stack +resource "cancom_ipam_host" "host-assignment-01" { + network_crn = cancom_ipam_network.tfnetwork01.id + name_tag = "host-assignment-01" + description = "assignment creaed by terraform" +} + +resource "cancom_ipam_host" "host-assignment-02" { + network_crn = cancom_ipam_network.tfnetwork01.id + name_tag = "host-assignment-02" + description = "assignment creaed by terraform" +} \ No newline at end of file diff --git a/examples/services/ipam/resources/ipam_instance.tf b/examples/services/ipam/resources/ipam_instance.tf new file mode 100644 index 0000000..65e2e08 --- /dev/null +++ b/examples/services/ipam/resources/ipam_instance.tf @@ -0,0 +1,6 @@ +resource "cancom_ipam_instance" "tfcreated01" { + name_tag ="created-by-terraform" + description = "Intance created by teraform" + managed_by = "CUSTOMER" + release_wait_time = "350" +} \ No newline at end of file diff --git a/examples/services/ipam/resources/ipam_network.tf b/examples/services/ipam/resources/ipam_network.tf new file mode 100644 index 0000000..7707fcb --- /dev/null +++ b/examples/services/ipam/resources/ipam_network.tf @@ -0,0 +1,17 @@ +resource "cancom_ipam_network" "" { + supernet_id = "" + name_tag = "" + request = "" + host_assign = "" + description = "" +} + + +# example with supernet created within the same stack +resource "cancom_ipam_network" "tfnetwork01" { + supernet_id = cancom_ipam_supernet.tfsupernet01.id + name_tag = "network-from-tf" + request = "/25" + host_assign = true + description = "erstellt über terraform" +} \ No newline at end of file diff --git a/examples/services/ipam/resources/ipam_supernet.tf b/examples/services/ipam/resources/ipam_supernet.tf index 188703e..67a4138 100644 --- a/examples/services/ipam/resources/ipam_supernet.tf +++ b/examples/services/ipam/resources/ipam_supernet.tf @@ -1,8 +1,16 @@ -resource "cancom_ipam_host" "host" { - network_crn = "crn:KmBRNWt87RV26jJfZMeJus::ip-management:network:F4tfeFwx2i7HFjNrFop49z" - name_tag = "hostname.cancom.de" - qualifier = "" - description = "" +resource "cancom_ipam_supernet" "" { + instance_id = "" + name_tag = "" + supernet_cidr = "" + description = "" +} + +# example with id from instance within same stack +resource "cancom_ipam_supernet" "tfsupernet01" { + instance_id = cancom_ipam_instance.tfcreated01.id + name_tag = "supernet-from-tf" + supernet_cidr = "10.100.0.0/16" + description = "supernet created by terraform" } diff --git a/templates/resources/ipam_host.md.tmpl b/templates/resources/ipam_host.md.tmpl index ac84599..10650c1 100644 --- a/templates/resources/ipam_host.md.tmpl +++ b/templates/resources/ipam_host.md.tmpl @@ -8,11 +8,11 @@ description: |- You manage networks and enable them for host-assignments by using the portal or the network resource of this provider. --- -# cancom_ipam_supernet (Resource) +# cancom_ipam_host (Resource) ## Example Usage -{{ tffile "examples/services/ipam/resources/ipam_supernet.tf" }} +{{ tffile "examples/services/ipam/resources/ipam_host.tf" }} {{ .SchemaMarkdown | trimspace }} diff --git a/templates/resources/ipam_instance.md.tmpl b/templates/resources/ipam_instance.md.tmpl new file mode 100644 index 0000000..ac84599 --- /dev/null +++ b/templates/resources/ipam_instance.md.tmpl @@ -0,0 +1,18 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "CANCOM: ipam_host Resource" +subcategory: "" +description: |- + IPAM host allowes you to assign host ip-addresses from a network that must be enabled for host assignment. The network is + identified by it´s id (crn). + You manage networks and enable them for host-assignments by using the portal or the network resource of this provider. +--- + +# cancom_ipam_supernet (Resource) + +## Example Usage + +{{ tffile "examples/services/ipam/resources/ipam_supernet.tf" }} + + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/resources/ipam_network.tmpl b/templates/resources/ipam_network.tmpl new file mode 100644 index 0000000..8cb0f03 --- /dev/null +++ b/templates/resources/ipam_network.tmpl @@ -0,0 +1,16 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "CANCOM: ipam_network Resource" +subcategory: "" +description: |- + IPAM networks are assigned by the service from supernets. +--- + +# cancom_ipam_network (Resource) + +## Example Usage + +{{ tffile "examples/services/ipam/resources/ipam_network.tf" }} + + +{{ .SchemaMarkdown | trimspace }} diff --git a/templates/resources/ipam_supernet.md.tmpl b/templates/resources/ipam_supernet.md.tmpl new file mode 100644 index 0000000..b6db0a6 --- /dev/null +++ b/templates/resources/ipam_supernet.md.tmpl @@ -0,0 +1,16 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "CANCOM: ipam_host Resource" +subcategory: "" +description: |- + IPAM Supernets are larger aggregates that are further subneted by the service. +--- + +# cancom_ipam_supernet (Resource) + +## Example Usage + +{{ tffile "examples/services/ipam/resources/ipam_supernet.tf" }} + + +{{ .SchemaMarkdown | trimspace }}