From 7e681544dce27c4dae7e92787fa0b41ec8086807 Mon Sep 17 00:00:00 2001 From: Vasubabu <3358152+vasubabu@users.noreply.github.com> Date: Mon, 27 Nov 2023 21:41:48 +0530 Subject: [PATCH] fix: Updated metal-go client for sub-command project bgp sessions --- docs/metal.md | 2 +- docs/metal_project.md | 2 +- docs/metal_project_bgp-config.md | 5 +- docs/metal_project_bgp-enable.md | 3 +- docs/metal_project_bgp-sessions.md | 3 +- docs/metal_project_create.md | 2 +- docs/metal_project_delete.md | 2 +- docs/metal_project_get.md | 2 +- docs/metal_project_update.md | 2 +- internal/projects/bgpconfig.go | 76 +++---- internal/projects/bgpenable.go | 96 ++++---- internal/projects/bgpsessions.go | 94 ++++---- internal/projects/project.go | 12 +- test/e2e/projecttest/project_test.go | 325 +++++++++++++++++++++++++++ test/helper/helper.go | 75 +++++++ 15 files changed, 536 insertions(+), 165 deletions(-) create mode 100644 test/e2e/projecttest/project_test.go diff --git a/docs/metal.md b/docs/metal.md index ce9a4fd9..e87d8bc9 100644 --- a/docs/metal.md +++ b/docs/metal.md @@ -43,7 +43,7 @@ Command line interface for Equinix Metal * [metal organization](metal_organization.md) - Organization operations: create, get, update, payment-methods, and delete. * [metal plan](metal_plan.md) - Plan operations: get. * [metal port](metal_port.md) - Port operations: get, convert, vlans. -* [metal project](metal_project.md) - Project operations: create, get, update, delete, and bgpenable, bgpconfig, bgpsessions. +* [metal project](metal_project.md) - Project operations: create, get, update, delete, and bgp-enable, bgp-config, bgp-sessions. * [metal ssh-key](metal_ssh-key.md) - SSH key operations: create, get, update, and delete. * [metal user](metal_user.md) - User operations: get and add. * [metal virtual-network](metal_virtual-network.md) - Virtual network (VLAN) operations : create, get, delete. diff --git a/docs/metal_project.md b/docs/metal_project.md index 381d7c3b..d3b179f4 100644 --- a/docs/metal_project.md +++ b/docs/metal_project.md @@ -1,6 +1,6 @@ ## metal project -Project operations: create, get, update, delete, and bgpenable, bgpconfig, bgpsessions. +Project operations: create, get, update, delete, and bgp-enable, bgp-config, bgp-sessions. ### Synopsis diff --git a/docs/metal_project_bgp-config.md b/docs/metal_project_bgp-config.md index 348a1d44..f5d355f8 100644 --- a/docs/metal_project_bgp-config.md +++ b/docs/metal_project_bgp-config.md @@ -13,8 +13,7 @@ metal project bgp-config --project-id [flags] ### Examples ``` - # Get BGP config for project 50693ba9-e4e4-4d8a-9eb2-4840b11e9375: - metal project bgp-config --project-id 50693ba9-e4e4-4d8a-9eb2-4840b11e9375 + metal project bgp-config --project-id 50693ba9-e4e4-4d8a-9eb2-4840b11e9375 -d ``` ### Options @@ -41,5 +40,5 @@ metal project bgp-config --project-id [flags] ### SEE ALSO -* [metal project](metal_project.md) - Project operations: create, get, update, delete, and bgpenable, bgpconfig, bgpsessions. +* [metal project](metal_project.md) - Project operations: create, get, update, delete, and bgp-enable, bgp-config, bgp-sessions. diff --git a/docs/metal_project_bgp-enable.md b/docs/metal_project_bgp-enable.md index 1dcaed2b..265deb62 100644 --- a/docs/metal_project_bgp-enable.md +++ b/docs/metal_project_bgp-enable.md @@ -13,7 +13,6 @@ metal project bgp-enable --project-id --deployment-type --deployment-type [flags] ### Examples ``` - # Get BGP Sessions for project 50693ba9-e4e4-4d8a-9eb2-4840b11e9375: metal project bgp-sessions --project-id 50693ba9-e4e4-4d8a-9eb2-4840b11e9375 ``` @@ -41,5 +40,5 @@ metal project bgp-sessions --project-id [flags] ### SEE ALSO -* [metal project](metal_project.md) - Project operations: create, get, update, delete, and bgpenable, bgpconfig, bgpsessions. +* [metal project](metal_project.md) - Project operations: create, get, update, delete, and bgp-enable, bgp-config, bgp-sessions. diff --git a/docs/metal_project_create.md b/docs/metal_project_create.md index 1d561c4b..a6a83a5f 100644 --- a/docs/metal_project_create.md +++ b/docs/metal_project_create.md @@ -46,5 +46,5 @@ metal project create -n [-O ] [-m [--force] [flags] ### SEE ALSO -* [metal project](metal_project.md) - Project operations: create, get, update, delete, and bgpenable, bgpconfig, bgpsessions. +* [metal project](metal_project.md) - Project operations: create, get, update, delete, and bgp-enable, bgp-config, bgp-sessions. diff --git a/docs/metal_project_get.md b/docs/metal_project_get.md index 64158eaf..e80c532f 100644 --- a/docs/metal_project_get.md +++ b/docs/metal_project_get.md @@ -48,5 +48,5 @@ metal project get [-i | -n ] [flags] ### SEE ALSO -* [metal project](metal_project.md) - Project operations: create, get, update, delete, and bgpenable, bgpconfig, bgpsessions. +* [metal project](metal_project.md) - Project operations: create, get, update, delete, and bgp-enable, bgp-config, bgp-sessions. diff --git a/docs/metal_project_update.md b/docs/metal_project_update.md index 09e77470..27d0e1f3 100644 --- a/docs/metal_project_update.md +++ b/docs/metal_project_update.md @@ -46,5 +46,5 @@ metal project update -i [-n ] [-m ] [f ### SEE ALSO -* [metal project](metal_project.md) - Project operations: create, get, update, delete, and bgpenable, bgpconfig, bgpsessions. +* [metal project](metal_project.md) - Project operations: create, get, update, delete, and bgp-enable, bgp-config, bgp-sessions. diff --git a/internal/projects/bgpconfig.go b/internal/projects/bgpconfig.go index cd991c84..b6c14e32 100644 --- a/internal/projects/bgpconfig.go +++ b/internal/projects/bgpconfig.go @@ -1,62 +1,54 @@ -// Copyright © 2022 Equinix Metal Developers -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - package projects import ( + "context" "fmt" "strconv" - "github.com/packethost/packngo" "github.com/spf13/cobra" ) +// BGPConfigFlags holds the flags for the BGPConfig command +type BGPConfigFlags struct { + projectID string +} + func (c *Client) BGPConfig() *cobra.Command { - var projectID string + flags := BGPConfigFlags{} - // bgpConfigProjectCmd represents the updateProject command bgpConfigProjectCmd := &cobra.Command{ - Use: `bgp-config --project-id `, - Short: "Gets BGP Config for a project.", - Long: `Gets BGP Config for a project.`, - Example: ` # Get BGP config for project 50693ba9-e4e4-4d8a-9eb2-4840b11e9375: - metal project bgp-config --project-id 50693ba9-e4e4-4d8a-9eb2-4840b11e9375`, + Use: `bgp-config --project-id `, + Short: "Gets BGP Config for a project.", + Long: `Gets BGP Config for a project.`, + Example: ` metal project bgp-config --project-id 50693ba9-e4e4-4d8a-9eb2-4840b11e9375 -d `, RunE: func(cmd *cobra.Command, args []string) error { - cmd.SilenceUsage = true - listOpt := c.Servicer.ListOptions(nil, nil) - getOpts := &packngo.GetOptions{Includes: listOpt.Includes, Excludes: listOpt.Excludes} - p, _, err := c.BGPConfigService.Get(projectID, getOpts) - if err != nil { - return fmt.Errorf("Could not get Project BGP Config: %w", err) - } - - data := make([][]string, 1) - - data[0] = []string{projectID, p.Status, strconv.Itoa(p.Asn), p.DeploymentType, strconv.Itoa(p.MaxPrefix)} - header := []string{"ID", "Status", "Sessions", "ASN", "DeploymentType", "MaxPrefix"} - return c.Out.Output(p, header, &data) + return getBGPConfig(c, &flags) }, } - bgpConfigProjectCmd.Flags().StringVarP(&projectID, "project-id", "p", "", "Project ID (METAL_PROJECT_ID)") + bgpConfigProjectCmd.Flags().StringVarP(&flags.projectID, "project-id", "p", "", "Project ID (METAL_PROJECT_ID)") _ = bgpConfigProjectCmd.MarkFlagRequired("project-id") + return bgpConfigProjectCmd } + +func getBGPConfig(c *Client, flags *BGPConfigFlags) error { + p, _, err := c.BGPConfigService.FindBgpConfigByProject(context.Background(), flags.projectID).Execute() + if err != nil { + return fmt.Errorf("error getting BGP Config for project %s: %w", flags.projectID, err) + } + + data := [][]string{ + { + flags.projectID, + string(p.GetStatus()), + strconv.Itoa(int(p.GetAsn())), + string(p.GetDeploymentType()), + strconv.Itoa(int(p.GetMaxPrefix())), + }, + } + header := []string{"ID", "Status", "ASN", "DeploymentType", "MaxPrefix"} + + return c.Out.Output(p, header, &data) +} diff --git a/internal/projects/bgpenable.go b/internal/projects/bgpenable.go index 236f22d5..1470723e 100644 --- a/internal/projects/bgpenable.go +++ b/internal/projects/bgpenable.go @@ -1,75 +1,63 @@ -// Copyright © 2022 Equinix Metal Developers -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - package projects import ( + "context" "fmt" "strconv" - "github.com/packethost/packngo" + metal "github.com/equinix-labs/metal-go/metal/v1" "github.com/spf13/cobra" ) +// BgpConfig holds the configuration for the BGP enable command +type BgpConfig struct { + ProjectID string + UseCase string + MD5 string + DeploymentType string + ASN int +} + func (c *Client) BGPEnable() *cobra.Command { - var ( - projectID, useCase, md5, deploymentType string - asn int - ) - // bgpEnableProjectCmd represents the updateProject command + config := BgpConfig{} + bgpEnableProjectCmd := &cobra.Command{ - Use: `bgp-enable --project-id --deployment-type [--asn ] [--md5 ] [--use-case ]`, - Short: "Enables BGP on a project.", - Long: `Enables BGP on a project.`, - Example: ` # Enable BGP on project 50693ba9-e4e4-4d8a-9eb2-4840b11e9375: - metal project bgp-enable --project-id 50693ba9-e4e4-4d8a-9eb2-4840b11e9375 --deployment-type local --asn 65000`, + Use: `bgp-enable --project-id --deployment-type [--asn ] [--md5 ] [--use-case ]`, + Short: "Enables BGP on a project.", + Long: `Enables BGP on a project.`, + Example: ` metal project bgp-enable --project-id 50693ba9-e4e4-4d8a-9eb2-4840b11e9375 --deployment-type local --asn 65000`, RunE: func(cmd *cobra.Command, args []string) error { - cmd.SilenceUsage = true - req := packngo.CreateBGPConfigRequest{ - UseCase: useCase, - Asn: asn, - DeploymentType: deploymentType, - Md5: md5, - } - - p, err := c.BGPConfigService.Create(projectID, req) - if err != nil { - return fmt.Errorf("Could not update Project: %w", err) - } - - data := make([][]string, 1) - - data[0] = []string{projectID, useCase, strconv.Itoa(asn), deploymentType} - header := []string{"ID", "UseCase", "ASN", "DeploymentType"} - return c.Out.Output(p, header, &data) + return enableBGP(c, &config) }, } - bgpEnableProjectCmd.Flags().StringVarP(&projectID, "project-id", "p", "", "Project ID (METAL_PROJECT_ID)") - bgpEnableProjectCmd.Flags().StringVar(&useCase, "use-case", "", "Use case for BGP") - bgpEnableProjectCmd.Flags().IntVar(&asn, "asn", 65000, "Local ASN") - bgpEnableProjectCmd.Flags().StringVar(&deploymentType, "deployment-type", "", "Deployment type (local, global)") - bgpEnableProjectCmd.Flags().StringVar(&md5, "md5", "", "BGP Password") + bgpEnableProjectCmd.Flags().StringVarP(&config.ProjectID, "project-id", "p", "", "Project ID (METAL_PROJECT_ID)") + bgpEnableProjectCmd.Flags().StringVar(&config.UseCase, "use-case", "", "Use case for BGP") + bgpEnableProjectCmd.Flags().IntVar(&config.ASN, "asn", 65000, "Local ASN") + bgpEnableProjectCmd.Flags().StringVar(&config.DeploymentType, "deployment-type", "", "Deployment type (local, global)") + bgpEnableProjectCmd.Flags().StringVar(&config.MD5, "md5", "", "BGP Password") _ = bgpEnableProjectCmd.MarkFlagRequired("project-id") _ = bgpEnableProjectCmd.MarkFlagRequired("asn") _ = bgpEnableProjectCmd.MarkFlagRequired("deployment-type") + return bgpEnableProjectCmd } + +func enableBGP(c *Client, config *BgpConfig) error { + req := metal.BgpConfigRequestInput{ + UseCase: &config.UseCase, + Asn: int32(config.ASN), + DeploymentType: metal.BgpConfigRequestInputDeploymentType(config.DeploymentType), + Md5: &config.MD5, + } + + p, err := c.BGPConfigService.RequestBgpConfig(context.Background(), config.ProjectID).BgpConfigRequestInput(req).Execute() + if err != nil { + return fmt.Errorf("error enabling BGP for project %s: %w", config.ProjectID, err) + } + + data := [][]string{{config.ProjectID, config.UseCase, strconv.Itoa(config.ASN), config.DeploymentType}} + header := []string{"ID", "UseCase", "ASN", "DeploymentType"} + return c.Out.Output(p, header, &data) +} diff --git a/internal/projects/bgpsessions.go b/internal/projects/bgpsessions.go index df5be76a..5bb65f56 100644 --- a/internal/projects/bgpsessions.go +++ b/internal/projects/bgpsessions.go @@ -1,69 +1,63 @@ -// Copyright © 2022 Equinix Metal Developers -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - package projects import ( + "context" "fmt" - "strconv" "strings" - "github.com/packethost/packngo" "github.com/spf13/cobra" ) +type BGPSessionsCommandArgs struct { + ProjectID string +} + func (c *Client) BGPSessions() *cobra.Command { - var projectID string + flags := BGPSessionsCommandArgs{} - // bgpSessionsProjectCmd represents the updateProject command bgpSessionsProjectCmd := &cobra.Command{ - Use: `bgp-sessions --project-id `, - Short: "Gets BGP Sessions for a project.", - Long: `Gets BGP Sessions for a project.`, - Example: ` # Get BGP Sessions for project 50693ba9-e4e4-4d8a-9eb2-4840b11e9375: - metal project bgp-sessions --project-id 50693ba9-e4e4-4d8a-9eb2-4840b11e9375`, + Use: `bgp-sessions --project-id `, + Short: "Gets BGP Sessions for a project.", + Long: `Gets BGP Sessions for a project.`, + Example: ` metal project bgp-sessions --project-id 50693ba9-e4e4-4d8a-9eb2-4840b11e9375`, RunE: func(cmd *cobra.Command, args []string) error { - cmd.SilenceUsage = true - listOpt := c.Servicer.ListOptions(nil, nil) - getOpts := &packngo.GetOptions{Includes: listOpt.Includes, Excludes: listOpt.Excludes} - p, _, err := c.BGPConfigService.Get(projectID, getOpts) - if err != nil { - return fmt.Errorf("Could not get Project BGP Sessions: %w", err) - } - - data := make([][]string, len(p.Sessions)) - for i, s := range p.Sessions { - data[i] = []string{ - s.ID, - s.Status, - strings.Join(s.LearnedRoutes, ","), - strconv.FormatBool(s.DefaultRoute != nil && *s.DefaultRoute), - } - } - header := []string{"ID", "Status", "Learned Routes", "Default Route"} - return c.Out.Output(p, header, &data) + return retrieveBGPSessions(c, &flags) }, } - bgpSessionsProjectCmd.Flags().StringVarP(&projectID, "project-id", "p", "", "Project ID (METAL_PROJECT_ID)") - + bgpSessionsProjectCmd.Flags().StringVarP(&flags.ProjectID, "project-id", "p", "", "Project ID (METAL_PROJECT_ID)") _ = bgpSessionsProjectCmd.MarkFlagRequired("project-id") + return bgpSessionsProjectCmd } + +func retrieveBGPSessions(c *Client, args *BGPSessionsCommandArgs) error { + p, _, err := c.BGPConfigService.FindProjectBgpSessions(context.Background(), args.ProjectID).Execute() + if err != nil { + return fmt.Errorf("error getting BGP Sessions for project %s: %w", args.ProjectID, err) + } + + sessions := p.GetBgpSessions() + if len(sessions) == 0 { + fmt.Printf("No BGP Sessions found for project %s\n", args.ProjectID) + return nil + } + + data := make([][]string, len(sessions)) + for i, s := range sessions { + defaultRoute := "false" + if s.DefaultRoute != nil && *s.DefaultRoute { + defaultRoute = "true" + } + + data[i] = []string{ + s.GetId(), + string(s.GetStatus()), + strings.Join(s.LearnedRoutes, ","), + defaultRoute, + } + } + header := []string{"ID", "Status", "Learned Routes", "Default Route"} + + return c.Out.Output(p, header, &data) +} diff --git a/internal/projects/project.go b/internal/projects/project.go index 7fac1c43..f781a907 100644 --- a/internal/projects/project.go +++ b/internal/projects/project.go @@ -23,14 +23,13 @@ package projects import ( metal "github.com/equinix-labs/metal-go/metal/v1" "github.com/equinix/metal-cli/internal/outputs" - "github.com/packethost/packngo" "github.com/spf13/cobra" ) type Client struct { Servicer Servicer ProjectService metal.ProjectsApiService - BGPConfigService packngo.BGPConfigService + BGPConfigService metal.BGPApiService Out outputs.Outputer } @@ -39,7 +38,7 @@ func (c *Client) NewCommand() *cobra.Command { cmd := &cobra.Command{ Use: `project`, Aliases: []string{"projects"}, - Short: "Project operations: create, get, update, delete, and bgpenable, bgpconfig, bgpsessions.", + Short: "Project operations: create, get, update, delete, and bgp-enable, bgp-config, bgp-sessions.", Long: "Information and management for Projects and Project-level BGP. Documentation on Projects is on https://metal.equinix.com/developers/docs/accounts/projects/, and documentation on BGP is on https://metal.equinix.com/developers/docs/bgp/bgp-on-equinix-metal/.", PersistentPreRun: func(cmd *cobra.Command, args []string) { @@ -49,7 +48,7 @@ func (c *Client) NewCommand() *cobra.Command { } } c.ProjectService = *c.Servicer.MetalAPI(cmd).ProjectsApi - c.BGPConfigService = c.Servicer.API(cmd).BGPConfig + c.BGPConfigService = *c.Servicer.MetalAPI(cmd).BGPApi }, } @@ -67,9 +66,10 @@ func (c *Client) NewCommand() *cobra.Command { type Servicer interface { MetalAPI(*cobra.Command) *metal.APIClient - API(*cobra.Command) *packngo.Client - ListOptions(defaultIncludes, defaultExcludes []string) *packngo.ListOptions Format() outputs.Format + Includes(defaultIncludes []string) (incl []string) + Excludes(defaultExcludes []string) (excl []string) + Filters() map[string]string } func NewClient(s Servicer, out outputs.Outputer) *Client { diff --git a/test/e2e/projecttest/project_test.go b/test/e2e/projecttest/project_test.go new file mode 100644 index 00000000..a202033e --- /dev/null +++ b/test/e2e/projecttest/project_test.go @@ -0,0 +1,325 @@ +package projecttest + +import ( + "fmt" + "io" + "os" + "strings" + "testing" + + root "github.com/equinix/metal-cli/internal/cli" + outputPkg "github.com/equinix/metal-cli/internal/outputs" + "github.com/equinix/metal-cli/internal/projects" + "github.com/equinix/metal-cli/test/helper" + "github.com/spf13/cobra" +) + +// setupTestOrganization initializes a test Orge and returns its ID along with a cleanup function. +func setupTestOrganization(t *testing.T, projectName string) (string, func()) { + orgId, err := helper.CreateTestOrganization(projectName) + if err != nil { + t.Fatal(err) + } + + teardown := func() { + err := helper.CleanTestOrganization(orgId) + if err != nil { + t.Error(err) + } + } + + return orgId, teardown +} + +// setupTestProject initializes a test project and returns its ID along with a cleanup function. +func setupTestProject(t *testing.T, projectName string) (string, func()) { + projectId, err := helper.CreateTestProject(projectName) + if err != nil { + t.Fatal(err) + } + + teardown := func() { + err := helper.CleanTestProject(projectId) + if err != nil { + t.Error(err) + } + } + + return projectId, teardown +} + +func TestCli_Project_Tests(t *testing.T) { + subCommand := "project" + consumerToken := "" + apiURL := "" + Version := "metal" + rootClient := root.NewClient(consumerToken, apiURL, Version) + randName, _ := helper.GenerateRandomString(32) + + type fields struct { + MainCmd *cobra.Command + Outputer outputPkg.Outputer + } + + tests := []struct { + name string + fields fields + want *cobra.Command + cmdFunc func(*testing.T, *cobra.Command, *cobra.Command, string) + }{ + { + name: "project-create-test", + fields: fields{ + MainCmd: projects.NewClient(rootClient, outputPkg.Outputer(&outputPkg.Standard{})).NewCommand(), + Outputer: outputPkg.Outputer(&outputPkg.Standard{}), + }, + want: &cobra.Command{}, + cmdFunc: func(t *testing.T, c *cobra.Command, rootCmd *cobra.Command, projectID string) { + root := c.Root() + projName := "metal-cli-" + randName + "-project-create-test" + orgId, cleanupOrg := setupTestOrganization(t, projName) + defer cleanupOrg() + + if orgId != "" { + root.SetArgs([]string{subCommand, "create", "-O", orgId, "-n", projName}) + rescueStdout := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + if err := root.Execute(); err != nil { + t.Error(err) + } + w.Close() + out, _ := io.ReadAll(r) + os.Stdout = rescueStdout + if !strings.Contains(string(out[:]), projName) { + t.Error("expected output should include " + projName + ", in the out string ") + } + } + }, + }, + + { + name: "project-update-test", + fields: fields{ + MainCmd: projects.NewClient(rootClient, outputPkg.Outputer(&outputPkg.Standard{})).NewCommand(), + Outputer: outputPkg.Outputer(&outputPkg.Standard{}), + }, + want: &cobra.Command{}, + cmdFunc: func(t *testing.T, c *cobra.Command, rootCmd *cobra.Command, projectID string) { + root := c.Root() + + projName := "metal-cli-" + randName + "-project-update-test" + + projectId, cleanProject := setupTestProject(t, projName) + defer cleanProject() + updateProjName := projName + "-123" + + if projectId != "" { + root.SetArgs([]string{subCommand, "update", "-i", projectId, "-n", updateProjName}) + rescueStdout := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + if err := root.Execute(); err != nil { + t.Error(err) + } + w.Close() + out, _ := io.ReadAll(r) + os.Stdout = rescueStdout + if !strings.Contains(string(out[:]), updateProjName) && + !strings.Contains(string(out[:]), projectId) { + t.Error("expected output should include " + updateProjName + projectId + ", in the out string ") + } + } + }, + }, + + { + name: "project-get-test", + fields: fields{ + MainCmd: projects.NewClient(rootClient, outputPkg.Outputer(&outputPkg.Standard{})).NewCommand(), + Outputer: outputPkg.Outputer(&outputPkg.Standard{}), + }, + want: &cobra.Command{}, + cmdFunc: func(t *testing.T, c *cobra.Command, rootCmd *cobra.Command, projectID string) { + root := c.Root() + projName := "metal-cli-" + randName + "-project-get-test" + + projectId, cleanProject := setupTestProject(t, projName) + defer cleanProject() + + if projectId != "" { + root.SetArgs([]string{subCommand, "get"}) + rescueStdout := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + if err := root.Execute(); err != nil { + t.Error(err) + } + w.Close() + out, _ := io.ReadAll(r) + os.Stdout = rescueStdout + if !strings.Contains(string(out[:]), projName) && + !strings.Contains(string(out[:]), projectId) { + t.Error("expected output should include " + projName + projectId + ", in the out string ") + } + } + }, + }, + + { + name: "project-get-id-test", + fields: fields{ + MainCmd: projects.NewClient(rootClient, outputPkg.Outputer(&outputPkg.Standard{})).NewCommand(), + Outputer: outputPkg.Outputer(&outputPkg.Standard{}), + }, + want: &cobra.Command{}, + cmdFunc: func(t *testing.T, c *cobra.Command, rootCmd *cobra.Command, projectID string) { + root := c.Root() + projName := "metal-cli-" + randName + "-project-get-id-test" + + projectId, cleanProject := setupTestProject(t, projName) + defer cleanProject() + + if projectId != "" { + root.SetArgs([]string{subCommand, "get", "-i", projectId}) + rescueStdout := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + if err := root.Execute(); err != nil { + t.Error(err) + } + w.Close() + out, _ := io.ReadAll(r) + os.Stdout = rescueStdout + if !strings.Contains(string(out[:]), projName) && + !strings.Contains(string(out[:]), projectId) { + t.Error("expected output should include " + projName + projectId + ", in the out string ") + } + } + }, + }, + + { + name: "project-delete-test", + fields: fields{ + MainCmd: projects.NewClient(rootClient, outputPkg.Outputer(&outputPkg.Standard{})).NewCommand(), + Outputer: outputPkg.Outputer(&outputPkg.Standard{}), + }, + want: &cobra.Command{}, + cmdFunc: func(t *testing.T, c *cobra.Command, rootCmd *cobra.Command, projectID string) { + root := c.Root() + + projName := "metal-cli-" + randName + "-project-delete-test" + + projectId, _ := setupTestProject(t, projName) + + if projectId != "" { + root.SetArgs([]string{subCommand, "delete", "-i", projectId, "-f"}) + rescueStdout := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + if err := root.Execute(); err != nil { + t.Error(err) + } + w.Close() + out, _ := io.ReadAll(r) + os.Stdout = rescueStdout + expectedOut := fmt.Sprintf("Project %s successfully deleted.", projectId) + if !strings.Contains(string(out[:]), expectedOut) { + t.Error(fmt.Errorf("expected output: '%s' but got '%s'", expectedOut, string(out))) + } + } + }, + }, + + { + name: "project-bgpenbale-test", + fields: fields{ + MainCmd: projects.NewClient(rootClient, outputPkg.Outputer(&outputPkg.Standard{})).NewCommand(), + Outputer: outputPkg.Outputer(&outputPkg.Standard{}), + }, + want: &cobra.Command{}, + cmdFunc: func(t *testing.T, c *cobra.Command, rootCmd *cobra.Command, projectID string) { + root := c.Root() + + projName := "metal-cli-" + randName + "-project-bgpenable-test" + + projectId, cleanProject := setupTestProject(t, projName) + defer cleanProject() + asn := "65000" + dtype := "local" + + if projectId != "" { + root.SetArgs([]string{subCommand, "bgp-enable", "--project-id", projectId, "--deployment-type", dtype, "--asn", asn}) + rescueStdout := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + if err := root.Execute(); err != nil { + t.Error(err) + } + w.Close() + out, _ := io.ReadAll(r) + os.Stdout = rescueStdout + if !strings.Contains(string(out[:]), projectId) && + !strings.Contains(string(out[:]), asn) && + !strings.Contains(string(out[:]), dtype) { + t.Error("expected output should include " + projectId + "," + asn + " and " + dtype + ", in the out string ") + } + } + }, + }, + + { + name: "project-bgpconfig-test", + fields: fields{ + MainCmd: projects.NewClient(rootClient, outputPkg.Outputer(&outputPkg.Standard{})).NewCommand(), + Outputer: outputPkg.Outputer(&outputPkg.Standard{}), + }, + want: &cobra.Command{}, + cmdFunc: func(t *testing.T, c *cobra.Command, rootCmd *cobra.Command, projectID string) { + root := c.Root() + + projName := "metal-cli-" + randName + "-project-bgpconfig-test" + + projectId, cleanProject := setupTestProject(t, projName) + defer cleanProject() + + err := helper.CreateTestBgpEnableTest(projectId) + if err != nil { + t.Error(err) + } + + asn := "65000" + dtype := "local" + status := "enabled" + + if projectId != "" { + root.SetArgs([]string{subCommand, "bgp-config", "--project-id", projectId}) + rescueStdout := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + if err := root.Execute(); err != nil { + t.Error(err) + } + w.Close() + out, _ := io.ReadAll(r) + os.Stdout = rescueStdout + if !strings.Contains(string(out[:]), projectId) && + !strings.Contains(string(out[:]), asn) && + !strings.Contains(string(out[:]), status) && + !strings.Contains(string(out[:]), dtype) { + t.Error("expected output should include " + projectId + "," + asn + "," + dtype + " and " + status + ", in the out string ") + } + } + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rootCmd := rootClient.NewCommand() + rootCmd.AddCommand(tt.fields.MainCmd) + tt.cmdFunc(t, tt.fields.MainCmd, rootCmd, "") + }) + } +} diff --git a/test/helper/helper.go b/test/helper/helper.go index ad39935b..0e2088e3 100644 --- a/test/helper/helper.go +++ b/test/helper/helper.go @@ -2,6 +2,8 @@ package helper import ( "context" + "crypto/rand" + "encoding/base64" "fmt" "os" "testing" @@ -14,6 +16,8 @@ import ( func TestClient() *openapiclient.APIClient { configuration := openapiclient.NewConfiguration() configuration.AddDefaultHeader("X-Auth-Token", os.Getenv("METAL_AUTH_TOKEN")) + // For debug purpose + //configuration.Debug = true apiClient := openapiclient.NewAPIClient(configuration) return apiClient } @@ -358,3 +362,74 @@ func CleanTestGateway(gatewayId string) error { return nil } + +func CreateTestOrganization(name string) (string, error) { + TestApiClient := TestClient() + + organizationInput := openapiclient.NewOrganizationInput() + organizationInput.Name = &name + organizationInput.Description = &name + + addr := openapiclient.NewAddressWithDefaults() + addr.SetAddress("Boston") + addr.SetCity("Boston") + addr.SetCountry("US") + addr.SetZipCode("02108") + organizationInput.Address = addr + + resp, _, err := TestApiClient.OrganizationsApi.CreateOrganization(context.Background()).OrganizationInput(*organizationInput).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `OrganizationsApi.CreateOrganization``: %v\n", err) + return "", err + } + + return resp.GetId(), nil +} + +func CleanTestOrganization(orgId string) error { + TestApiClient := TestClient() + + _, err := TestApiClient.OrganizationsApi.DeleteOrganization(context.Background(), orgId).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `OrganizationsApi.DeleteOrganization``: %v\n", err) + return err + } + + return nil +} + +func CreateTestBgpEnableTest(projId string) error { + TestApiClient := TestClient() + + bgpConfigRequestInput := *openapiclient.NewBgpConfigRequestInput(int32(65000), openapiclient.BgpConfigRequestInputDeploymentType("local")) + + _, err := TestApiClient.BGPApi.RequestBgpConfig(context.Background(), projId).BgpConfigRequestInput(bgpConfigRequestInput).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `BGPApi.RequestBgpConfig``: %v\n", err) + return err + } + return nil +} + +//nolint:staticcheck +func GenerateRandomString(length int) (string, error) { + // Calculate the number of bytes needed for the given string length + numBytes := (length * 3) / 4 + + // Create a byte slice to store the random bytes + randomBytes := make([]byte, numBytes) + + // Read random bytes from the crypto/rand package + _, err := rand.Read(randomBytes) + if err != nil { + return "", err + } + + // Encode the random bytes to base64 to get a string + randomString := base64.URLEncoding.EncodeToString(randomBytes) + + // Trim any padding characters + randomString = randomString[:length] + + return randomString, nil +}