From 2e904624afd633cd92fcf9ba5d7fe075ace948dd Mon Sep 17 00:00:00 2001
From: Artem Lifshits <55093318+artem-lifshits@users.noreply.github.com>
Date: Tue, 30 Jul 2024 17:40:58 +0200
Subject: [PATCH] er (#702)

---
 acceptance/openstack/er/associations_test.go | 159 +++++++++++++++++++
 acceptance/openstack/er/propagations_test.go | 159 +++++++++++++++++++
 openstack/er/v3/association/Create.go        |  41 +++++
 openstack/er/v3/association/Delete.go        |  24 +++
 openstack/er/v3/association/List.go          |  45 ++++++
 openstack/er/v3/propagation/Create.go        |  43 +++++
 openstack/er/v3/propagation/Delete.go        |  24 +++
 openstack/er/v3/propagation/List.go          |  45 ++++++
 8 files changed, 540 insertions(+)
 create mode 100644 acceptance/openstack/er/associations_test.go
 create mode 100644 acceptance/openstack/er/propagations_test.go
 create mode 100644 openstack/er/v3/association/Create.go
 create mode 100644 openstack/er/v3/association/Delete.go
 create mode 100644 openstack/er/v3/association/List.go
 create mode 100644 openstack/er/v3/propagation/Create.go
 create mode 100644 openstack/er/v3/propagation/Delete.go
 create mode 100644 openstack/er/v3/propagation/List.go

diff --git a/acceptance/openstack/er/associations_test.go b/acceptance/openstack/er/associations_test.go
new file mode 100644
index 000000000..a7526ddc6
--- /dev/null
+++ b/acceptance/openstack/er/associations_test.go
@@ -0,0 +1,159 @@
+package er
+
+import (
+	"os"
+	"testing"
+
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients"
+	"github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/common/pointerto"
+	tag "github.com/opentelekomcloud/gophertelekomcloud/openstack/common/tags"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/er/v3/association"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/er/v3/instance"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/er/v3/route_table"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/er/v3/vpc"
+	th "github.com/opentelekomcloud/gophertelekomcloud/testhelper"
+)
+
+func TestERAssociationsLifeCycle(t *testing.T) {
+	if os.Getenv("RUN_ER_LIFECYCLE") == "" {
+		t.Skip("too slow to run in zuul")
+	}
+
+	client, err := clients.NewERClient()
+	th.AssertNoErr(t, err)
+
+	createOpts := instance.CreateOpts{
+		Name:                        tools.RandomString("acctest_er_router-", 4),
+		Asn:                         64512,
+		AutoAcceptSharedAttachments: pointerto.Bool(true),
+		AvailabilityZoneIDs: []string{
+			"eu-de-01",
+			"eu-de-02",
+		},
+	}
+
+	t.Logf("Attempting to create enterprise router")
+
+	createResp, err := instance.Create(client, createOpts)
+	th.AssertNoErr(t, err)
+
+	err = waitForInstanceAvailable(client, 100, createResp.Instance.ID)
+	th.AssertNoErr(t, err)
+
+	t.Cleanup(func() {
+		t.Logf("Attempting to delete enterprise router")
+		err = instance.Delete(client, createResp.Instance.ID)
+		th.AssertNoErr(t, err)
+		err = waitForInstanceDeleted(client, 500, createResp.Instance.ID)
+	})
+
+	createRouteTableOpts := route_table.CreateOpts{
+		Name:        rtName,
+		RouterID:    createResp.Instance.ID,
+		Description: &descriptionRt,
+		Tags: []tag.ResourceTag{
+			{
+				Key:   "muh",
+				Value: "muh",
+			},
+			{
+				Key:   "test",
+				Value: "test",
+			},
+		},
+	}
+
+	t.Logf("Attempting to create route table")
+	createRtResp, err := route_table.Create(client, createRouteTableOpts)
+	th.AssertNoErr(t, err)
+
+	err = waitForRouteTableAvailable(client, 300, createResp.Instance.ID, createRtResp.ID)
+	th.AssertNoErr(t, err)
+
+	t.Cleanup(func() {
+		t.Logf("Attempting to delete route table")
+		err = route_table.Delete(client, createResp.Instance.ID, createRtResp.ID)
+		th.AssertNoErr(t, err)
+		err = waitForRouteTableDeleted(client, 500, createResp.Instance.ID, createRtResp.ID)
+	})
+
+	t.Logf("Attempting to create vpc attachemnt")
+
+	createVpcOpts := vpc.CreateOpts{
+		Name:                vpcName,
+		RouterID:            createResp.Instance.ID,
+		VpcId:               vpcId,
+		SubnetId:            networkId,
+		Description:         description,
+		AutoCreateVpcRoutes: true,
+		Tags: []tag.ResourceTag{
+			{
+				Key:   "muh",
+				Value: "muh",
+			},
+			{
+				Key:   "test",
+				Value: "test",
+			},
+		},
+	}
+
+	createVpcResp, err := vpc.Create(client, createVpcOpts)
+	th.AssertNoErr(t, err)
+
+	err = waitForVpcAttachmentsAvailable(client, 100, createResp.Instance.ID, createVpcResp.ID)
+	th.AssertNoErr(t, err)
+
+	t.Cleanup(func() {
+		t.Logf("Attempting to delete vpc attachemnt")
+		err = vpc.Delete(client, createResp.Instance.ID, createVpcResp.ID)
+		th.AssertNoErr(t, err)
+		err = waitForVpcAttachmentsDeleted(client, 500, createResp.Instance.ID, createVpcResp.ID)
+		th.AssertNoErr(t, err)
+	})
+
+	t.Logf("Attempting to associate route table with vpc attachemnt")
+
+	associateOpts := association.CreateOpts{
+		RouterID:     createResp.Instance.ID,
+		RouteTableID: createRtResp.ID,
+		AttachmentID: createVpcResp.ID,
+	}
+
+	associationResp, err := association.Create(client, associateOpts)
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, associateOpts.RouteTableID, associationResp.RouteTableID)
+	th.AssertEquals(t, associationResp.State, "pending")
+
+	err = waitForVpcAssociationAvailable(client, 100, createResp.Instance.ID, createRtResp.ID)
+	th.AssertNoErr(t, err)
+
+	t.Cleanup(func() {
+		t.Logf("Attempting to dissasociate vpc attachemnt")
+		err = association.Delete(client, association.DeleteOpts{
+			RouterID:     createResp.Instance.ID,
+			RouteTableID: createRtResp.ID,
+			AttachmentID: createVpcResp.ID,
+		})
+		th.AssertNoErr(t, err)
+	})
+
+}
+
+func waitForVpcAssociationAvailable(client *golangsdk.ServiceClient, secs int, erId, rtId string) error {
+	return golangsdk.WaitFor(secs, func() (bool, error) {
+		listAssotiationResp, err := association.List(client, association.ListOpts{
+			RouterId:     erId,
+			RouteTableId: rtId,
+		})
+		if err != nil {
+			return false, err
+		}
+		if listAssotiationResp.Associations[0].State == "available" {
+			return true, nil
+		}
+		return false, nil
+	})
+}
diff --git a/acceptance/openstack/er/propagations_test.go b/acceptance/openstack/er/propagations_test.go
new file mode 100644
index 000000000..9f5857b4a
--- /dev/null
+++ b/acceptance/openstack/er/propagations_test.go
@@ -0,0 +1,159 @@
+package er
+
+import (
+	"os"
+	"testing"
+
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/acceptance/clients"
+	"github.com/opentelekomcloud/gophertelekomcloud/acceptance/tools"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/common/pointerto"
+	tag "github.com/opentelekomcloud/gophertelekomcloud/openstack/common/tags"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/er/v3/instance"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/er/v3/propagation"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/er/v3/route_table"
+	"github.com/opentelekomcloud/gophertelekomcloud/openstack/er/v3/vpc"
+	th "github.com/opentelekomcloud/gophertelekomcloud/testhelper"
+)
+
+func TestERPropagationsLifeCycle(t *testing.T) {
+	if os.Getenv("RUN_ER_LIFECYCLE") == "" {
+		t.Skip("too slow to run in zuul")
+	}
+
+	client, err := clients.NewERClient()
+	th.AssertNoErr(t, err)
+
+	createOpts := instance.CreateOpts{
+		Name:                        tools.RandomString("acctest_er_router-", 4),
+		Asn:                         64512,
+		AutoAcceptSharedAttachments: pointerto.Bool(true),
+		AvailabilityZoneIDs: []string{
+			"eu-de-01",
+			"eu-de-02",
+		},
+	}
+
+	t.Logf("Attempting to create enterprise router")
+
+	createResp, err := instance.Create(client, createOpts)
+	th.AssertNoErr(t, err)
+
+	err = waitForInstanceAvailable(client, 100, createResp.Instance.ID)
+	th.AssertNoErr(t, err)
+
+	t.Cleanup(func() {
+		t.Logf("Attempting to delete enterprise router")
+		err = instance.Delete(client, createResp.Instance.ID)
+		th.AssertNoErr(t, err)
+		err = waitForInstanceDeleted(client, 500, createResp.Instance.ID)
+	})
+
+	createRouteTableOpts := route_table.CreateOpts{
+		Name:        rtName,
+		RouterID:    createResp.Instance.ID,
+		Description: &descriptionRt,
+		Tags: []tag.ResourceTag{
+			{
+				Key:   "muh",
+				Value: "muh",
+			},
+			{
+				Key:   "test",
+				Value: "test",
+			},
+		},
+	}
+
+	t.Logf("Attempting to create route table")
+	createRtResp, err := route_table.Create(client, createRouteTableOpts)
+	th.AssertNoErr(t, err)
+
+	err = waitForRouteTableAvailable(client, 300, createResp.Instance.ID, createRtResp.ID)
+	th.AssertNoErr(t, err)
+
+	t.Cleanup(func() {
+		t.Logf("Attempting to delete route table")
+		err = route_table.Delete(client, createResp.Instance.ID, createRtResp.ID)
+		th.AssertNoErr(t, err)
+		err = waitForRouteTableDeleted(client, 500, createResp.Instance.ID, createRtResp.ID)
+	})
+
+	t.Logf("Attempting to create vpc attachemnt")
+
+	createVpcOpts := vpc.CreateOpts{
+		Name:                vpcName,
+		RouterID:            createResp.Instance.ID,
+		VpcId:               vpcId,
+		SubnetId:            networkId,
+		Description:         description,
+		AutoCreateVpcRoutes: true,
+		Tags: []tag.ResourceTag{
+			{
+				Key:   "muh",
+				Value: "muh",
+			},
+			{
+				Key:   "test",
+				Value: "test",
+			},
+		},
+	}
+
+	createVpcResp, err := vpc.Create(client, createVpcOpts)
+	th.AssertNoErr(t, err)
+
+	err = waitForVpcAttachmentsAvailable(client, 100, createResp.Instance.ID, createVpcResp.ID)
+	th.AssertNoErr(t, err)
+
+	t.Cleanup(func() {
+		t.Logf("Attempting to delete vpc attachemnt")
+		err = vpc.Delete(client, createResp.Instance.ID, createVpcResp.ID)
+		th.AssertNoErr(t, err)
+		err = waitForVpcAttachmentsDeleted(client, 500, createResp.Instance.ID, createVpcResp.ID)
+		th.AssertNoErr(t, err)
+	})
+
+	t.Logf("Attempting to create enterprise route propagation")
+
+	propagateOpts := propagation.CreateOpts{
+		RouterID:     createResp.Instance.ID,
+		RouteTableID: createRtResp.ID,
+		AttachmentID: createVpcResp.ID,
+	}
+
+	propagateResp, err := propagation.Create(client, propagateOpts)
+	th.AssertNoErr(t, err)
+	th.AssertEquals(t, propagateOpts.RouteTableID, propagateResp.RouteTableID)
+	th.AssertEquals(t, propagateResp.State, "pending")
+
+	err = waitForVpcPropagationAvailable(client, 100, createResp.Instance.ID, createRtResp.ID)
+	th.AssertNoErr(t, err)
+
+	t.Cleanup(func() {
+		t.Logf("Attempting to disable propagation")
+		err = propagation.Delete(client, propagation.DeleteOpts{
+			RouterID:     createResp.Instance.ID,
+			RouteTableID: createRtResp.ID,
+			AttachmentID: createVpcResp.ID,
+		})
+		th.AssertNoErr(t, err)
+	})
+
+}
+
+func waitForVpcPropagationAvailable(client *golangsdk.ServiceClient, secs int, erId, rtId string) error {
+	return golangsdk.WaitFor(secs, func() (bool, error) {
+		listPropagationResp, err := propagation.List(client, propagation.ListOpts{
+			RouterId:     erId,
+			RouteTableId: rtId,
+		})
+		if err != nil {
+			return false, err
+		}
+		if listPropagationResp.Propagations[0].State == "available" {
+			return true, nil
+		}
+		return false, nil
+	})
+}
diff --git a/openstack/er/v3/association/Create.go b/openstack/er/v3/association/Create.go
new file mode 100644
index 000000000..b10a31275
--- /dev/null
+++ b/openstack/er/v3/association/Create.go
@@ -0,0 +1,41 @@
+package association
+
+import (
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/build"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
+)
+
+type CreateOpts struct {
+	RouterID     string `json:"-" required:"true"`
+	RouteTableID string `json:"-" required:"true"`
+	AttachmentID string `json:"attachment_id,omitempty"`
+}
+
+func Create(client *golangsdk.ServiceClient, opts CreateOpts) (*Association, error) {
+	b, err := build.RequestBody(opts, "")
+	if err != nil {
+		return nil, err
+	}
+
+	raw, err := client.Post(client.ServiceURL("enterprise-router", opts.RouterID, "route-tables", opts.RouteTableID, "associate"), b, nil, &golangsdk.RequestOpts{
+		OkCodes: []int{202},
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	var res Association
+	return &res, extract.IntoStructPtr(raw.Body, &res, "association")
+}
+
+type Association struct {
+	ID           string `json:"id"`
+	RouteTableID string `json:"route_table_id"`
+	AttachmentID string `json:"attachment_id"`
+	ResourceType string `json:"resource_type"`
+	ResourceID   string `json:"resource_id"`
+	State        string `json:"state"`
+	CreatedAt    string `json:"created_at"`
+	UpdatedAt    string `json:"updated_at"`
+}
diff --git a/openstack/er/v3/association/Delete.go b/openstack/er/v3/association/Delete.go
new file mode 100644
index 000000000..f317bac2c
--- /dev/null
+++ b/openstack/er/v3/association/Delete.go
@@ -0,0 +1,24 @@
+package association
+
+import (
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/build"
+)
+
+type DeleteOpts struct {
+	RouterID     string `json:"-" required:"true"`
+	RouteTableID string `json:"-" required:"true"`
+	AttachmentID string `json:"attachment_id,omitempty"`
+}
+
+func Delete(client *golangsdk.ServiceClient, opts DeleteOpts) (err error) {
+	b, err := build.RequestBody(opts, "")
+	if err != nil {
+		return err
+	}
+
+	_, err = client.Post(client.ServiceURL("enterprise-router", opts.RouterID, "route-tables", opts.RouteTableID, "disassociate"), b, nil, &golangsdk.RequestOpts{
+		OkCodes: []int{202},
+	})
+	return
+}
diff --git a/openstack/er/v3/association/List.go b/openstack/er/v3/association/List.go
new file mode 100644
index 000000000..752afa6c7
--- /dev/null
+++ b/openstack/er/v3/association/List.go
@@ -0,0 +1,45 @@
+package association
+
+import (
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
+)
+
+type ListOpts struct {
+	RouterId     string   `json:"-"`
+	RouteTableId string   `json:"-"`
+	Limit        int      `q:"limit"`
+	Marker       string   `q:"marker"`
+	AttachmentId []string `q:"attachment_id"`
+	ResourceType []string `q:"resource_type"`
+	State        []string `q:"state"`
+	SortKey      []string `q:"sort_key"`
+	SortDir      []string `q:"sort_dir"`
+}
+
+func List(client *golangsdk.ServiceClient, opts ListOpts) (*ListAssociations, error) {
+	url, err := golangsdk.NewURLBuilder().WithEndpoints("enterprise-router", opts.RouterId, "route-tables", opts.RouteTableId, "associations").WithQueryParams(&opts).Build()
+	if err != nil {
+		return nil, err
+	}
+
+	raw, err := client.Get(client.ServiceURL(url.String()), nil, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	var res ListAssociations
+	err = extract.Into(raw.Body, &res)
+	return &res, err
+}
+
+type ListAssociations struct {
+	Associations []Association `json:"associations"`
+	PageInfo     *PageInfo     `json:"page_info"`
+	RequestId    string        `json:"request_id"`
+}
+
+type PageInfo struct {
+	NextMarker   string `json:"next_marker"`
+	CurrentCount int    `json:"current_count"`
+}
diff --git a/openstack/er/v3/propagation/Create.go b/openstack/er/v3/propagation/Create.go
new file mode 100644
index 000000000..4326f5deb
--- /dev/null
+++ b/openstack/er/v3/propagation/Create.go
@@ -0,0 +1,43 @@
+package propagation
+
+import (
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/build"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
+)
+
+type CreateOpts struct {
+	RouterID     string `json:"-" required:"true"`
+	RouteTableID string `json:"-" required:"true"`
+	AttachmentID string `json:"attachment_id,omitempty"`
+}
+
+func Create(client *golangsdk.ServiceClient, opts CreateOpts) (*Propagation, error) {
+	b, err := build.RequestBody(opts, "")
+	if err != nil {
+		return nil, err
+	}
+
+	raw, err := client.Post(client.ServiceURL("enterprise-router", opts.RouterID, "route-tables", opts.RouteTableID, "enable-propagations"), b, nil, &golangsdk.RequestOpts{
+		OkCodes: []int{202},
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	var res Propagation
+	return &res, extract.IntoStructPtr(raw.Body, &res, "propagation")
+}
+
+type Propagation struct {
+	ID           string `json:"id"`
+	ProjectID    string `json:"project_id"`
+	ErID         string `json:"er_id"`
+	RouteTableID string `json:"route_table_id"`
+	AttachmentID string `json:"attachment_id"`
+	ResourceType string `json:"resource_type"`
+	ResourceID   string `json:"resource_id"`
+	State        string `json:"state"`
+	CreatedAt    string `json:"created_at"`
+	UpdatedAt    string `json:"updated_at"`
+}
diff --git a/openstack/er/v3/propagation/Delete.go b/openstack/er/v3/propagation/Delete.go
new file mode 100644
index 000000000..96ba9b178
--- /dev/null
+++ b/openstack/er/v3/propagation/Delete.go
@@ -0,0 +1,24 @@
+package propagation
+
+import (
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/build"
+)
+
+type DeleteOpts struct {
+	RouterID     string `json:"-" required:"true"`
+	RouteTableID string `json:"-" required:"true"`
+	AttachmentID string `json:"attachment_id,omitempty"`
+}
+
+func Delete(client *golangsdk.ServiceClient, opts DeleteOpts) (err error) {
+	b, err := build.RequestBody(opts, "")
+	if err != nil {
+		return err
+	}
+
+	_, err = client.Post(client.ServiceURL("enterprise-router", opts.RouterID, "route-tables", opts.RouteTableID, "disable-propagations"), b, nil, &golangsdk.RequestOpts{
+		OkCodes: []int{202},
+	})
+	return
+}
diff --git a/openstack/er/v3/propagation/List.go b/openstack/er/v3/propagation/List.go
new file mode 100644
index 000000000..5bb0467eb
--- /dev/null
+++ b/openstack/er/v3/propagation/List.go
@@ -0,0 +1,45 @@
+package propagation
+
+import (
+	golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+	"github.com/opentelekomcloud/gophertelekomcloud/internal/extract"
+)
+
+type ListOpts struct {
+	RouterId     string   `json:"-"`
+	RouteTableId string   `json:"-"`
+	Limit        int      `q:"limit"`
+	Marker       string   `q:"marker"`
+	AttachmentId []string `q:"attachment_id"`
+	ResourceType []string `q:"resource_type"`
+	State        []string `q:"state"`
+	SortKey      []string `q:"sort_key"`
+	SortDir      []string `q:"sort_dir"`
+}
+
+func List(client *golangsdk.ServiceClient, opts ListOpts) (*ListAssociations, error) {
+	url, err := golangsdk.NewURLBuilder().WithEndpoints("enterprise-router", opts.RouterId, "route-tables", opts.RouteTableId, "propagations").WithQueryParams(&opts).Build()
+	if err != nil {
+		return nil, err
+	}
+
+	raw, err := client.Get(client.ServiceURL(url.String()), nil, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	var res ListAssociations
+	err = extract.Into(raw.Body, &res)
+	return &res, err
+}
+
+type ListAssociations struct {
+	Propagations []Propagation `json:"propagations"`
+	PageInfo     *PageInfo     `json:"page_info"`
+	RequestId    string        `json:"request_id"`
+}
+
+type PageInfo struct {
+	NextMarker   string `json:"next_marker"`
+	CurrentCount int    `json:"current_count"`
+}