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"` +}