-
Notifications
You must be signed in to change notification settings - Fork 33
feat(iaas): List NICs for Project #1242
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,12 @@ | ||
| package list | ||
|
|
||
| import ( | ||
| "cmp" | ||
| "context" | ||
| "fmt" | ||
| "slices" | ||
|
|
||
| "github.com/stackitcloud/stackit-cli/internal/pkg/projectname" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
|
|
||
| "github.com/spf13/cobra" | ||
|
|
@@ -30,7 +33,7 @@ type inputModel struct { | |
| *globalflags.GlobalFlagModel | ||
| Limit *int64 | ||
| LabelSelector *string | ||
| NetworkId string | ||
| NetworkId *string | ||
| } | ||
|
|
||
| func NewCmd(params *types.CmdParams) *cobra.Command { | ||
|
|
@@ -40,6 +43,11 @@ func NewCmd(params *types.CmdParams) *cobra.Command { | |
| Long: "Lists all network interfaces of a network.", | ||
| Args: args.NoArgs, | ||
| Example: examples.Build( | ||
| // Note: this subcommand uses two different API enpoints, which makes the implementation somewhat messy | ||
| examples.NewExample( | ||
| `Lists all network interfaces`, | ||
| `$ stackit network-interface list`, | ||
| ), | ||
| examples.NewExample( | ||
| `Lists all network interfaces with network ID "xxx"`, | ||
| `$ stackit network-interface list --network-id xxx`, | ||
|
|
@@ -70,22 +78,50 @@ func NewCmd(params *types.CmdParams) *cobra.Command { | |
| return err | ||
| } | ||
|
|
||
| // Call API | ||
| req := buildRequest(ctx, model, apiClient) | ||
| if model.NetworkId == nil { | ||
| // Call API to get all NICs in the Project | ||
| req := buildProjectRequest(ctx, model, apiClient) | ||
|
|
||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("list network interfaces: %w", err) | ||
| } | ||
|
|
||
| if resp.Items == nil || len(*resp.Items) == 0 { | ||
| projectLabel, err := projectname.GetProjectName(ctx, params.Printer, params.CliVersion, cmd) | ||
| if err != nil { | ||
| projectLabel = model.ProjectId | ||
| } | ||
| params.Printer.Outputf("No network interfaces found for project %q\n", projectLabel) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as in your other PR. This belongs into |
||
| return nil | ||
| } | ||
|
|
||
| // Truncate output | ||
| items := *resp.Items | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential nil pointer dereference, take care of it please
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| if model.Limit != nil && len(items) > int(*model.Limit) { | ||
| items = items[:*model.Limit] | ||
| } | ||
|
|
||
| return outputProjectResult(params.Printer, model.OutputFormat, items) | ||
| } | ||
|
|
||
| // Call API to get NICs for one Network | ||
| req := buildNetworkRequest(ctx, model, apiClient) | ||
|
|
||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("list network interfaces: %w", err) | ||
| } | ||
|
|
||
| if resp.Items == nil || len(*resp.Items) == 0 { | ||
| networkLabel, err := iaasUtils.GetNetworkName(ctx, apiClient, model.ProjectId, model.Region, model.NetworkId) | ||
| networkLabel, err := iaasUtils.GetNetworkName(ctx, apiClient, model.ProjectId, model.Region, *model.NetworkId) | ||
| if err != nil { | ||
| params.Printer.Debug(print.ErrorLevel, "get network name: %v", err) | ||
| networkLabel = model.NetworkId | ||
| networkLabel = *model.NetworkId | ||
rubenhoenle marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } else if networkLabel == "" { | ||
| networkLabel = model.NetworkId | ||
| networkLabel = *model.NetworkId | ||
| } | ||
| params.Printer.Info("No network interfaces found for network %q\n", networkLabel) | ||
| params.Printer.Outputf("No network interfaces found for network %q\n", networkLabel) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. invalid JSON/YAML output, see comment above |
||
| return nil | ||
| } | ||
|
|
||
|
|
@@ -95,7 +131,7 @@ func NewCmd(params *types.CmdParams) *cobra.Command { | |
| items = items[:*model.Limit] | ||
| } | ||
|
|
||
| return outputResult(params.Printer, model.OutputFormat, items) | ||
| return outputNetworkResult(params.Printer, model.OutputFormat, items) | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
|
|
@@ -106,9 +142,6 @@ func configureFlags(cmd *cobra.Command) { | |
| cmd.Flags().Var(flags.UUIDFlag(), networkIdFlag, "Network ID") | ||
| cmd.Flags().Int64(limitFlag, 0, "Maximum number of entries to list") | ||
| cmd.Flags().String(labelSelectorFlag, "", "Filter by label") | ||
|
|
||
| err := flags.MarkFlagsRequired(cmd, networkIdFlag) | ||
| cobra.CheckErr(err) | ||
| } | ||
|
|
||
| func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel, error) { | ||
|
|
@@ -129,23 +162,60 @@ func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel, | |
| GlobalFlagModel: globalFlags, | ||
| Limit: limit, | ||
| LabelSelector: flags.FlagToStringPointer(p, cmd, labelSelectorFlag), | ||
| NetworkId: flags.FlagToStringValue(p, cmd, networkIdFlag), | ||
| NetworkId: flags.FlagToStringPointer(p, cmd, networkIdFlag), | ||
| } | ||
|
|
||
| p.DebugInputModel(model) | ||
| return &model, nil | ||
| } | ||
|
|
||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiListNicsRequest { | ||
| req := apiClient.ListNics(ctx, model.ProjectId, model.Region, model.NetworkId) | ||
| func buildProjectRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiListProjectNICsRequest { | ||
| req := apiClient.ListProjectNICs(ctx, model.ProjectId, model.Region) | ||
| if model.LabelSelector != nil { | ||
| req = req.LabelSelector(*model.LabelSelector) | ||
| } | ||
|
|
||
| return req | ||
| } | ||
|
|
||
| func outputResult(p *print.Printer, outputFormat string, nics []iaas.NIC) error { | ||
| func buildNetworkRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiListNicsRequest { | ||
| req := apiClient.ListNics(ctx, model.ProjectId, model.Region, *model.NetworkId) | ||
| if model.LabelSelector != nil { | ||
| req = req.LabelSelector(*model.LabelSelector) | ||
| } | ||
|
|
||
| return req | ||
| } | ||
|
|
||
| func outputProjectResult(p *print.Printer, outputFormat string, nics []iaas.NIC) error { | ||
| return p.OutputResult(outputFormat, nics, func() error { | ||
| slices.SortFunc(nics, func(a, b iaas.NIC) int { | ||
| return cmp.Compare(*a.NetworkId, *b.NetworkId) | ||
| }) | ||
|
|
||
| table := tables.NewTable() | ||
| table.SetHeader("ID", "NAME", "NETWORK ID", "NIC SECURITY", "DEVICE ID", "IPv4 ADDRESS", "STATUS", "TYPE") | ||
|
|
||
| for _, nic := range nics { | ||
| table.AddRow( | ||
| utils.PtrString(nic.Id), | ||
| utils.PtrString(nic.Name), | ||
| utils.PtrString(nic.NetworkId), | ||
| utils.PtrString(nic.NicSecurity), | ||
| utils.PtrString(nic.Device), | ||
| utils.PtrString(nic.Ipv4), | ||
| utils.PtrString(nic.Status), | ||
| utils.PtrString(nic.Type), | ||
| ) | ||
| table.AddSeparator() | ||
| } | ||
|
|
||
| p.Outputln(table.Render()) | ||
| return nil | ||
| }) | ||
| } | ||
|
|
||
| func outputNetworkResult(p *print.Printer, outputFormat string, nics []iaas.NIC) error { | ||
| return p.OutputResult(outputFormat, nics, func() error { | ||
| table := tables.NewTable() | ||
| table.SetHeader("ID", "NAME", "NIC SECURITY", "DEVICE ID", "IPv4 ADDRESS", "STATUS", "TYPE") | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,15 +53,24 @@ func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | |
| }, | ||
| Limit: utils.Ptr(int64(10)), | ||
| LabelSelector: utils.Ptr(testLabelSelector), | ||
| NetworkId: testNetworkId, | ||
| NetworkId: utils.Ptr(testNetworkId), | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
|
|
||
| func fixtureRequest(mods ...func(request *iaas.ApiListNicsRequest)) iaas.ApiListNicsRequest { | ||
| func fixtureProjectRequest(mods ...func(request *iaas.ApiListProjectNICsRequest)) iaas.ApiListProjectNICsRequest { | ||
| request := testClient.ListProjectNICs(testCtx, testProjectId, testRegion) | ||
| request = request.LabelSelector(testLabelSelector) | ||
| for _, mod := range mods { | ||
| mod(&request) | ||
| } | ||
| return request | ||
| } | ||
|
|
||
| func fixtureNetworkRequest(mods ...func(request *iaas.ApiListNicsRequest)) iaas.ApiListNicsRequest { | ||
| request := testClient.ListNics(testCtx, testProjectId, testRegion, testNetworkId) | ||
| request = request.LabelSelector(testLabelSelector) | ||
| for _, mod := range mods { | ||
|
|
@@ -148,7 +157,35 @@ func TestParseInput(t *testing.T) { | |
| } | ||
| } | ||
|
|
||
| func TestBuildRequest(t *testing.T) { | ||
| func TestBuildProjectRequest(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| model *inputModel | ||
| expectedRequest iaas.ApiListProjectNICsRequest | ||
| }{ | ||
| { | ||
| description: "base", | ||
| model: fixtureInputModel(), | ||
| expectedRequest: fixtureProjectRequest(), | ||
| }, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| request := buildProjectRequest(testCtx, tt.model, testClient) | ||
|
|
||
| diff := cmp.Diff(request, tt.expectedRequest, | ||
| cmp.AllowUnexported(tt.expectedRequest), | ||
| cmpopts.EquateComparable(testCtx), | ||
| ) | ||
| if diff != "" { | ||
| t.Fatalf("Data does not match: %s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestBuildNetworkRequest(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| model *inputModel | ||
|
|
@@ -157,13 +194,13 @@ func TestBuildRequest(t *testing.T) { | |
| { | ||
| description: "base", | ||
| model: fixtureInputModel(), | ||
| expectedRequest: fixtureRequest(), | ||
| expectedRequest: fixtureNetworkRequest(), | ||
| }, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| request := buildRequest(testCtx, tt.model, testClient) | ||
| request := buildNetworkRequest(testCtx, tt.model, testClient) | ||
|
|
||
| diff := cmp.Diff(request, tt.expectedRequest, | ||
| cmp.AllowUnexported(tt.expectedRequest), | ||
|
|
@@ -176,7 +213,7 @@ func TestBuildRequest(t *testing.T) { | |
| } | ||
| } | ||
|
|
||
| func TestOutputResult(t *testing.T) { | ||
| func TestOutputProjectResult(t *testing.T) { | ||
| type args struct { | ||
| outputFormat string | ||
| nics []iaas.NIC | ||
|
|
@@ -191,12 +228,55 @@ func TestOutputResult(t *testing.T) { | |
| args: args{}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "empty NIC in NIC-slice", | ||
| args: args{ | ||
| outputFormat: print.PrettyOutputFormat, | ||
| nics: []iaas.NIC{{}}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(&types.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputProjectResult(p, tt.args.outputFormat, tt.args.nics); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| func TestOutputNetworkResult(t *testing.T) { | ||
| type args struct { | ||
| outputFormat string | ||
| nics []iaas.NIC | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| args args | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "empty", | ||
| args: args{}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we have some more test-cases for the output result stuff please? |
||
| name: "empty NIC in NIC-slice", | ||
| args: args{ | ||
| outputFormat: print.PrettyOutputFormat, | ||
| nics: []iaas.NIC{{}}, | ||
| }, | ||
| wantErr: false, | ||
| }, | ||
| } | ||
| p := print.NewPrinter() | ||
| p.Cmd = NewCmd(&types.CmdParams{Printer: p}) | ||
| for _, tt := range tests { | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.nics); (err != nil) != tt.wantErr { | ||
| if err := outputNetworkResult(p, tt.args.outputFormat, tt.args.nics); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.