mygithub.libinneed.workers.dev/stackitcloud/stackit-cli
Advanced tools
| ## stackit network-area routing-table route create | ||
| Creates a route in a routing-table | ||
| ### Synopsis | ||
| Creates a route in a routing-table. | ||
| ``` | ||
| stackit network-area routing-table route create [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Create a route with CIDRv4 destination and IPv4 nexthop | ||
| $ stackit network-area routing-table route create --routing-table-id xxx --organization-id yyy --network-area-id zzz --destination-type cidrv4 --destination-value <ipv4-cidr> --nexthop-type ipv4 --nexthop-value <ipv4-address> | ||
| Create a route with CIDRv6 destination and IPv6 nexthop | ||
| $ stackit network-area routing-table route create --routing-table-id xxx --organization-id yyy --network-area-id zzz --destination-type cidrv6 --destination-value <ipv6-cidr> --nexthop-type ipv6 --nexthop-value <ipv6-address> | ||
| Create a route with CIDRv6 destination and Nexthop Internet | ||
| $ stackit network-area routing-table route create --routing-table-id xxx --organization-id yyy --network-area-id zzz --destination-type cidrv6 --destination-value <ipv6-cidr> --nexthop-type internet | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| --destination-type string Destination type | ||
| --destination-value string Destination value | ||
| -h, --help Help for "stackit network-area routing-table route create" | ||
| --labels stringToString Key=value labels (default []) | ||
| --network-area-id string Network-Area ID | ||
| --nexthop-type string Next hop type | ||
| --nexthop-value string NextHop value | ||
| --organization-id string Organization ID | ||
| --routing-table-id string Routing-Table ID | ||
| ``` | ||
| ### Options inherited from parent commands | ||
| ``` | ||
| -y, --assume-yes If set, skips all confirmation prompts | ||
| --async If set, runs the command asynchronously | ||
| -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] | ||
| -p, --project-id string Project ID | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit network-area routing-table route](./stackit_network-area_routing-table_route.md) - Manages routes of a routing-table | ||
| ## stackit network-area routing-table route delete | ||
| Deletes a route within a routing-table | ||
| ### Synopsis | ||
| Deletes a route within a routing-table | ||
| ``` | ||
| stackit network-area routing-table route delete routing-table-id [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Deletes a route within a routing-table | ||
| $ stackit network-area routing-table route delete xxx --routing-table-id xxx --organization-id yyy --network-area-id zzz | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit network-area routing-table route delete" | ||
| --network-area-id string Network-Area ID | ||
| --organization-id string Organization ID | ||
| --routing-table-id string Routing-Table ID | ||
| ``` | ||
| ### Options inherited from parent commands | ||
| ``` | ||
| -y, --assume-yes If set, skips all confirmation prompts | ||
| --async If set, runs the command asynchronously | ||
| -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] | ||
| -p, --project-id string Project ID | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit network-area routing-table route](./stackit_network-area_routing-table_route.md) - Manages routes of a routing-table | ||
| ## stackit network-area routing-table route describe | ||
| Describes a route within a routing-table | ||
| ### Synopsis | ||
| Describes a route within a routing-table | ||
| ``` | ||
| stackit network-area routing-table route describe ROUTE_ID [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Describe a route within a routing-table | ||
| $ stackit network-area routing-table route describe xxx --routing-table-id xxx --organization-id yyy --network-area-id zzz | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit network-area routing-table route describe" | ||
| --network-area-id string Network-Area ID | ||
| --organization-id string Organization ID | ||
| --routing-table-id string Routing-Table ID | ||
| ``` | ||
| ### Options inherited from parent commands | ||
| ``` | ||
| -y, --assume-yes If set, skips all confirmation prompts | ||
| --async If set, runs the command asynchronously | ||
| -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] | ||
| -p, --project-id string Project ID | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit network-area routing-table route](./stackit_network-area_routing-table_route.md) - Manages routes of a routing-table | ||
| ## stackit network-area routing-table route list | ||
| Lists all routes within a routing-table | ||
| ### Synopsis | ||
| Lists all routes within a routing-table | ||
| ``` | ||
| stackit network-area routing-table route list [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| List all routes within a routing-table | ||
| $ stackit network-area routing-table route list --routing-table-id xxx --organization-id yyy --network-area-id zzz | ||
| List all routes within a routing-table with labels | ||
| $ stackit network-area routing-table list --routing-table-id xxx --organization-id yyy --network-area-id zzz --label-selector env=dev,env=rc | ||
| List all routes within a routing-tables with labels and limit to 10 | ||
| $ stackit network-area routing-table list --routing-table-id xxx --organization-id yyy --network-area-id zzz --label-selector env=dev,env=rc --limit 10 | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit network-area routing-table route list" | ||
| --label-selector string Filter by label | ||
| --limit int Maximum number of entries to list | ||
| --network-area-id string Network-Area ID | ||
| --organization-id string Organization ID | ||
| --routing-table-id string Routing-Table ID | ||
| ``` | ||
| ### Options inherited from parent commands | ||
| ``` | ||
| -y, --assume-yes If set, skips all confirmation prompts | ||
| --async If set, runs the command asynchronously | ||
| -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] | ||
| -p, --project-id string Project ID | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit network-area routing-table route](./stackit_network-area_routing-table_route.md) - Manages routes of a routing-table | ||
| ## stackit network-area routing-table route update | ||
| Updates a route in a routing-table | ||
| ### Synopsis | ||
| Updates a route in a routing-table. | ||
| ``` | ||
| stackit network-area routing-table route update ROUTE_ID [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Updates the label(s) of a route with ID "xxx" in a routing-table ID "xxx" in organization with ID "yyy" and network-area with ID "zzz" | ||
| $ stackit network-area routing-table route update xxx --labels key=value,foo=bar --routing-table-id xxx --organization-id yyy --network-area-id zzz | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit network-area routing-table route update" | ||
| --labels stringToString Labels are key-value string pairs which can be attached to a route. A label can be provided with the format key=value and the flag can be used multiple times to provide a list of labels (default []) | ||
| --network-area-id string Network-Area ID | ||
| --organization-id string Organization ID | ||
| --routing-table-id string Routing-Table ID | ||
| ``` | ||
| ### Options inherited from parent commands | ||
| ``` | ||
| -y, --assume-yes If set, skips all confirmation prompts | ||
| --async If set, runs the command asynchronously | ||
| -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] | ||
| -p, --project-id string Project ID | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit network-area routing-table route](./stackit_network-area_routing-table_route.md) - Manages routes of a routing-table | ||
| ## stackit network-area routing-table route | ||
| Manages routes of a routing-table | ||
| ### Synopsis | ||
| Manages routes of a routing-table | ||
| ``` | ||
| stackit network-area routing-table route [flags] | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit network-area routing-table route" | ||
| ``` | ||
| ### Options inherited from parent commands | ||
| ``` | ||
| -y, --assume-yes If set, skips all confirmation prompts | ||
| --async If set, runs the command asynchronously | ||
| -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] | ||
| -p, --project-id string Project ID | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit network-area routing-table](./stackit_network-area_routing-table.md) - Manage routing-tables and its according routes | ||
| * [stackit network-area routing-table route create](./stackit_network-area_routing-table_route_create.md) - Creates a route in a routing-table | ||
| * [stackit network-area routing-table route delete](./stackit_network-area_routing-table_route_delete.md) - Deletes a route within a routing-table | ||
| * [stackit network-area routing-table route describe](./stackit_network-area_routing-table_route_describe.md) - Describes a route within a routing-table | ||
| * [stackit network-area routing-table route list](./stackit_network-area_routing-table_route_list.md) - Lists all routes within a routing-table | ||
| * [stackit network-area routing-table route update](./stackit_network-area_routing-table_route_update.md) - Updates a route in a routing-table | ||
| ## stackit server security-group attach | ||
| Attaches a security group to a server | ||
| ### Synopsis | ||
| Attaches a security group to a server. | ||
| ``` | ||
| stackit server security-group attach [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Attach a security group with ID "xxx" to a server with ID "yyy" | ||
| $ stackit server security-group attach --server-id yyy --security-group-id xxx | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit server security-group attach" | ||
| --security-group-id string Security Group ID | ||
| --server-id string Server ID | ||
| ``` | ||
| ### Options inherited from parent commands | ||
| ``` | ||
| -y, --assume-yes If set, skips all confirmation prompts | ||
| --async If set, runs the command asynchronously | ||
| -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] | ||
| -p, --project-id string Project ID | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit server security-group](./stackit_server_security-group.md) - Allows attaching/detaching security groups to servers | ||
| ## stackit server security-group detach | ||
| Detaches a security group from a server | ||
| ### Synopsis | ||
| Detaches a security group from a server. | ||
| ``` | ||
| stackit server security-group detach [flags] | ||
| ``` | ||
| ### Examples | ||
| ``` | ||
| Detach a security group with ID "xxx" from a server with ID "yyy" | ||
| $ stackit server security-group detach --server-id yyy --security-group-id xxx | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit server security-group detach" | ||
| --security-group-id string Security Group ID | ||
| --server-id string Server ID | ||
| ``` | ||
| ### Options inherited from parent commands | ||
| ``` | ||
| -y, --assume-yes If set, skips all confirmation prompts | ||
| --async If set, runs the command asynchronously | ||
| -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] | ||
| -p, --project-id string Project ID | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit server security-group](./stackit_server_security-group.md) - Allows attaching/detaching security groups to servers | ||
| ## stackit server security-group | ||
| Allows attaching/detaching security groups to servers | ||
| ### Synopsis | ||
| Allows attaching/detaching security groups to servers. | ||
| ``` | ||
| stackit server security-group [flags] | ||
| ``` | ||
| ### Options | ||
| ``` | ||
| -h, --help Help for "stackit server security-group" | ||
| ``` | ||
| ### Options inherited from parent commands | ||
| ``` | ||
| -y, --assume-yes If set, skips all confirmation prompts | ||
| --async If set, runs the command asynchronously | ||
| -o, --output-format string Output format, one of ["json" "pretty" "none" "yaml"] | ||
| -p, --project-id string Project ID | ||
| --region string Target region for region-specific requests | ||
| --verbosity string Verbosity of the CLI, one of ["debug" "info" "warning" "error"] (default "info") | ||
| ``` | ||
| ### SEE ALSO | ||
| * [stackit server](./stackit_server.md) - Provides functionality for servers | ||
| * [stackit server security-group attach](./stackit_server_security-group_attach.md) - Attaches a security group to a server | ||
| * [stackit server security-group detach](./stackit_server_security-group_detach.md) - Detaches a security group from a server | ||
| package create | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "time" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/iaas" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &iaas.APIClient{} | ||
| const testRegion = "eu01" | ||
| var testOrgId = uuid.NewString() | ||
| var testNetworkAreaId = uuid.NewString() | ||
| var testRoutingTableId = uuid.NewString() | ||
| const testDestinationTypeFlag = destTypeCIDRv4 | ||
| const testDestinationValueFlag = "1.1.1.0/24" | ||
| const testNextHopTypeFlag = nextHopTypeIPv4 | ||
| const testNextHopValueFlag = "1.1.1.1" | ||
| const testLabelSelectorFlag = "key1=value1,key2=value2" | ||
| var testLabels = &map[string]string{ | ||
| "key1": "value1", | ||
| "key2": "value2", | ||
| } | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.RegionFlag: testRegion, | ||
| labelFlag: testLabelSelectorFlag, | ||
| organizationIdFlag: testOrgId, | ||
| networkAreaIdFlag: testNetworkAreaId, | ||
| routingTableIdFlag: testRoutingTableId, | ||
| destinationTypeFlag: testDestinationTypeFlag, | ||
| destinationValueFlag: testDestinationValueFlag, | ||
| nextHopTypeFlag: testNextHopTypeFlag, | ||
| nextHopValueFlag: testNextHopValueFlag, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| Region: testRegion, | ||
| }, | ||
| OrganizationId: testOrgId, | ||
| NetworkAreaId: testNetworkAreaId, | ||
| RoutingTableId: testRoutingTableId, | ||
| DestinationType: testDestinationTypeFlag, | ||
| DestinationValue: utils.Ptr(testDestinationValueFlag), | ||
| NextHopType: testNextHopTypeFlag, | ||
| NextHopValue: utils.Ptr(testNextHopValueFlag), | ||
| Labels: testLabels, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureRequest(mods ...func(request *iaas.ApiAddRoutesToRoutingTableRequest)) iaas.ApiAddRoutesToRoutingTableRequest { | ||
| request := testClient.AddRoutesToRoutingTable(testCtx, testOrgId, testNetworkAreaId, testRegion, testRoutingTableId) | ||
| request = request.AddRoutesToRoutingTablePayload(fixturePayload()) | ||
| for _, mod := range mods { | ||
| mod(&request) | ||
| } | ||
| return request | ||
| } | ||
| func fixturePayload(mods ...func(payload *iaas.AddRoutesToRoutingTablePayload)) iaas.AddRoutesToRoutingTablePayload { | ||
| payload := iaas.AddRoutesToRoutingTablePayload{ | ||
| Items: &[]iaas.Route{ | ||
| { | ||
| Destination: &iaas.RouteDestination{ | ||
| DestinationCIDRv4: &iaas.DestinationCIDRv4{ | ||
| Type: utils.Ptr(testDestinationTypeFlag), | ||
| Value: utils.Ptr(testDestinationValueFlag), | ||
| }, | ||
| }, | ||
| Nexthop: &iaas.RouteNexthop{ | ||
| NexthopIPv4: &iaas.NexthopIPv4{ | ||
| Type: utils.Ptr(testNextHopTypeFlag), | ||
| Value: utils.Ptr(testNextHopValueFlag), | ||
| }, | ||
| }, | ||
| Labels: utils.ConvertStringMapToInterfaceMap(testLabels), | ||
| }, | ||
| }, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(&payload) | ||
| } | ||
| return payload | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| argValues []string | ||
| flagValues map[string]string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "valid input", | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "routing-table ID missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, routingTableIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "destination value missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, destinationValueFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "destination type missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, destinationTypeFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "next hop type missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, nextHopTypeFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "next hop value missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, nextHopValueFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "no values", | ||
| flagValues: map[string]string{}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "organization ID missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, organizationIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "organization ID invalid - empty", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[organizationIdFlag] = "" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "organization ID invalid - format", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[organizationIdFlag] = "invalid-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "network area ID missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, networkAreaIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "network area ID invalid - empty", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[networkAreaIdFlag] = "" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "network area ID invalid - format", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[networkAreaIdFlag] = "invalid-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid destination type enum", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[destinationTypeFlag] = nextHopTypeIPv4 // Deliberately invalid for dest | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "destination value not IPv4 CIDR", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[destinationValueFlag] = "0.0.0.0" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "destination value not IPv6 CIDR", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[destinationTypeFlag] = destTypeCIDRv6 | ||
| flagValues[destinationValueFlag] = "2001:db8::" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "destination value is IPv6 CIDR", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[destinationTypeFlag] = destTypeCIDRv6 | ||
| flagValues[destinationValueFlag] = "2001:db8::/32" | ||
| }), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.DestinationType = destTypeCIDRv6 | ||
| model.DestinationValue = utils.Ptr("2001:db8::/32") | ||
| }), | ||
| }, | ||
| { | ||
| description: "invalid next hop type enum", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[nextHopTypeFlag] = destTypeCIDRv4 // Deliberately invalid for hop | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "next hop type is internet and next hop value is provided", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[nextHopTypeFlag] = nextHopTypeInternet | ||
| flagValues[nextHopValueFlag] = "1.1.1.1" // should not be allowed | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "next hop type is blackhole and next hop value is provided", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[nextHopTypeFlag] = nextHopTypeBlackhole | ||
| flagValues[nextHopValueFlag] = "1.1.1.1" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "next hop type is internet and next hop value is not provided", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[nextHopTypeFlag] = nextHopTypeInternet | ||
| delete(flagValues, nextHopValueFlag) | ||
| }), | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.NextHopType = nextHopTypeInternet | ||
| model.NextHopValue = nil | ||
| }), | ||
| isValid: true, | ||
| }, | ||
| { | ||
| description: "next hop type is blackhole and next hop value is not provided", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[nextHopTypeFlag] = nextHopTypeBlackhole | ||
| delete(flagValues, nextHopValueFlag) | ||
| }), | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.NextHopType = nextHopTypeBlackhole | ||
| model.NextHopValue = nil | ||
| }), | ||
| isValid: true, | ||
| }, | ||
| { | ||
| description: "next hop type is IPv4 and next hop value is missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[nextHopTypeFlag] = nextHopTypeIPv4 | ||
| delete(flagValues, nextHopValueFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "next hop type is IPv6 and next hop value is missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[nextHopTypeFlag] = nextHopTypeIPv6 | ||
| delete(flagValues, nextHopValueFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid next hop type provided", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[nextHopTypeFlag] = "invalid-type" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "optional labels are provided", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[labelFlag] = "key=value" | ||
| }), | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.Labels = utils.Ptr(map[string]string{"key": "value"}) | ||
| }), | ||
| isValid: true, | ||
| }, | ||
| { | ||
| description: "argument value is empty string", | ||
| argValues: []string{""}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: false, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "argument value wrong", | ||
| argValues: []string{"foo-bar"}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: false, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| testutils.TestParseInput(t, NewCmd, parseInput, tt.expectedModel, tt.argValues, tt.flagValues, tt.isValid) | ||
| }) | ||
| } | ||
| } | ||
| func TestBuildNextHop(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| model *inputModel | ||
| expected *iaas.RouteNexthop | ||
| }{ | ||
| { | ||
| description: "IPv4 next hop", | ||
| model: fixtureInputModel(func(m *inputModel) { | ||
| m.NextHopType = nextHopTypeIPv4 | ||
| m.NextHopValue = utils.Ptr("1.1.1.1") | ||
| }), | ||
| expected: &iaas.RouteNexthop{ | ||
| NexthopIPv4: &iaas.NexthopIPv4{ | ||
| Type: utils.Ptr(nextHopTypeIPv4), | ||
| Value: utils.Ptr("1.1.1.1"), | ||
| }, | ||
| }, | ||
| }, | ||
| { | ||
| description: "IPv6 next hop", | ||
| model: fixtureInputModel(func(m *inputModel) { | ||
| m.NextHopType = nextHopTypeIPv6 | ||
| m.NextHopValue = utils.Ptr("::1") | ||
| }), | ||
| expected: &iaas.RouteNexthop{ | ||
| NexthopIPv6: &iaas.NexthopIPv6{ | ||
| Type: utils.Ptr(nextHopTypeIPv6), | ||
| Value: utils.Ptr("::1"), | ||
| }, | ||
| }, | ||
| }, | ||
| { | ||
| description: "Internet next hop", | ||
| model: fixtureInputModel(func(m *inputModel) { | ||
| m.NextHopType = nextHopTypeInternet | ||
| m.NextHopValue = nil | ||
| }), | ||
| expected: &iaas.RouteNexthop{ | ||
| NexthopInternet: &iaas.NexthopInternet{ | ||
| Type: utils.Ptr(nextHopTypeInternet), | ||
| }, | ||
| }, | ||
| }, | ||
| { | ||
| description: "Blackhole next hop", | ||
| model: fixtureInputModel(func(m *inputModel) { | ||
| m.NextHopType = nextHopTypeBlackhole | ||
| m.NextHopValue = nil | ||
| }), | ||
| expected: &iaas.RouteNexthop{ | ||
| NexthopBlackhole: &iaas.NexthopBlackhole{ | ||
| Type: utils.Ptr(nextHopTypeBlackhole), | ||
| }, | ||
| }, | ||
| }, | ||
| { | ||
| description: "Unsupported next hop type", | ||
| model: fixtureInputModel(func(m *inputModel) { | ||
| m.NextHopType = "unsupported" | ||
| }), | ||
| expected: nil, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| got := buildNextHop(tt.model) | ||
| if diff := cmp.Diff(tt.expected, got); diff != "" { | ||
| t.Errorf("buildNextHop() mismatch (-want +got):\n%s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| func TestBuildDestination(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| model *inputModel | ||
| expected *iaas.RouteDestination | ||
| }{ | ||
| { | ||
| description: "CIDRv4 destination", | ||
| model: fixtureInputModel(func(m *inputModel) { | ||
| m.DestinationType = destTypeCIDRv4 | ||
| m.DestinationValue = utils.Ptr("192.168.1.0/24") | ||
| }), | ||
| expected: &iaas.RouteDestination{ | ||
| DestinationCIDRv4: &iaas.DestinationCIDRv4{ | ||
| Type: utils.Ptr(destTypeCIDRv4), | ||
| Value: utils.Ptr("192.168.1.0/24"), | ||
| }, | ||
| }, | ||
| }, | ||
| { | ||
| description: "CIDRv6 destination", | ||
| model: fixtureInputModel(func(m *inputModel) { | ||
| m.DestinationType = destTypeCIDRv6 | ||
| m.DestinationValue = utils.Ptr("2001:db8::/32") | ||
| }), | ||
| expected: &iaas.RouteDestination{ | ||
| DestinationCIDRv6: &iaas.DestinationCIDRv6{ | ||
| Type: utils.Ptr(destTypeCIDRv6), | ||
| Value: utils.Ptr("2001:db8::/32"), | ||
| }, | ||
| }, | ||
| }, | ||
| { | ||
| description: "unsupported destination type", | ||
| model: fixtureInputModel(func(m *inputModel) { | ||
| m.DestinationType = "other" | ||
| m.DestinationValue = utils.Ptr("1.1.1.1") | ||
| }), | ||
| expected: nil, | ||
| }, | ||
| { | ||
| description: "nil destination value", | ||
| model: fixtureInputModel(func(m *inputModel) { | ||
| m.DestinationValue = nil | ||
| }), | ||
| expected: nil, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| got := buildDestination(tt.model) | ||
| if diff := cmp.Diff(tt.expected, got); diff != "" { | ||
| t.Errorf("buildDestination() mismatch (-want +got):\n%s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| func TestBuildRequest(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| model *inputModel | ||
| expectedRequest iaas.ApiAddRoutesToRoutingTableRequest | ||
| }{ | ||
| { | ||
| description: "base", | ||
| model: fixtureInputModel(), | ||
| expectedRequest: fixtureRequest(), | ||
| }, | ||
| { | ||
| description: "optional labels provided", | ||
| model: fixtureInputModel(func(model *inputModel) { | ||
| model.Labels = utils.Ptr(map[string]string{"key": "value"}) | ||
| }), | ||
| expectedRequest: fixtureRequest(func(request *iaas.ApiAddRoutesToRoutingTableRequest) { | ||
| *request = (*request).AddRoutesToRoutingTablePayload(fixturePayload(func(payload *iaas.AddRoutesToRoutingTablePayload) { | ||
| (*payload.Items)[0].Labels = utils.ConvertStringMapToInterfaceMap(utils.Ptr(map[string]string{"key": "value"})) | ||
| })) | ||
| }), | ||
| }, | ||
| { | ||
| description: "destination is cidrv6 and nexthop is ipv6", | ||
| model: fixtureInputModel(func(model *inputModel) { | ||
| model.DestinationType = destTypeCIDRv6 | ||
| model.DestinationValue = utils.Ptr("2001:db8::/32") | ||
| model.NextHopType = nextHopTypeIPv6 | ||
| model.NextHopValue = utils.Ptr("2001:db8::1") | ||
| }), | ||
| expectedRequest: fixtureRequest(func(request *iaas.ApiAddRoutesToRoutingTableRequest) { | ||
| *request = (*request).AddRoutesToRoutingTablePayload(iaas.AddRoutesToRoutingTablePayload{ | ||
| Items: &[]iaas.Route{ | ||
| { | ||
| Destination: &iaas.RouteDestination{ | ||
| DestinationCIDRv6: &iaas.DestinationCIDRv6{ | ||
| Type: utils.Ptr(destTypeCIDRv6), | ||
| Value: utils.Ptr("2001:db8::/32"), | ||
| }, | ||
| }, | ||
| Nexthop: &iaas.RouteNexthop{ | ||
| NexthopIPv6: &iaas.NexthopIPv6{ | ||
| Type: utils.Ptr(nextHopTypeIPv6), | ||
| Value: utils.Ptr("2001:db8::1"), | ||
| }, | ||
| }, | ||
| Labels: utils.ConvertStringMapToInterfaceMap(testLabels), | ||
| }, | ||
| }, | ||
| }) | ||
| }), | ||
| }, | ||
| { | ||
| description: "nexthop type is internet (no value)", | ||
| model: fixtureInputModel(func(model *inputModel) { | ||
| model.NextHopType = nextHopTypeInternet | ||
| model.NextHopValue = nil | ||
| }), | ||
| expectedRequest: fixtureRequest(func(request *iaas.ApiAddRoutesToRoutingTableRequest) { | ||
| payload := fixturePayload(func(payload *iaas.AddRoutesToRoutingTablePayload) { | ||
| (*payload.Items)[0].Nexthop = &iaas.RouteNexthop{ | ||
| NexthopInternet: &iaas.NexthopInternet{ | ||
| Type: utils.Ptr(nextHopTypeInternet), | ||
| }, | ||
| } | ||
| }) | ||
| *request = (*request).AddRoutesToRoutingTablePayload(payload) | ||
| }), | ||
| }, | ||
| { | ||
| description: "nexthop type is blackhole (no value)", | ||
| model: fixtureInputModel(func(model *inputModel) { | ||
| model.NextHopType = nextHopTypeBlackhole | ||
| model.NextHopValue = nil | ||
| }), | ||
| expectedRequest: fixtureRequest(func(request *iaas.ApiAddRoutesToRoutingTableRequest) { | ||
| payload := fixturePayload(func(payload *iaas.AddRoutesToRoutingTablePayload) { | ||
| (*payload.Items)[0].Nexthop = &iaas.RouteNexthop{ | ||
| NexthopBlackhole: &iaas.NexthopBlackhole{ | ||
| Type: utils.Ptr(nextHopTypeBlackhole), | ||
| }, | ||
| } | ||
| }) | ||
| *request = (*request).AddRoutesToRoutingTablePayload(payload) | ||
| }), | ||
| }, | ||
| { | ||
| description: "nexthop type is ipv4 with value", | ||
| model: fixtureInputModel(func(model *inputModel) { | ||
| model.NextHopType = nextHopTypeIPv4 | ||
| model.NextHopValue = utils.Ptr("1.2.3.4") | ||
| }), | ||
| expectedRequest: fixtureRequest(func(request *iaas.ApiAddRoutesToRoutingTableRequest) { | ||
| payload := fixturePayload(func(payload *iaas.AddRoutesToRoutingTablePayload) { | ||
| (*payload.Items)[0].Nexthop = &iaas.RouteNexthop{ | ||
| NexthopIPv4: &iaas.NexthopIPv4{ | ||
| Type: utils.Ptr(nextHopTypeIPv4), | ||
| Value: utils.Ptr("1.2.3.4"), | ||
| }, | ||
| } | ||
| }) | ||
| *request = (*request).AddRoutesToRoutingTablePayload(payload) | ||
| }), | ||
| }, | ||
| { | ||
| description: "nexthop type is ipv6 with value", | ||
| model: fixtureInputModel(func(model *inputModel) { | ||
| model.NextHopType = nextHopTypeIPv6 | ||
| model.NextHopValue = utils.Ptr("2001:db8::1") | ||
| }), | ||
| expectedRequest: fixtureRequest(func(request *iaas.ApiAddRoutesToRoutingTableRequest) { | ||
| payload := fixturePayload(func(payload *iaas.AddRoutesToRoutingTablePayload) { | ||
| (*payload.Items)[0].Nexthop = &iaas.RouteNexthop{ | ||
| NexthopIPv6: &iaas.NexthopIPv6{ | ||
| Type: utils.Ptr(nextHopTypeIPv6), | ||
| Value: utils.Ptr("2001:db8::1"), | ||
| }, | ||
| } | ||
| }) | ||
| *request = (*request).AddRoutesToRoutingTablePayload(payload) | ||
| }), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| request, err := buildRequest(testCtx, tt.model, testClient) | ||
| if err != nil { | ||
| t.Fatalf("buildRequest returned error: %v", err) | ||
| } | ||
| if diff := cmp.Diff(request, tt.expectedRequest, | ||
| cmp.AllowUnexported(tt.expectedRequest), | ||
| cmpopts.EquateComparable(testCtx)); diff != "" { | ||
| t.Errorf("buildRequest() mismatch (-got +want):\n%s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| func TestOutputResult(t *testing.T) { | ||
| dummyRoute := iaas.Route{ | ||
| Id: utils.Ptr("route-foo"), | ||
| Destination: &iaas.RouteDestination{ | ||
| DestinationCIDRv4: &iaas.DestinationCIDRv4{ | ||
| Type: utils.Ptr(destTypeCIDRv4), | ||
| Value: utils.Ptr("10.0.0.0/24"), | ||
| }, | ||
| }, | ||
| Nexthop: &iaas.RouteNexthop{ | ||
| NexthopIPv4: &iaas.NexthopIPv4{ | ||
| Type: utils.Ptr(nextHopTypeIPv4), | ||
| Value: utils.Ptr("10.0.0.1"), | ||
| }, | ||
| }, | ||
| Labels: utils.ConvertStringMapToInterfaceMap(testLabels), | ||
| CreatedAt: utils.Ptr(time.Now()), | ||
| UpdatedAt: utils.Ptr(time.Now()), | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| outputFormat string | ||
| routes []iaas.Route | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "nil routes should return error", | ||
| outputFormat: print.PrettyOutputFormat, | ||
| routes: nil, | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| name: "empty routes list", | ||
| outputFormat: print.PrettyOutputFormat, | ||
| routes: []iaas.Route{}, | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| name: "route list with empty struct", | ||
| outputFormat: print.PrettyOutputFormat, | ||
| routes: []iaas.Route{{}}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "pretty output with one route", | ||
| outputFormat: print.PrettyOutputFormat, | ||
| routes: []iaas.Route{dummyRoute}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "pretty output with multiple routes", | ||
| outputFormat: print.PrettyOutputFormat, | ||
| routes: []iaas.Route{dummyRoute, dummyRoute, dummyRoute}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "json output with one route", | ||
| outputFormat: print.JSONOutputFormat, | ||
| routes: []iaas.Route{dummyRoute}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "yaml output with one route", | ||
| outputFormat: print.YAMLOutputFormat, | ||
| routes: []iaas.Route{dummyRoute}, | ||
| 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.outputFormat, tt.routes); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package create | ||
| import ( | ||
| "context" | ||
| "errors" | ||
| "fmt" | ||
| "strings" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/examples" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/flags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/client" | ||
| iaasUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/utils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/iaas" | ||
| ) | ||
| const ( | ||
| destinationTypeFlag = "destination-type" | ||
| destinationValueFlag = "destination-value" | ||
| labelFlag = "labels" | ||
| networkAreaIdFlag = "network-area-id" | ||
| nextHopTypeFlag = "nexthop-type" | ||
| nextHopValueFlag = "nexthop-value" | ||
| organizationIdFlag = "organization-id" | ||
| routingTableIdFlag = "routing-table-id" | ||
| // Destination Type Constants | ||
| destTypeCIDRv4 = "cidrv4" | ||
| destTypeCIDRv6 = "cidrv6" | ||
| // NextHop Type Constants | ||
| nextHopTypeIPv4 = "ipv4" | ||
| nextHopTypeIPv6 = "ipv6" | ||
| nextHopTypeInternet = "internet" | ||
| nextHopTypeBlackhole = "blackhole" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| DestinationType string | ||
| DestinationValue *string | ||
| Labels *map[string]string | ||
| NetworkAreaId string | ||
| NextHopType string | ||
| NextHopValue *string | ||
| OrganizationId string | ||
| RoutingTableId string | ||
| } | ||
| func NewCmd(params *types.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "create", | ||
| Short: "Creates a route in a routing-table", | ||
| Long: "Creates a route in a routing-table.", | ||
| Args: args.NoArgs, | ||
| Example: examples.Build( | ||
| examples.NewExample("Create a route with CIDRv4 destination and IPv4 nexthop", | ||
| `$ stackit network-area routing-table route create --routing-table-id xxx --organization-id yyy --network-area-id zzz --destination-type cidrv4 --destination-value <ipv4-cidr> --nexthop-type ipv4 --nexthop-value <ipv4-address>`), | ||
| examples.NewExample("Create a route with CIDRv6 destination and IPv6 nexthop", | ||
| `$ stackit network-area routing-table route create --routing-table-id xxx --organization-id yyy --network-area-id zzz --destination-type cidrv6 --destination-value <ipv6-cidr> --nexthop-type ipv6 --nexthop-value <ipv6-address>`), | ||
| examples.NewExample("Create a route with CIDRv6 destination and Nexthop Internet", | ||
| `$ stackit network-area routing-table route create --routing-table-id xxx --organization-id yyy --network-area-id zzz --destination-type cidrv6 --destination-value <ipv6-cidr> --nexthop-type internet`), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, _ []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd, nil) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| routingTableLabel, err := iaasUtils.GetRoutingTableOfAreaName(ctx, apiClient, model.OrganizationId, model.NetworkAreaId, model.Region, model.RoutingTableId) | ||
| if err != nil { | ||
| params.Printer.Debug(print.ErrorLevel, "get routing-table name: %v", err) | ||
| routingTableLabel = model.RoutingTableId | ||
| } else if routingTableLabel == "" { | ||
| routingTableLabel = model.RoutingTableId | ||
| } | ||
| prompt := fmt.Sprintf("Are you sure you want to create a route for routing-table %q?", routingTableLabel) | ||
| err = params.Printer.PromptForConfirmation(prompt) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| req, err := buildRequest(ctx, model, apiClient) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("create route request failed: %w", err) | ||
| } | ||
| return outputResult(params.Printer, model.OutputFormat, resp.GetItems()) | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().Var(flags.CIDRFlag(), destinationValueFlag, "Destination value") | ||
| cmd.Flags().String(nextHopValueFlag, "", "NextHop value") | ||
| cmd.Flags().Var(flags.UUIDFlag(), networkAreaIdFlag, "Network-Area ID") | ||
| cmd.Flags().Var(flags.UUIDFlag(), organizationIdFlag, "Organization ID") | ||
| cmd.Flags().Var(flags.UUIDFlag(), routingTableIdFlag, "Routing-Table ID") | ||
| cmd.Flags().Var( | ||
| flags.EnumFlag(true, "", destTypeCIDRv4, destTypeCIDRv6), | ||
| destinationTypeFlag, | ||
| "Destination type") | ||
| cmd.Flags().Var( | ||
| flags.EnumFlag(true, "", nextHopTypeIPv4, nextHopTypeIPv6, nextHopTypeInternet, nextHopTypeBlackhole), | ||
| nextHopTypeFlag, | ||
| "Next hop type") | ||
| cmd.Flags().StringToString(labelFlag, nil, "Key=value labels") | ||
| err := flags.MarkFlagsRequired(cmd, organizationIdFlag, networkAreaIdFlag, routingTableIdFlag, destinationTypeFlag, destinationValueFlag, nextHopTypeFlag) | ||
| cobra.CheckErr(err) | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| model := &inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| DestinationType: flags.FlagToStringValue(p, cmd, destinationTypeFlag), | ||
| DestinationValue: flags.FlagToStringPointer(p, cmd, destinationValueFlag), | ||
| Labels: flags.FlagToStringToStringPointer(p, cmd, labelFlag), | ||
| NetworkAreaId: flags.FlagToStringValue(p, cmd, networkAreaIdFlag), | ||
| NextHopType: flags.FlagToStringValue(p, cmd, nextHopTypeFlag), | ||
| NextHopValue: flags.FlagToStringPointer(p, cmd, nextHopValueFlag), | ||
| OrganizationId: flags.FlagToStringValue(p, cmd, organizationIdFlag), | ||
| RoutingTableId: flags.FlagToStringValue(p, cmd, routingTableIdFlag), | ||
| } | ||
| // Next Hop validation logic | ||
| switch strings.ToLower(model.NextHopType) { | ||
| case nextHopTypeInternet, nextHopTypeBlackhole: | ||
| if model.NextHopValue != nil && *model.NextHopValue != "" { | ||
| return nil, errors.New("--nexthop-value is not allowed when --nexthop-type is 'internet' or 'blackhole'") | ||
| } | ||
| case nextHopTypeIPv4, nextHopTypeIPv6: | ||
| if model.NextHopValue == nil || *model.NextHopValue == "" { | ||
| return nil, errors.New("--nexthop-value is required when --nexthop-type is 'ipv4' or 'ipv6'") | ||
| } | ||
| default: | ||
| return nil, fmt.Errorf("invalid nexthop-type: %q", model.NextHopType) | ||
| } | ||
| p.DebugInputModel(model) | ||
| return model, nil | ||
| } | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) (iaas.ApiAddRoutesToRoutingTableRequest, error) { | ||
| destination := buildDestination(model) | ||
| nextHop := buildNextHop(model) | ||
| if destination != nil && nextHop != nil { | ||
| payload := iaas.AddRoutesToRoutingTablePayload{ | ||
| Items: &[]iaas.Route{ | ||
| { | ||
| Destination: destination, | ||
| Nexthop: nextHop, | ||
| Labels: utils.ConvertStringMapToInterfaceMap(model.Labels), | ||
| }, | ||
| }, | ||
| } | ||
| return apiClient.AddRoutesToRoutingTable( | ||
| ctx, | ||
| model.OrganizationId, | ||
| model.NetworkAreaId, | ||
| model.Region, | ||
| model.RoutingTableId, | ||
| ).AddRoutesToRoutingTablePayload(payload), nil | ||
| } | ||
| return nil, fmt.Errorf("invalid input") | ||
| } | ||
| func buildDestination(model *inputModel) *iaas.RouteDestination { | ||
| if model.DestinationValue == nil { | ||
| return nil | ||
| } | ||
| destinationType := strings.ToLower(model.DestinationType) | ||
| switch destinationType { | ||
| case destTypeCIDRv4: | ||
| return &iaas.RouteDestination{ | ||
| DestinationCIDRv4: &iaas.DestinationCIDRv4{ | ||
| Type: &model.DestinationType, | ||
| Value: model.DestinationValue, | ||
| }, | ||
| } | ||
| case destTypeCIDRv6: | ||
| return &iaas.RouteDestination{ | ||
| DestinationCIDRv6: &iaas.DestinationCIDRv6{ | ||
| Type: &model.DestinationType, | ||
| Value: model.DestinationValue, | ||
| }, | ||
| } | ||
| default: | ||
| return nil | ||
| } | ||
| } | ||
| func buildNextHop(model *inputModel) *iaas.RouteNexthop { | ||
| nextHopType := strings.ToLower(model.NextHopType) | ||
| switch nextHopType { | ||
| case nextHopTypeIPv4: | ||
| return &iaas.RouteNexthop{ | ||
| NexthopIPv4: &iaas.NexthopIPv4{ | ||
| Type: &model.NextHopType, | ||
| Value: model.NextHopValue, | ||
| }, | ||
| } | ||
| case nextHopTypeIPv6: | ||
| return &iaas.RouteNexthop{ | ||
| NexthopIPv6: &iaas.NexthopIPv6{ | ||
| Type: &model.NextHopType, | ||
| Value: model.NextHopValue, | ||
| }, | ||
| } | ||
| case nextHopTypeInternet: | ||
| return &iaas.RouteNexthop{ | ||
| NexthopInternet: &iaas.NexthopInternet{ | ||
| Type: &model.NextHopType, | ||
| }, | ||
| } | ||
| case nextHopTypeBlackhole: | ||
| return &iaas.RouteNexthop{ | ||
| NexthopBlackhole: &iaas.NexthopBlackhole{ | ||
| Type: &model.NextHopType, | ||
| }, | ||
| } | ||
| default: | ||
| return nil | ||
| } | ||
| } | ||
| func outputResult(p *print.Printer, outputFormat string, routes []iaas.Route) error { | ||
| if len(routes) == 0 { | ||
| return fmt.Errorf("create routes response is empty") | ||
| } | ||
| return p.OutputResult(outputFormat, routes, func() error { | ||
| for _, route := range routes { | ||
| p.Outputf("Created route with ID %q\n", utils.PtrString(route.Id)) | ||
| } | ||
| return nil | ||
| }) | ||
| } |
| package delete | ||
| import ( | ||
| "testing" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| ) | ||
| var ( | ||
| testOrgId = uuid.NewString() | ||
| testNetworkAreaId = uuid.NewString() | ||
| testRoutingTableId = uuid.NewString() | ||
| testRouteId = uuid.NewString() | ||
| ) | ||
| func fixtureFlagValues(mods ...func(map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| organizationIdFlag: testOrgId, | ||
| networkAreaIdFlag: testNetworkAreaId, | ||
| routingTableIdFlag: testRoutingTableId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(*inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| Verbosity: globalflags.InfoVerbosity, | ||
| }, | ||
| OrganizationId: testOrgId, | ||
| NetworkAreaId: testNetworkAreaId, | ||
| RoutingTableId: testRoutingTableId, | ||
| RouteID: testRouteId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| argValues []string | ||
| flagValues map[string]string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "valid input", | ||
| argValues: []string{testRouteId}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(m *inputModel) { | ||
| m.RouteID = testRouteId | ||
| }), | ||
| }, | ||
| { | ||
| description: "missing route id arg", | ||
| argValues: []string{}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "missing organization-id flag", | ||
| argValues: []string{testRouteId}, | ||
| flagValues: fixtureFlagValues(func(m map[string]string) { | ||
| delete(m, "organization-id") | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "missing network-area-id flag", | ||
| argValues: []string{testRouteId}, | ||
| flagValues: fixtureFlagValues(func(m map[string]string) { | ||
| delete(m, "network-area-id") | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "missing routing-table-id flag", | ||
| argValues: []string{testRouteId}, | ||
| flagValues: fixtureFlagValues(func(m map[string]string) { | ||
| delete(m, "routing-table-id") | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "arg value missing", | ||
| argValues: []string{""}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: false, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "arg value wrong", | ||
| argValues: []string{"foo-bar"}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: false, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "invalid organization-id flag", | ||
| argValues: []string{testRouteId}, | ||
| flagValues: map[string]string{"organization-id": "invalid-org"}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid network-area-id flag", | ||
| argValues: []string{testRouteId}, | ||
| flagValues: map[string]string{"network-area-id": "invalid-area"}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid routing-table-id flag", | ||
| argValues: []string{testRouteId}, | ||
| flagValues: map[string]string{"routing-table-id": "invalid-table"}, | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| testutils.TestParseInput(t, NewCmd, parseInput, tt.expectedModel, tt.argValues, tt.flagValues, tt.isValid) | ||
| }) | ||
| } | ||
| } |
| package delete | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/examples" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/flags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/client" | ||
| iaasUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/utils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| ) | ||
| const ( | ||
| networkAreaIdFlag = "network-area-id" | ||
| organizationIdFlag = "organization-id" | ||
| routeIdArg = "ROUTE_ID" | ||
| routingTableIdFlag = "routing-table-id" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| NetworkAreaId string | ||
| OrganizationId string | ||
| RouteID string | ||
| RoutingTableId string | ||
| } | ||
| func NewCmd(params *types.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: fmt.Sprintf("delete %s", routingTableIdFlag), | ||
| Short: "Deletes a route within a routing-table", | ||
| Long: "Deletes a route within a routing-table", | ||
| Args: args.SingleArg(routeIdArg, utils.ValidateUUID), | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Deletes a route within a routing-table`, | ||
| `$ stackit network-area routing-table route delete xxx --routing-table-id xxx --organization-id yyy --network-area-id zzz`, | ||
| ), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| routingTableLabel, err := iaasUtils.GetRoutingTableOfAreaName(ctx, apiClient, model.OrganizationId, model.NetworkAreaId, model.Region, model.RoutingTableId) | ||
| if err != nil { | ||
| params.Printer.Debug(print.ErrorLevel, "get routing-table name: %v", err) | ||
| routingTableLabel = model.RoutingTableId | ||
| } else if routingTableLabel == "" { | ||
| routingTableLabel = model.RoutingTableId | ||
| } | ||
| prompt := fmt.Sprintf("Are you sure you want to delete the route %q in routing-table %q for network area id %q?", model.RouteID, routingTableLabel, model.NetworkAreaId) | ||
| err = params.Printer.PromptForConfirmation(prompt) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Call API | ||
| req := apiClient.DeleteRouteFromRoutingTable( | ||
| ctx, | ||
| model.OrganizationId, | ||
| model.NetworkAreaId, | ||
| model.Region, | ||
| model.RoutingTableId, | ||
| model.RouteID, | ||
| ) | ||
| err = req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("delete route from routing-table: %w", err) | ||
| } | ||
| params.Printer.Outputf("Route %q from routing-table %q deleted.\n", model.RouteID, model.RoutingTableId) | ||
| return nil | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().Var(flags.UUIDFlag(), networkAreaIdFlag, "Network-Area ID") | ||
| cmd.Flags().Var(flags.UUIDFlag(), organizationIdFlag, "Organization ID") | ||
| cmd.Flags().Var(flags.UUIDFlag(), routingTableIdFlag, "Routing-Table ID") | ||
| err := flags.MarkFlagsRequired(cmd, organizationIdFlag, networkAreaIdFlag, routingTableIdFlag) | ||
| cobra.CheckErr(err) | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| routeId := inputArgs[0] | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| NetworkAreaId: flags.FlagToStringValue(p, cmd, networkAreaIdFlag), | ||
| OrganizationId: flags.FlagToStringValue(p, cmd, organizationIdFlag), | ||
| RouteID: routeId, | ||
| RoutingTableId: flags.FlagToStringValue(p, cmd, routingTableIdFlag), | ||
| } | ||
| p.DebugInputModel(model) | ||
| return &model, nil | ||
| } |
| package describe | ||
| import ( | ||
| "testing" | ||
| "time" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/iaas" | ||
| ) | ||
| const testRegion = "eu01" | ||
| var testOrgId = uuid.NewString() | ||
| var testNetworkAreaId = uuid.NewString() | ||
| var testRoutingTableId = uuid.NewString() | ||
| var testRouteId = uuid.NewString() | ||
| var testLabels = &map[string]string{ | ||
| "key1": "value1", | ||
| "key2": "value2", | ||
| } | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.RegionFlag: testRegion, | ||
| organizationIdFlag: testOrgId, | ||
| networkAreaIdFlag: testNetworkAreaId, | ||
| routingTableIdFlag: testRoutingTableId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| Region: testRegion, | ||
| }, | ||
| OrganizationId: testOrgId, | ||
| NetworkAreaId: testNetworkAreaId, | ||
| RoutingTableId: testRoutingTableId, | ||
| RouteID: testRouteId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureArgValues(mods ...func(argValues []string)) []string { | ||
| argValues := []string{ | ||
| testRouteId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(argValues) | ||
| } | ||
| return argValues | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| flagValues map[string]string | ||
| argValues []string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| flagValues: fixtureFlagValues(), | ||
| argValues: fixtureArgValues(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "no values", | ||
| argValues: []string{}, | ||
| flagValues: map[string]string{}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "routing-table-id missing", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, routingTableIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "network-area-id missing", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, networkAreaIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "org-id missing", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, organizationIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid routing-table-id", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[routingTableIdFlag] = "invalid-id" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "arg value missing", | ||
| argValues: []string{""}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: false, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "arg value wrong", | ||
| argValues: []string{"foo-bar"}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: false, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "invalid organization-id", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[organizationIdFlag] = "invalid-org" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid network-area-id", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[networkAreaIdFlag] = "invalid-area" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| testutils.TestParseInput(t, NewCmd, parseInput, tt.expectedModel, tt.argValues, tt.flagValues, tt.isValid) | ||
| }) | ||
| } | ||
| } | ||
| func TestOutputResult(t *testing.T) { | ||
| dummyRoute := iaas.Route{ | ||
| Id: utils.Ptr("route-foo"), | ||
| Destination: &iaas.RouteDestination{ | ||
| DestinationCIDRv4: &iaas.DestinationCIDRv4{ | ||
| Type: utils.Ptr("cidrv4"), | ||
| Value: utils.Ptr("10.0.0.0/24"), | ||
| }, | ||
| }, | ||
| Nexthop: &iaas.RouteNexthop{ | ||
| NexthopIPv4: &iaas.NexthopIPv4{ | ||
| Type: utils.Ptr("ipv4"), | ||
| Value: utils.Ptr("10.0.0.1"), | ||
| }, | ||
| }, | ||
| Labels: utils.ConvertStringMapToInterfaceMap(testLabels), | ||
| CreatedAt: utils.Ptr(time.Now()), | ||
| UpdatedAt: utils.Ptr(time.Now()), | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| outputFormat string | ||
| route *iaas.Route | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "nil route should return error", | ||
| outputFormat: print.PrettyOutputFormat, | ||
| route: nil, | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| name: "empty route", | ||
| outputFormat: print.PrettyOutputFormat, | ||
| route: &iaas.Route{}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "json empty route", | ||
| outputFormat: print.JSONOutputFormat, | ||
| route: &iaas.Route{}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "pretty output with one route", | ||
| outputFormat: print.PrettyOutputFormat, | ||
| route: &dummyRoute, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "json output with one route", | ||
| outputFormat: print.JSONOutputFormat, | ||
| route: &dummyRoute, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "yaml output with one route", | ||
| outputFormat: print.YAMLOutputFormat, | ||
| route: &dummyRoute, | ||
| 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.outputFormat, tt.route); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package describe | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "strings" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/examples" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/flags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/client" | ||
| routeUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/network-area/routing-table/utils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/tables" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/iaas" | ||
| ) | ||
| const ( | ||
| networkAreaIdFlag = "network-area-id" | ||
| organizationIdFlag = "organization-id" | ||
| routeIdArg = "ROUTE_ID" | ||
| routingTableIdFlag = "routing-table-id" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| NetworkAreaId string | ||
| OrganizationId string | ||
| RouteID string | ||
| RoutingTableId string | ||
| } | ||
| func NewCmd(params *types.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: fmt.Sprintf("describe %s", routeIdArg), | ||
| Short: "Describes a route within a routing-table", | ||
| Long: "Describes a route within a routing-table", | ||
| Args: args.SingleArg(routeIdArg, utils.ValidateUUID), | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Describe a route within a routing-table`, | ||
| `$ stackit network-area routing-table route describe xxx --routing-table-id xxx --organization-id yyy --network-area-id zzz`, | ||
| ), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Call API | ||
| request := apiClient.GetRouteOfRoutingTable( | ||
| ctx, | ||
| model.OrganizationId, | ||
| model.NetworkAreaId, | ||
| model.Region, | ||
| model.RoutingTableId, | ||
| model.RouteID, | ||
| ) | ||
| response, err := request.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("describe route: %w", err) | ||
| } | ||
| return outputResult(params.Printer, model.OutputFormat, response) | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().Var(flags.UUIDFlag(), networkAreaIdFlag, "Network-Area ID") | ||
| cmd.Flags().Var(flags.UUIDFlag(), organizationIdFlag, "Organization ID") | ||
| cmd.Flags().Var(flags.UUIDFlag(), routingTableIdFlag, "Routing-Table ID") | ||
| err := flags.MarkFlagsRequired(cmd, organizationIdFlag, networkAreaIdFlag, routingTableIdFlag) | ||
| cobra.CheckErr(err) | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| routeId := inputArgs[0] | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| NetworkAreaId: flags.FlagToStringValue(p, cmd, networkAreaIdFlag), | ||
| OrganizationId: flags.FlagToStringValue(p, cmd, organizationIdFlag), | ||
| RouteID: routeId, | ||
| RoutingTableId: flags.FlagToStringValue(p, cmd, routingTableIdFlag), | ||
| } | ||
| p.DebugInputModel(model) | ||
| return &model, nil | ||
| } | ||
| func outputResult(p *print.Printer, outputFormat string, route *iaas.Route) error { | ||
| if route == nil { | ||
| return fmt.Errorf("describe route response is empty") | ||
| } | ||
| return p.OutputResult(outputFormat, route, func() error { | ||
| routeDetails := routeUtils.ExtractRouteDetails(*route) | ||
| table := tables.NewTable() | ||
| table.AddRow("ID", utils.PtrString(route.Id)) | ||
| table.AddSeparator() | ||
| table.AddRow("DESTINATION TYPE", routeDetails.DestType) | ||
| table.AddSeparator() | ||
| table.AddRow("DESTINATION VALUE", routeDetails.DestValue) | ||
| table.AddSeparator() | ||
| table.AddRow("NEXTHOP TYPE", routeDetails.HopType) | ||
| table.AddSeparator() | ||
| table.AddRow("NEXTHOP VALUE", routeDetails.HopValue) | ||
| table.AddSeparator() | ||
| if route.Labels != nil && len(*route.Labels) > 0 { | ||
| var labels []string | ||
| for key, value := range *route.Labels { | ||
| labels = append(labels, fmt.Sprintf("%s: %s", key, value)) | ||
| } | ||
| table.AddRow("LABELS", strings.Join(labels, "\n")) | ||
| table.AddSeparator() | ||
| } | ||
| table.AddRow("CREATED AT", routeDetails.CreatedAt) | ||
| table.AddSeparator() | ||
| table.AddRow("UPDATED AT", routeDetails.UpdatedAt) | ||
| table.AddSeparator() | ||
| err := table.Display(p) | ||
| if err != nil { | ||
| return fmt.Errorf("render table: %w", err) | ||
| } | ||
| return nil | ||
| }) | ||
| } |
| package list | ||
| import ( | ||
| "strconv" | ||
| "testing" | ||
| "time" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/iaas" | ||
| ) | ||
| const testRegion = "eu01" | ||
| var testOrgId = uuid.NewString() | ||
| var testNetworkAreaId = uuid.NewString() | ||
| var testRoutingTableId = uuid.NewString() | ||
| const testLabelSelectorFlag = "key1=value1,key2=value2" | ||
| var testLabels = &map[string]string{ | ||
| "key1": "value1", | ||
| "key2": "value2", | ||
| } | ||
| var testLimitFlag = int64(10) | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.RegionFlag: testRegion, | ||
| organizationIdFlag: testOrgId, | ||
| networkAreaIdFlag: testNetworkAreaId, | ||
| routingTableIdFlag: testRoutingTableId, | ||
| labelSelectorFlag: testLabelSelectorFlag, | ||
| limitFlag: strconv.Itoa(int(testLimitFlag)), | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| Region: testRegion, | ||
| }, | ||
| OrganizationId: testOrgId, | ||
| NetworkAreaId: testNetworkAreaId, | ||
| RoutingTableId: testRoutingTableId, | ||
| LabelSelector: utils.Ptr(testLabelSelectorFlag), | ||
| Limit: utils.Ptr(testLimitFlag), | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| flagValues map[string]string | ||
| argValues []string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "no values", | ||
| flagValues: map[string]string{}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "routing-table-id missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, routingTableIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "network-area-id missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, networkAreaIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "org-id missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, organizationIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "labels missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, labelSelectorFlag) | ||
| }), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.LabelSelector = nil | ||
| }), | ||
| }, | ||
| { | ||
| description: "limit missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, limitFlag) | ||
| }), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.Limit = nil | ||
| }), | ||
| }, | ||
| { | ||
| description: "invalid limit flag", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[limitFlag] = "invalid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "negative limit flag", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[limitFlag] = "-10" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "limit zero flag", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[limitFlag] = "0" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| testutils.TestParseInput(t, NewCmd, parseInput, tt.expectedModel, tt.argValues, tt.flagValues, tt.isValid) | ||
| }) | ||
| } | ||
| } | ||
| func TestOutputResult(t *testing.T) { | ||
| dummyRoute := iaas.Route{ | ||
| Id: utils.Ptr("route-foo"), | ||
| Destination: &iaas.RouteDestination{ | ||
| DestinationCIDRv4: &iaas.DestinationCIDRv4{ | ||
| Type: utils.Ptr("cidrv4"), | ||
| Value: utils.Ptr("10.0.0.0/24"), | ||
| }, | ||
| }, | ||
| Nexthop: &iaas.RouteNexthop{ | ||
| NexthopIPv4: &iaas.NexthopIPv4{ | ||
| Type: utils.Ptr("ipv4"), | ||
| Value: utils.Ptr("10.0.0.1"), | ||
| }, | ||
| }, | ||
| Labels: utils.ConvertStringMapToInterfaceMap(testLabels), | ||
| CreatedAt: utils.Ptr(time.Now()), | ||
| UpdatedAt: utils.Ptr(time.Now()), | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| outputFormat string | ||
| routes []iaas.Route | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "nil routes should return error", | ||
| outputFormat: print.PrettyOutputFormat, | ||
| routes: nil, | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| name: "empty routes list", | ||
| outputFormat: print.PrettyOutputFormat, | ||
| routes: []iaas.Route{}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "empty routes list json output", | ||
| outputFormat: print.JSONOutputFormat, | ||
| routes: []iaas.Route{}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "empty routes list json output", | ||
| outputFormat: print.YAMLOutputFormat, | ||
| routes: []iaas.Route{}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "route list with empty struct", | ||
| outputFormat: print.PrettyOutputFormat, | ||
| routes: []iaas.Route{{}}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "pretty output with one route", | ||
| outputFormat: print.PrettyOutputFormat, | ||
| routes: []iaas.Route{dummyRoute}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "pretty output with multiple routes", | ||
| outputFormat: print.PrettyOutputFormat, | ||
| routes: []iaas.Route{dummyRoute, dummyRoute, dummyRoute}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "json output with one route", | ||
| outputFormat: print.JSONOutputFormat, | ||
| routes: []iaas.Route{dummyRoute}, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "yaml output with one route", | ||
| outputFormat: print.YAMLOutputFormat, | ||
| routes: []iaas.Route{dummyRoute}, | ||
| 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.outputFormat, tt.routes, "dummy-org", "dummy-route-table-id"); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package list | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/errors" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/examples" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/flags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/client" | ||
| routeUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/network-area/routing-table/utils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/tables" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/iaas" | ||
| ) | ||
| const ( | ||
| labelSelectorFlag = "label-selector" | ||
| limitFlag = "limit" | ||
| networkAreaIdFlag = "network-area-id" | ||
| organizationIdFlag = "organization-id" | ||
| routingTableIdFlag = "routing-table-id" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| LabelSelector *string | ||
| Limit *int64 | ||
| NetworkAreaId string | ||
| OrganizationId string | ||
| RoutingTableId string | ||
| } | ||
| func NewCmd(params *types.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "list", | ||
| Short: "Lists all routes within a routing-table", | ||
| Long: "Lists all routes within a routing-table", | ||
| Args: args.NoArgs, | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `List all routes within a routing-table`, | ||
| `$ stackit network-area routing-table route list --routing-table-id xxx --organization-id yyy --network-area-id zzz`, | ||
| ), | ||
| examples.NewExample( | ||
| `List all routes within a routing-table with labels`, | ||
| `$ stackit network-area routing-table list --routing-table-id xxx --organization-id yyy --network-area-id zzz --label-selector env=dev,env=rc`, | ||
| ), | ||
| examples.NewExample( | ||
| `List all routes within a routing-tables with labels and limit to 10`, | ||
| `$ stackit network-area routing-table list --routing-table-id xxx --organization-id yyy --network-area-id zzz --label-selector env=dev,env=rc --limit 10`, | ||
| ), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, _ []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd, nil) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Call API | ||
| request := apiClient.ListRoutesOfRoutingTable( | ||
| ctx, | ||
| model.OrganizationId, | ||
| model.NetworkAreaId, | ||
| model.Region, | ||
| model.RoutingTableId, | ||
| ) | ||
| if model.LabelSelector != nil { | ||
| request.LabelSelector(*model.LabelSelector) | ||
| } | ||
| response, err := request.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("list routes: %w", err) | ||
| } | ||
| routes := utils.GetSliceFromPointer(response.Items) | ||
| // Truncate output | ||
| if model.Limit != nil && len(routes) > int(*model.Limit) { | ||
| routes = routes[:*model.Limit] | ||
| } | ||
| return outputResult(params.Printer, model.OutputFormat, routes, model.OrganizationId, model.RoutingTableId) | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().Int64(limitFlag, 0, "Maximum number of entries to list") | ||
| cmd.Flags().String(labelSelectorFlag, "", "Filter by label") | ||
| cmd.Flags().Var(flags.UUIDFlag(), networkAreaIdFlag, "Network-Area ID") | ||
| cmd.Flags().Var(flags.UUIDFlag(), organizationIdFlag, "Organization ID") | ||
| cmd.Flags().Var(flags.UUIDFlag(), routingTableIdFlag, "Routing-Table ID") | ||
| err := flags.MarkFlagsRequired(cmd, organizationIdFlag, networkAreaIdFlag, routingTableIdFlag) | ||
| cobra.CheckErr(err) | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| limit := flags.FlagToInt64Pointer(p, cmd, limitFlag) | ||
| if limit != nil && *limit < 1 { | ||
| return nil, &errors.FlagValidationError{ | ||
| Flag: limitFlag, | ||
| Details: "must be greater than 0", | ||
| } | ||
| } | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| LabelSelector: flags.FlagToStringPointer(p, cmd, labelSelectorFlag), | ||
| Limit: limit, | ||
| NetworkAreaId: flags.FlagToStringValue(p, cmd, networkAreaIdFlag), | ||
| OrganizationId: flags.FlagToStringValue(p, cmd, organizationIdFlag), | ||
| RoutingTableId: flags.FlagToStringValue(p, cmd, routingTableIdFlag), | ||
| } | ||
| p.DebugInputModel(model) | ||
| return &model, nil | ||
| } | ||
| func outputResult(p *print.Printer, outputFormat string, routes []iaas.Route, orgId, routeTableId string) error { | ||
| if routes == nil { | ||
| return fmt.Errorf("list routes routes are nil") | ||
| } | ||
| return p.OutputResult(outputFormat, routes, func() error { | ||
| if len(routes) == 0 { | ||
| p.Outputf("No routes found for routing-table %q in organization %q\n", routeTableId, orgId) | ||
| return nil | ||
| } | ||
| table := tables.NewTable() | ||
| table.SetHeader("ID", "DESTINATION TYPE", "DESTINATION VALUE", "NEXTHOP TYPE", "NEXTHOP VALUE", "LABELS", "CREATED AT", "UPDATED AT") | ||
| for _, route := range routes { | ||
| routeDetails := routeUtils.ExtractRouteDetails(route) | ||
| table.AddRow( | ||
| utils.PtrString(route.Id), | ||
| routeDetails.DestType, | ||
| routeDetails.DestValue, | ||
| routeDetails.HopType, | ||
| routeDetails.HopValue, | ||
| routeDetails.Labels, | ||
| routeDetails.CreatedAt, | ||
| routeDetails.UpdatedAt, | ||
| ) | ||
| } | ||
| err := table.Display(p) | ||
| if err != nil { | ||
| return fmt.Errorf("render table: %w", err) | ||
| } | ||
| return nil | ||
| }) | ||
| } |
| package route | ||
| import ( | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/network-area/routingtable/route/create" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/network-area/routingtable/route/delete" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/network-area/routingtable/route/describe" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/network-area/routingtable/route/list" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/network-area/routingtable/route/update" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| ) | ||
| func NewCmd(params *types.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "route", | ||
| Short: "Manages routes of a routing-table", | ||
| Long: "Manages routes of a routing-table", | ||
| Args: args.NoArgs, | ||
| Run: utils.CmdHelp, | ||
| } | ||
| addSubcommands(cmd, params) | ||
| return cmd | ||
| } | ||
| func addSubcommands(cmd *cobra.Command, params *types.CmdParams) { | ||
| cmd.AddCommand(describe.NewCmd(params)) | ||
| cmd.AddCommand(list.NewCmd(params)) | ||
| cmd.AddCommand(delete.NewCmd(params)) | ||
| cmd.AddCommand(update.NewCmd(params)) | ||
| cmd.AddCommand(create.NewCmd(params)) | ||
| } |
| package update | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "time" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/iaas" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &iaas.APIClient{} | ||
| const testRegion = "eu01" | ||
| var testOrgId = uuid.NewString() | ||
| var testNetworkAreaId = uuid.NewString() | ||
| var testRoutingTableId = uuid.NewString() | ||
| var testRouteId = uuid.NewString() | ||
| const testLabelSelectorFlag = "key1=value1,key2=value2" | ||
| var testLabels = &map[string]string{ | ||
| "key1": "value1", | ||
| "key2": "value2", | ||
| } | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.RegionFlag: testRegion, | ||
| organizationIdFlag: testOrgId, | ||
| networkAreaIdFlag: testNetworkAreaId, | ||
| routingTableIdFlag: testRoutingTableId, | ||
| labelFlag: testLabelSelectorFlag, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| Region: testRegion, | ||
| }, | ||
| OrganizationId: testOrgId, | ||
| NetworkAreaId: testNetworkAreaId, | ||
| RoutingTableId: testRoutingTableId, | ||
| RouteId: testRouteId, | ||
| Labels: testLabels, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureArgValues(mods ...func(argValues []string)) []string { | ||
| argValues := []string{ | ||
| testRouteId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(argValues) | ||
| } | ||
| return argValues | ||
| } | ||
| func fixtureRequest(mods ...func(req *iaas.ApiUpdateRouteOfRoutingTableRequest)) iaas.ApiUpdateRouteOfRoutingTableRequest { | ||
| req := testClient.UpdateRouteOfRoutingTable( | ||
| testCtx, | ||
| testOrgId, | ||
| testNetworkAreaId, | ||
| testRegion, | ||
| testRoutingTableId, | ||
| testRouteId, | ||
| ) | ||
| payload := iaas.UpdateRouteOfRoutingTablePayload{ | ||
| Labels: utils.ConvertStringMapToInterfaceMap(testLabels), | ||
| } | ||
| req = req.UpdateRouteOfRoutingTablePayload(payload) | ||
| for _, mod := range mods { | ||
| mod(&req) | ||
| } | ||
| return req | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| flagValues map[string]string | ||
| argValues []string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| flagValues: fixtureFlagValues(), | ||
| argValues: fixtureArgValues(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "no values", | ||
| argValues: []string{}, | ||
| flagValues: map[string]string{}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "routing-table-id missing", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, routingTableIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "network-area-id missing", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, networkAreaIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "org-id missing", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, organizationIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "routing-table-id missing", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, routingTableIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "arg value missing", | ||
| argValues: []string{""}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: false, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "arg value wrong", | ||
| argValues: []string{"foo-bar"}, | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: false, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "labels are missing", | ||
| argValues: []string{}, | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, labelFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "invalid label format", | ||
| argValues: []string{}, | ||
| flagValues: map[string]string{labelFlag: "invalid-label"}, | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| testutils.TestParseInput(t, NewCmd, parseInput, tt.expectedModel, tt.argValues, tt.flagValues, tt.isValid) | ||
| }) | ||
| } | ||
| } | ||
| func TestBuildRequest(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| model *inputModel | ||
| expectedRequest iaas.ApiUpdateRouteOfRoutingTableRequest | ||
| }{ | ||
| { | ||
| description: "base", | ||
| model: fixtureInputModel(), | ||
| expectedRequest: fixtureRequest(), | ||
| }, | ||
| { | ||
| description: "labels nil", | ||
| model: fixtureInputModel(func(m *inputModel) { | ||
| m.Labels = nil | ||
| }), | ||
| expectedRequest: fixtureRequest(func(request *iaas.ApiUpdateRouteOfRoutingTableRequest) { | ||
| *request = (*request).UpdateRouteOfRoutingTablePayload(iaas.UpdateRouteOfRoutingTablePayload{ | ||
| Labels: nil, | ||
| }) | ||
| }), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| gotReq := buildRequest(testCtx, tt.model, testClient) | ||
| if diff := cmp.Diff( | ||
| tt.expectedRequest, | ||
| gotReq, | ||
| cmp.AllowUnexported(tt.expectedRequest), | ||
| cmpopts.EquateComparable(testCtx), | ||
| ); diff != "" { | ||
| t.Errorf("buildRequest() mismatch (-want +got):\n%s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| func TestOutputResult(t *testing.T) { | ||
| dummyRoute := iaas.Route{ | ||
| Id: utils.Ptr("route-foo"), | ||
| Destination: &iaas.RouteDestination{ | ||
| DestinationCIDRv4: &iaas.DestinationCIDRv4{ | ||
| Type: utils.Ptr("cidrv4"), | ||
| Value: utils.Ptr("10.0.0.0/24"), | ||
| }, | ||
| }, | ||
| Nexthop: &iaas.RouteNexthop{ | ||
| NexthopIPv4: &iaas.NexthopIPv4{ | ||
| Type: utils.Ptr("ipv4"), | ||
| Value: utils.Ptr("10.0.0.1"), | ||
| }, | ||
| }, | ||
| Labels: utils.ConvertStringMapToInterfaceMap(testLabels), | ||
| CreatedAt: utils.Ptr(time.Now()), | ||
| UpdatedAt: utils.Ptr(time.Now()), | ||
| } | ||
| tests := []struct { | ||
| name string | ||
| outputFormat string | ||
| route *iaas.Route | ||
| wantErr bool | ||
| }{ | ||
| { | ||
| name: "nil route should return error", | ||
| outputFormat: print.PrettyOutputFormat, | ||
| route: nil, | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| name: "empty route", | ||
| outputFormat: print.PrettyOutputFormat, | ||
| route: &iaas.Route{}, | ||
| // should fail on pretty format | ||
| wantErr: true, | ||
| }, | ||
| { | ||
| name: "pretty output with one route", | ||
| outputFormat: print.PrettyOutputFormat, | ||
| route: &dummyRoute, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "json output with one route", | ||
| outputFormat: print.JSONOutputFormat, | ||
| route: &dummyRoute, | ||
| wantErr: false, | ||
| }, | ||
| { | ||
| name: "yaml output with one route", | ||
| outputFormat: print.YAMLOutputFormat, | ||
| route: &dummyRoute, | ||
| 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.outputFormat, "", "", tt.route); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package update | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| cliErr "github.com/stackitcloud/stackit-cli/internal/pkg/errors" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/examples" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/flags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/client" | ||
| iaasUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/utils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/iaas" | ||
| ) | ||
| const ( | ||
| labelFlag = "labels" | ||
| networkAreaIdFlag = "network-area-id" | ||
| organizationIdFlag = "organization-id" | ||
| routeIdArg = "ROUTE_ID" | ||
| routingTableIdFlag = "routing-table-id" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| Labels *map[string]string | ||
| NetworkAreaId string | ||
| OrganizationId string | ||
| RouteId string | ||
| RoutingTableId string | ||
| } | ||
| func NewCmd(params *types.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: fmt.Sprintf("update %s", routeIdArg), | ||
| Short: "Updates a route in a routing-table", | ||
| Long: "Updates a route in a routing-table.", | ||
| Args: args.SingleArg(routeIdArg, utils.ValidateUUID), | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Updates the label(s) of a route with ID "xxx" in a routing-table ID "xxx" in organization with ID "yyy" and network-area with ID "zzz"`, | ||
| "$ stackit network-area routing-table route update xxx --labels key=value,foo=bar --routing-table-id xxx --organization-id yyy --network-area-id zzz", | ||
| ), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| routingTableLabel, err := iaasUtils.GetRoutingTableOfAreaName(ctx, apiClient, model.OrganizationId, model.NetworkAreaId, model.Region, model.RoutingTableId) | ||
| if err != nil { | ||
| params.Printer.Debug(print.ErrorLevel, "get routing-table name: %v", err) | ||
| routingTableLabel = model.RoutingTableId | ||
| } else if routingTableLabel == "" { | ||
| routingTableLabel = model.RoutingTableId | ||
| } | ||
| prompt := fmt.Sprintf("Are you sure you want to update route %q for routing-table %q?", model.RouteId, routingTableLabel) | ||
| err = params.Printer.PromptForConfirmation(prompt) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| req := buildRequest(ctx, model, apiClient) | ||
| resp, err := req.Execute() | ||
| if err != nil { | ||
| return fmt.Errorf("update route %q of routing-table %q : %w", model.RouteId, model.RoutingTableId, err) | ||
| } | ||
| return outputResult(params.Printer, model.OutputFormat, model.RoutingTableId, model.NetworkAreaId, resp) | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().StringToString(labelFlag, nil, "Labels are key-value string pairs which can be attached to a route. A label can be provided with the format key=value and the flag can be used multiple times to provide a list of labels") | ||
| cmd.Flags().Var(flags.UUIDFlag(), networkAreaIdFlag, "Network-Area ID") | ||
| cmd.Flags().Var(flags.UUIDFlag(), organizationIdFlag, "Organization ID") | ||
| cmd.Flags().Var(flags.UUIDFlag(), routingTableIdFlag, "Routing-Table ID") | ||
| err := flags.MarkFlagsRequired(cmd, labelFlag, organizationIdFlag, networkAreaIdFlag, routingTableIdFlag) | ||
| cobra.CheckErr(err) | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| routeId := inputArgs[0] | ||
| labels := flags.FlagToStringToStringPointer(p, cmd, labelFlag) | ||
| if labels == nil { | ||
| return nil, &cliErr.EmptyUpdateError{} | ||
| } | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| Labels: labels, | ||
| NetworkAreaId: flags.FlagToStringValue(p, cmd, networkAreaIdFlag), | ||
| OrganizationId: flags.FlagToStringValue(p, cmd, organizationIdFlag), | ||
| RouteId: routeId, | ||
| RoutingTableId: flags.FlagToStringValue(p, cmd, routingTableIdFlag), | ||
| } | ||
| p.DebugInputModel(model) | ||
| return &model, nil | ||
| } | ||
| func outputResult(p *print.Printer, outputFormat, routingTableId, networkAreaId string, route *iaas.Route) error { | ||
| return p.OutputResult(outputFormat, route, func() error { | ||
| if route == nil { | ||
| return fmt.Errorf("update route response is empty") | ||
| } | ||
| if route.Id == nil || *route.Id == "" { | ||
| return fmt.Errorf("update route response has empty id") | ||
| } | ||
| p.Outputf("Updated route %q for routing-table %q in network-area %q.\n", *route.Id, routingTableId, networkAreaId) | ||
| return nil | ||
| }) | ||
| } | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiUpdateRouteOfRoutingTableRequest { | ||
| req := apiClient.UpdateRouteOfRoutingTable( | ||
| ctx, | ||
| model.OrganizationId, | ||
| model.NetworkAreaId, | ||
| model.Region, | ||
| model.RoutingTableId, | ||
| model.RouteId, | ||
| ) | ||
| payload := iaas.UpdateRouteOfRoutingTablePayload{ | ||
| Labels: utils.ConvertStringMapToInterfaceMap(model.Labels), | ||
| } | ||
| return req.UpdateRouteOfRoutingTablePayload(payload) | ||
| } |
| package attach | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/iaas" | ||
| ) | ||
| const ( | ||
| testRegion = "eu01" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &iaas.APIClient{} | ||
| var testProjectId = uuid.NewString() | ||
| var testServerId = uuid.NewString() | ||
| var testSecurityGroupId = uuid.NewString() | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| serverIdFlag: testServerId, | ||
| securityGroupIdFlag: testSecurityGroupId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| }, | ||
| ServerId: testServerId, | ||
| SecurityGroupId: testSecurityGroupId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureRequest(mods ...func(request *iaas.ApiAddSecurityGroupToServerRequest)) iaas.ApiAddSecurityGroupToServerRequest { | ||
| request := testClient.AddSecurityGroupToServer(testCtx, testProjectId, testRegion, testServerId, testSecurityGroupId) | ||
| for _, mod := range mods { | ||
| mod(&request) | ||
| } | ||
| return request | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| flagValues map[string]string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "no values", | ||
| flagValues: map[string]string{}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 1", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 2", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "security group id missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, securityGroupIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "security group id invalid 1", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[securityGroupIdFlag] = "" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "security group id invalid 2", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[securityGroupIdFlag] = "invalid-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "server id flag missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, serverIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "server id invalid 1", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[serverIdFlag] = "" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "server id invalid 2", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[serverIdFlag] = "invalid-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| testutils.TestParseInput(t, NewCmd, parseInput, tt.expectedModel, []string{}, tt.flagValues, tt.isValid) | ||
| }) | ||
| } | ||
| } | ||
| func TestBuildRequest(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| model *inputModel | ||
| expectedRequest iaas.ApiAddSecurityGroupToServerRequest | ||
| }{ | ||
| { | ||
| description: "base", | ||
| model: fixtureInputModel(), | ||
| expectedRequest: fixtureRequest(), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| request := buildRequest(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) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package attach | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| cliErr "github.com/stackitcloud/stackit-cli/internal/pkg/errors" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/examples" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/flags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/client" | ||
| iaasUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/iaas" | ||
| ) | ||
| const ( | ||
| serverIdFlag = "server-id" | ||
| securityGroupIdFlag = "security-group-id" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| ServerId string | ||
| SecurityGroupId string | ||
| } | ||
| func NewCmd(params *types.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "attach", | ||
| Short: "Attaches a security group to a server", | ||
| Long: "Attaches a security group to a server.", | ||
| Args: args.NoArgs, | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Attach a security group with ID "xxx" to a server with ID "yyy"`, | ||
| `$ stackit server security-group attach --server-id yyy --security-group-id xxx`, | ||
| ), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| serverLabel, err := iaasUtils.GetServerName(ctx, apiClient, model.ProjectId, model.Region, model.ServerId) | ||
| if err != nil { | ||
| params.Printer.Debug(print.ErrorLevel, "get server name: %v", err) | ||
| serverLabel = model.ServerId | ||
| } else if serverLabel == "" { | ||
| serverLabel = model.ServerId | ||
| } | ||
| securityGroupLabel, err := iaasUtils.GetSecurityGroupName(ctx, apiClient, model.ProjectId, model.Region, model.SecurityGroupId) | ||
| if err != nil { | ||
| params.Printer.Debug(print.ErrorLevel, "get security group name: %v", err) | ||
| securityGroupLabel = model.SecurityGroupId | ||
| } | ||
| prompt := fmt.Sprintf("Are you sure you want to attach security group %q to server %q?", securityGroupLabel, serverLabel) | ||
| err = params.Printer.PromptForConfirmation(prompt) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Call API | ||
| req := buildRequest(ctx, model, apiClient) | ||
| if err := req.Execute(); err != nil { | ||
| return fmt.Errorf("attach security group to server: %w", err) | ||
| } | ||
| params.Printer.Outputf("Attached security group %q to server %q\n", securityGroupLabel, serverLabel) | ||
| return nil | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().Var(flags.UUIDFlag(), serverIdFlag, "Server ID") | ||
| cmd.Flags().Var(flags.UUIDFlag(), securityGroupIdFlag, "Security Group ID") | ||
| err := flags.MarkFlagsRequired(cmd, serverIdFlag, securityGroupIdFlag) | ||
| cobra.CheckErr(err) | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| if globalFlags.ProjectId == "" { | ||
| return nil, &cliErr.ProjectIdError{} | ||
| } | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| ServerId: flags.FlagToStringValue(p, cmd, serverIdFlag), | ||
| SecurityGroupId: flags.FlagToStringValue(p, cmd, securityGroupIdFlag), | ||
| } | ||
| p.DebugInputModel(model) | ||
| return &model, nil | ||
| } | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiAddSecurityGroupToServerRequest { | ||
| req := apiClient.AddSecurityGroupToServer(ctx, model.ProjectId, model.Region, model.ServerId, model.SecurityGroupId) | ||
| return req | ||
| } |
| package detach | ||
| import ( | ||
| "context" | ||
| "testing" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/iaas" | ||
| ) | ||
| const ( | ||
| testRegion = "eu01" | ||
| ) | ||
| type testCtxKey struct{} | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &iaas.APIClient{} | ||
| var testProjectId = uuid.NewString() | ||
| var testServerId = uuid.NewString() | ||
| var testSecurityGroupId = uuid.NewString() | ||
| func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string { | ||
| flagValues := map[string]string{ | ||
| globalflags.ProjectIdFlag: testProjectId, | ||
| globalflags.RegionFlag: testRegion, | ||
| serverIdFlag: testServerId, | ||
| securityGroupIdFlag: testSecurityGroupId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(flagValues) | ||
| } | ||
| return flagValues | ||
| } | ||
| func fixtureInputModel(mods ...func(model *inputModel)) *inputModel { | ||
| model := &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| Verbosity: globalflags.VerbosityDefault, | ||
| ProjectId: testProjectId, | ||
| Region: testRegion, | ||
| }, | ||
| ServerId: testServerId, | ||
| SecurityGroupId: testSecurityGroupId, | ||
| } | ||
| for _, mod := range mods { | ||
| mod(model) | ||
| } | ||
| return model | ||
| } | ||
| func fixtureRequest(mods ...func(request *iaas.ApiRemoveSecurityGroupFromServerRequest)) iaas.ApiRemoveSecurityGroupFromServerRequest { | ||
| request := testClient.RemoveSecurityGroupFromServer(testCtx, testProjectId, testRegion, testServerId, testSecurityGroupId) | ||
| for _, mod := range mods { | ||
| mod(&request) | ||
| } | ||
| return request | ||
| } | ||
| func TestParseInput(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| flagValues map[string]string | ||
| isValid bool | ||
| expectedModel *inputModel | ||
| }{ | ||
| { | ||
| description: "base", | ||
| flagValues: fixtureFlagValues(), | ||
| isValid: true, | ||
| expectedModel: fixtureInputModel(), | ||
| }, | ||
| { | ||
| description: "no values", | ||
| flagValues: map[string]string{}, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, globalflags.ProjectIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 1", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "project id invalid 2", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[globalflags.ProjectIdFlag] = "invalid-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "security group id missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, securityGroupIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "security group id invalid 1", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[securityGroupIdFlag] = "" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "security group id invalid 2", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[securityGroupIdFlag] = "invalid-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "server id flag missing", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| delete(flagValues, serverIdFlag) | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "server id invalid 1", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[serverIdFlag] = "" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "server id invalid 2", | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[serverIdFlag] = "invalid-uuid" | ||
| }), | ||
| isValid: false, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| testutils.TestParseInput(t, NewCmd, parseInput, tt.expectedModel, []string{}, tt.flagValues, tt.isValid) | ||
| }) | ||
| } | ||
| } | ||
| func TestBuildRequest(t *testing.T) { | ||
| tests := []struct { | ||
| description string | ||
| model *inputModel | ||
| expectedRequest iaas.ApiRemoveSecurityGroupFromServerRequest | ||
| }{ | ||
| { | ||
| description: "base", | ||
| model: fixtureInputModel(), | ||
| expectedRequest: fixtureRequest(), | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| request := buildRequest(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) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package detach | ||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| cliErr "github.com/stackitcloud/stackit-cli/internal/pkg/errors" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/examples" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/flags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/client" | ||
| iaasUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/iaas/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/iaas" | ||
| ) | ||
| const ( | ||
| serverIdFlag = "server-id" | ||
| securityGroupIdFlag = "security-group-id" | ||
| ) | ||
| type inputModel struct { | ||
| *globalflags.GlobalFlagModel | ||
| ServerId string | ||
| SecurityGroupId string | ||
| } | ||
| func NewCmd(params *types.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "detach", | ||
| Short: "Detaches a security group from a server", | ||
| Long: "Detaches a security group from a server.", | ||
| Args: args.NoArgs, | ||
| Example: examples.Build( | ||
| examples.NewExample( | ||
| `Detach a security group with ID "xxx" from a server with ID "yyy"`, | ||
| `$ stackit server security-group detach --server-id yyy --security-group-id xxx`, | ||
| ), | ||
| ), | ||
| RunE: func(cmd *cobra.Command, args []string) error { | ||
| ctx := context.Background() | ||
| model, err := parseInput(params.Printer, cmd, args) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Configure API client | ||
| apiClient, err := client.ConfigureClient(params.Printer, params.CliVersion) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| serverLabel, err := iaasUtils.GetServerName(ctx, apiClient, model.ProjectId, model.Region, model.ServerId) | ||
| if err != nil { | ||
| params.Printer.Debug(print.ErrorLevel, "get server name: %v", err) | ||
| serverLabel = model.ServerId | ||
| } else if serverLabel == "" { | ||
| serverLabel = model.ServerId | ||
| } | ||
| securityGroupLabel, err := iaasUtils.GetSecurityGroupName(ctx, apiClient, model.ProjectId, model.Region, model.SecurityGroupId) | ||
| if err != nil { | ||
| params.Printer.Debug(print.ErrorLevel, "get security group name: %v", err) | ||
| securityGroupLabel = model.SecurityGroupId | ||
| } | ||
| prompt := fmt.Sprintf("Are you sure you want to detach security group %q from server %q?", securityGroupLabel, serverLabel) | ||
| err = params.Printer.PromptForConfirmation(prompt) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| // Call API | ||
| req := buildRequest(ctx, model, apiClient) | ||
| if err := req.Execute(); err != nil { | ||
| return fmt.Errorf("detach security group from server: %w", err) | ||
| } | ||
| params.Printer.Outputf("Detached security group %q from server %q\n", securityGroupLabel, serverLabel) | ||
| return nil | ||
| }, | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().Var(flags.UUIDFlag(), serverIdFlag, "Server ID") | ||
| cmd.Flags().Var(flags.UUIDFlag(), securityGroupIdFlag, "Security Group ID") | ||
| err := flags.MarkFlagsRequired(cmd, serverIdFlag, securityGroupIdFlag) | ||
| cobra.CheckErr(err) | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel, error) { | ||
| globalFlags := globalflags.Parse(p, cmd) | ||
| if globalFlags.ProjectId == "" { | ||
| return nil, &cliErr.ProjectIdError{} | ||
| } | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| ServerId: flags.FlagToStringValue(p, cmd, serverIdFlag), | ||
| SecurityGroupId: flags.FlagToStringValue(p, cmd, securityGroupIdFlag), | ||
| } | ||
| p.DebugInputModel(model) | ||
| return &model, nil | ||
| } | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *iaas.APIClient) iaas.ApiRemoveSecurityGroupFromServerRequest { | ||
| req := apiClient.RemoveSecurityGroupFromServer(ctx, model.ProjectId, model.Region, model.ServerId, model.SecurityGroupId) | ||
| return req | ||
| } |
| package securitygroup | ||
| import ( | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/server/security-group/attach" | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/server/security-group/detach" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/spf13/cobra" | ||
| ) | ||
| func NewCmd(params *types.CmdParams) *cobra.Command { | ||
| cmd := &cobra.Command{ | ||
| Use: "security-group", | ||
| Short: "Allows attaching/detaching security groups to servers", | ||
| Long: "Allows attaching/detaching security groups to servers.", | ||
| Args: args.NoArgs, | ||
| Run: utils.CmdHelp, | ||
| } | ||
| addSubcommands(cmd, params) | ||
| return cmd | ||
| } | ||
| func addSubcommands(cmd *cobra.Command, params *types.CmdParams) { | ||
| cmd.AddCommand(attach.NewCmd(params)) | ||
| cmd.AddCommand(detach.NewCmd(params)) | ||
| } |
| package utils | ||
| import ( | ||
| "testing" | ||
| "time" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/iaas" | ||
| ) | ||
| const ipv4 = "ipv4" | ||
| const ipv6 = "ipv6" | ||
| const cidrv4 = "cidrv4" | ||
| const cidrv6 = "cidrv6" | ||
| func TestExtractRouteDetails(t *testing.T) { | ||
| created := time.Date(2024, 1, 2, 3, 4, 5, 0, time.UTC) | ||
| updated := time.Date(2024, 1, 2, 4, 5, 6, 0, time.UTC) | ||
| tests := []struct { | ||
| description string | ||
| input *iaas.Route | ||
| want RouteDetails | ||
| }{ | ||
| { | ||
| description: "completely empty route (zero value)", | ||
| input: &iaas.Route{}, | ||
| want: RouteDetails{}, | ||
| }, | ||
| { | ||
| description: "destination only, no nexthop, no labels", | ||
| input: &iaas.Route{ | ||
| Destination: &iaas.RouteDestination{ | ||
| DestinationCIDRv4: &iaas.DestinationCIDRv4{ | ||
| Type: utils.Ptr(cidrv4), | ||
| Value: utils.Ptr("10.0.0.0/24"), | ||
| }, | ||
| }, | ||
| }, | ||
| want: RouteDetails{ | ||
| DestType: cidrv4, | ||
| DestValue: "10.0.0.0/24", | ||
| }, | ||
| }, | ||
| { | ||
| description: "nexthop only, no destination, empty labels map", | ||
| input: &iaas.Route{ | ||
| Nexthop: &iaas.RouteNexthop{ | ||
| NexthopIPv4: &iaas.NexthopIPv4{ | ||
| Type: utils.Ptr(ipv4), | ||
| Value: utils.Ptr("10.0.0.1"), | ||
| }, | ||
| }, | ||
| Labels: &map[string]interface{}{}, // empty but non-nil | ||
| }, | ||
| want: RouteDetails{ | ||
| HopType: ipv4, | ||
| HopValue: "10.0.0.1", | ||
| }, | ||
| }, | ||
| { | ||
| description: "destination present, nexthop struct nil, labels nil", | ||
| input: &iaas.Route{ | ||
| Destination: &iaas.RouteDestination{ | ||
| DestinationCIDRv6: &iaas.DestinationCIDRv6{ | ||
| Type: utils.Ptr(cidrv6), | ||
| Value: utils.Ptr("2001:db8::/32"), | ||
| }, | ||
| }, | ||
| Nexthop: nil, | ||
| Labels: nil, | ||
| }, | ||
| want: RouteDetails{ | ||
| DestType: cidrv6, | ||
| DestValue: "2001:db8::/32", | ||
| }, | ||
| }, | ||
| { | ||
| description: "CIDRv4 destination, IPv4 nexthop, with labels", | ||
| input: &iaas.Route{ | ||
| Destination: &iaas.RouteDestination{ | ||
| DestinationCIDRv4: &iaas.DestinationCIDRv4{ | ||
| Type: utils.Ptr(cidrv4), | ||
| Value: utils.Ptr("10.0.0.0/24"), | ||
| }, | ||
| }, | ||
| Nexthop: &iaas.RouteNexthop{ | ||
| NexthopIPv4: &iaas.NexthopIPv4{ | ||
| Type: utils.Ptr(ipv4), | ||
| Value: utils.Ptr("10.0.0.1"), | ||
| }, | ||
| }, | ||
| Labels: &map[string]interface{}{ | ||
| "key": "value", | ||
| }, | ||
| }, | ||
| want: RouteDetails{ | ||
| DestType: cidrv4, | ||
| DestValue: "10.0.0.0/24", | ||
| HopType: ipv4, | ||
| HopValue: "10.0.0.1", | ||
| Labels: "key: value", | ||
| }, | ||
| }, | ||
| { | ||
| description: "CIDRv6 destination, IPv6 nexthop, with no labels", | ||
| input: &iaas.Route{ | ||
| Destination: &iaas.RouteDestination{ | ||
| DestinationCIDRv6: &iaas.DestinationCIDRv6{ | ||
| Type: utils.Ptr(cidrv6), | ||
| Value: utils.Ptr("2001:db8::/32"), | ||
| }, | ||
| }, | ||
| Nexthop: &iaas.RouteNexthop{ | ||
| NexthopIPv6: &iaas.NexthopIPv6{ | ||
| Type: utils.Ptr(ipv6), | ||
| Value: utils.Ptr("2001:db8::1"), | ||
| }, | ||
| }, | ||
| Labels: nil, | ||
| }, | ||
| want: RouteDetails{ | ||
| DestType: cidrv6, | ||
| DestValue: "2001:db8::/32", | ||
| HopType: ipv6, | ||
| HopValue: "2001:db8::1", | ||
| }, | ||
| }, | ||
| { | ||
| description: "Internet nexthop without value", | ||
| input: &iaas.Route{ | ||
| Destination: &iaas.RouteDestination{ | ||
| DestinationCIDRv4: &iaas.DestinationCIDRv4{ | ||
| Type: utils.Ptr(cidrv4), | ||
| Value: utils.Ptr("0.0.0.0/0"), | ||
| }, | ||
| }, | ||
| Nexthop: &iaas.RouteNexthop{ | ||
| NexthopInternet: &iaas.NexthopInternet{ | ||
| Type: utils.Ptr("Internet"), | ||
| }, | ||
| }, | ||
| Labels: nil, | ||
| }, | ||
| want: RouteDetails{ | ||
| DestType: cidrv4, | ||
| DestValue: "0.0.0.0/0", | ||
| HopType: "Internet", | ||
| // HopValue empty | ||
| }, | ||
| }, | ||
| { | ||
| description: "Blackhole nexthop without value and nil labels map", | ||
| input: &iaas.Route{ | ||
| Destination: &iaas.RouteDestination{ | ||
| DestinationCIDRv6: &iaas.DestinationCIDRv6{ | ||
| Type: utils.Ptr(cidrv6), | ||
| Value: utils.Ptr("::/0"), | ||
| }, | ||
| }, | ||
| Nexthop: &iaas.RouteNexthop{ | ||
| NexthopBlackhole: &iaas.NexthopBlackhole{ | ||
| Type: utils.Ptr("Blackhole"), | ||
| }, | ||
| }, | ||
| Labels: nil, | ||
| }, | ||
| want: RouteDetails{ | ||
| DestType: cidrv6, | ||
| DestValue: "::/0", | ||
| HopType: "Blackhole", | ||
| }, | ||
| }, | ||
| { | ||
| description: "route with created and updated timestamps", | ||
| input: &iaas.Route{ | ||
| Destination: &iaas.RouteDestination{ | ||
| DestinationCIDRv4: &iaas.DestinationCIDRv4{ | ||
| Type: utils.Ptr(cidrv4), | ||
| Value: utils.Ptr("10.0.0.0/24"), | ||
| }, | ||
| }, | ||
| Nexthop: &iaas.RouteNexthop{ | ||
| NexthopIPv4: &iaas.NexthopIPv4{ | ||
| Type: utils.Ptr(ipv4), | ||
| Value: utils.Ptr("10.0.0.1"), | ||
| }, | ||
| }, | ||
| CreatedAt: &created, | ||
| UpdatedAt: &updated, | ||
| }, | ||
| want: RouteDetails{ | ||
| DestType: cidrv4, | ||
| DestValue: "10.0.0.0/24", | ||
| HopType: ipv4, | ||
| HopValue: "10.0.0.1", | ||
| CreatedAt: created.Format(time.RFC3339), | ||
| UpdatedAt: updated.Format(time.RFC3339), | ||
| Labels: "", | ||
| }, | ||
| }, | ||
| { | ||
| description: "route with created timestamp only", | ||
| input: &iaas.Route{ | ||
| Destination: &iaas.RouteDestination{ | ||
| DestinationCIDRv4: &iaas.DestinationCIDRv4{ | ||
| Type: utils.Ptr(cidrv4), | ||
| Value: utils.Ptr("10.0.0.0/24"), | ||
| }, | ||
| }, | ||
| Nexthop: &iaas.RouteNexthop{ | ||
| NexthopIPv4: &iaas.NexthopIPv4{ | ||
| Type: utils.Ptr(ipv4), | ||
| Value: utils.Ptr("10.0.0.1"), | ||
| }, | ||
| }, | ||
| CreatedAt: &created, | ||
| }, | ||
| want: RouteDetails{ | ||
| DestType: cidrv4, | ||
| DestValue: "10.0.0.0/24", | ||
| HopType: ipv4, | ||
| HopValue: "10.0.0.1", | ||
| CreatedAt: created.Format(time.RFC3339), | ||
| UpdatedAt: "", | ||
| Labels: "", | ||
| }, | ||
| }, | ||
| { | ||
| description: "route with updated timestamp only", | ||
| input: &iaas.Route{ | ||
| Destination: &iaas.RouteDestination{ | ||
| DestinationCIDRv4: &iaas.DestinationCIDRv4{ | ||
| Type: utils.Ptr(cidrv4), | ||
| Value: utils.Ptr("10.0.0.0/24"), | ||
| }, | ||
| }, | ||
| Nexthop: &iaas.RouteNexthop{ | ||
| NexthopIPv4: &iaas.NexthopIPv4{ | ||
| Type: utils.Ptr(ipv4), | ||
| Value: utils.Ptr("10.0.0.1"), | ||
| }, | ||
| }, | ||
| UpdatedAt: &updated, | ||
| }, | ||
| want: RouteDetails{ | ||
| DestType: cidrv4, | ||
| DestValue: "10.0.0.0/24", | ||
| HopType: ipv4, | ||
| HopValue: "10.0.0.1", | ||
| CreatedAt: "", | ||
| UpdatedAt: updated.Format(time.RFC3339), | ||
| Labels: "", | ||
| }, | ||
| }, | ||
| } | ||
| for _, tt := range tests { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| got := ExtractRouteDetails(*tt.input) | ||
| if diff := cmp.Diff(tt.want, got); diff != "" { | ||
| t.Fatalf("ExtractRouteDetails() mismatch (-want +got):\n%s", diff) | ||
| } | ||
| }) | ||
| } | ||
| } |
| package utils | ||
| import ( | ||
| "fmt" | ||
| "time" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/iaas" | ||
| ) | ||
| type RouteDetails struct { | ||
| DestType string | ||
| DestValue string | ||
| HopType string | ||
| HopValue string | ||
| CreatedAt string | ||
| UpdatedAt string | ||
| Labels string | ||
| } | ||
| func ExtractRouteDetails(route iaas.Route) RouteDetails { | ||
| var routeDetails RouteDetails | ||
| if route.Destination != nil { | ||
| if route.Destination.DestinationCIDRv4 != nil { | ||
| routeDetails.DestType = utils.PtrString(route.Destination.DestinationCIDRv4.Type) | ||
| routeDetails.DestValue = utils.PtrString(route.Destination.DestinationCIDRv4.Value) | ||
| } else if route.Destination.DestinationCIDRv6 != nil { | ||
| routeDetails.DestType = utils.PtrString(route.Destination.DestinationCIDRv6.Type) | ||
| routeDetails.DestValue = utils.PtrString(route.Destination.DestinationCIDRv6.Value) | ||
| } | ||
| } | ||
| if route.Nexthop != nil { | ||
| if route.Nexthop.NexthopIPv4 != nil { | ||
| routeDetails.HopType = utils.PtrString(route.Nexthop.NexthopIPv4.Type) | ||
| routeDetails.HopValue = utils.PtrString(route.Nexthop.NexthopIPv4.Value) | ||
| } else if route.Nexthop.NexthopIPv6 != nil { | ||
| routeDetails.HopType = utils.PtrString(route.Nexthop.NexthopIPv6.Type) | ||
| routeDetails.HopValue = utils.PtrString(route.Nexthop.NexthopIPv6.Value) | ||
| } else if route.Nexthop.NexthopInternet != nil { | ||
| routeDetails.HopType = utils.PtrString(route.Nexthop.NexthopInternet.Type) | ||
| } else if route.Nexthop.NexthopBlackhole != nil { | ||
| routeDetails.HopType = utils.PtrString(route.Nexthop.NexthopBlackhole.Type) | ||
| } | ||
| } | ||
| if route.Labels != nil && len(*route.Labels) > 0 { | ||
| stringMap := make(map[string]string) | ||
| for key, value := range *route.Labels { | ||
| stringMap[key] = fmt.Sprintf("%v", value) | ||
| } | ||
| routeDetails.Labels = utils.JoinStringMap(stringMap, ": ", "\n") | ||
| } | ||
| if route.CreatedAt != nil { | ||
| routeDetails.CreatedAt = route.CreatedAt.Format(time.RFC3339) | ||
| } | ||
| if route.UpdatedAt != nil { | ||
| routeDetails.UpdatedAt = route.UpdatedAt.Format(time.RFC3339) | ||
| } | ||
| return routeDetails | ||
| } |
@@ -68,3 +68,3 @@ name: CI | ||
| - name: Check new code coverage | ||
| uses: fgrosse/go-coverage-report@v1.2.0 | ||
| uses: fgrosse/go-coverage-report@v1.3.0 | ||
| continue-on-error: true # Add this line to prevent pipeline failures in forks | ||
@@ -71,0 +71,0 @@ with: |
@@ -40,3 +40,4 @@ ## stackit network-area routing-table | ||
| * [stackit network-area routing-table list](./stackit_network-area_routing-table_list.md) - Lists all routing-tables | ||
| * [stackit network-area routing-table route](./stackit_network-area_routing-table_route.md) - Manages routes of a routing-table | ||
| * [stackit network-area routing-table update](./stackit_network-area_routing-table_update.md) - Updates a routing-table | ||
@@ -18,2 +18,5 @@ ## stackit object-storage bucket create | ||
| $ stackit object-storage bucket create my-bucket | ||
| Create an Object Storage bucket with enabled object-lock | ||
| $ stackit object-storage bucket create my-bucket --object-lock-enabled | ||
| ``` | ||
@@ -24,3 +27,4 @@ | ||
| ``` | ||
| -h, --help Help for "stackit object-storage bucket create" | ||
| -h, --help Help for "stackit object-storage bucket create" | ||
| --object-lock-enabled is the object-lock enabled for the bucket | ||
| ``` | ||
@@ -27,0 +31,0 @@ |
@@ -49,2 +49,3 @@ ## stackit server | ||
| * [stackit server resize](./stackit_server_resize.md) - Resizes the server to the given machine type | ||
| * [stackit server security-group](./stackit_server_security-group.md) - Allows attaching/detaching security groups to servers | ||
| * [stackit server service-account](./stackit_server_service-account.md) - Allows attaching/detaching service accounts to servers | ||
@@ -51,0 +52,0 @@ * [stackit server start](./stackit_server_start.md) - Starts an existing server or allocates the server if deallocated |
+2
-2
@@ -18,3 +18,3 @@ module github.com/stackitcloud/stackit-cli | ||
| github.com/spf13/viper v1.21.0 | ||
| github.com/stackitcloud/stackit-sdk-go/core v0.22.0 | ||
| github.com/stackitcloud/stackit-sdk-go/core v0.23.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/alb v0.10.0 | ||
@@ -273,3 +273,3 @@ github.com/stackitcloud/stackit-sdk-go/services/authorization v0.12.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/mariadb v0.25.6 | ||
| github.com/stackitcloud/stackit-sdk-go/services/objectstorage v1.4.5 | ||
| github.com/stackitcloud/stackit-sdk-go/services/objectstorage v1.7.0 | ||
| github.com/stackitcloud/stackit-sdk-go/services/observability v0.17.0 | ||
@@ -276,0 +276,0 @@ github.com/stackitcloud/stackit-sdk-go/services/rabbitmq v0.26.0 |
@@ -255,2 +255,3 @@ package create | ||
| outputFormat string | ||
| async bool | ||
| projectLabel string | ||
@@ -291,3 +292,3 @@ resp *sfs.CreateResourcePoolResponse | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.projectLabel, tt.args.resp); (err != nil) != tt.wantErr { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.async, tt.args.projectLabel, tt.args.resp); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
@@ -294,0 +295,0 @@ } |
@@ -112,3 +112,3 @@ package create | ||
| return outputResult(params.Printer, model.OutputFormat, projectLabel, resp) | ||
| return outputResult(params.Printer, model.OutputFormat, model.Async, projectLabel, resp) | ||
| }, | ||
@@ -174,3 +174,3 @@ } | ||
| func outputResult(p *print.Printer, outputFormat, projectLabel string, resp *sfs.CreateResourcePoolResponse) error { | ||
| func outputResult(p *print.Printer, outputFormat string, async bool, projectLabel string, resp *sfs.CreateResourcePoolResponse) error { | ||
| return p.OutputResult(outputFormat, resp, func() error { | ||
@@ -181,5 +181,9 @@ if resp == nil || resp.ResourcePool == nil { | ||
| } | ||
| p.Outputf("Created resource pool for project %q. Resource pool ID: %s\n", projectLabel, utils.PtrString(resp.ResourcePool.Id)) | ||
| operationState := "Created" | ||
| if async { | ||
| operationState = "Triggered creation of" | ||
| } | ||
| p.Outputf("%s resource pool for project %q. Resource pool ID: %s\n", operationState, projectLabel, utils.PtrString(resp.ResourcePool.Id)) | ||
| return nil | ||
| }) | ||
| } |
@@ -178,2 +178,3 @@ package delete | ||
| outputFormat string | ||
| async bool | ||
| resourcePoolName string | ||
@@ -205,3 +206,3 @@ response map[string]interface{} | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.resourcePoolName, tt.args.response); (err != nil) != tt.wantErr { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.async, tt.args.resourcePoolName, tt.args.response); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
@@ -208,0 +209,0 @@ } |
@@ -85,3 +85,3 @@ package delete | ||
| return outputResult(params.Printer, model.OutputFormat, resourcePoolName, resp) | ||
| return outputResult(params.Printer, model.OutputFormat, model.Async, resourcePoolName, resp) | ||
| }, | ||
@@ -114,7 +114,11 @@ } | ||
| func outputResult(p *print.Printer, outputFormat, resourcePoolName string, response map[string]interface{}) error { | ||
| func outputResult(p *print.Printer, outputFormat string, async bool, resourcePoolName string, response map[string]interface{}) error { | ||
| return p.OutputResult(outputFormat, response, func() error { | ||
| p.Outputf("Deleted resource pool %q\n", resourcePoolName) | ||
| operationState := "Deleted" | ||
| if async { | ||
| operationState = "Triggered deletion of" | ||
| } | ||
| p.Outputf("%s resource pool %q\n", operationState, resourcePoolName) | ||
| return nil | ||
| }) | ||
| } |
@@ -311,2 +311,3 @@ package update | ||
| outputFormat string | ||
| async bool | ||
| resp *sfs.UpdateResourcePoolResponse | ||
@@ -357,3 +358,3 @@ } | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.resp); (err != nil) != tt.wantErr { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.async, tt.args.resp); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
@@ -360,0 +361,0 @@ } |
@@ -113,3 +113,3 @@ package update | ||
| return outputResult(params.Printer, model.OutputFormat, resp) | ||
| return outputResult(params.Printer, model.OutputFormat, model.Async, resp) | ||
| }, | ||
@@ -169,3 +169,3 @@ } | ||
| func outputResult(p *print.Printer, outputFormat string, resp *sfs.UpdateResourcePoolResponse) error { | ||
| func outputResult(p *print.Printer, outputFormat string, async bool, resp *sfs.UpdateResourcePoolResponse) error { | ||
| return p.OutputResult(outputFormat, resp, func() error { | ||
@@ -176,5 +176,9 @@ if resp == nil || resp.ResourcePool == nil { | ||
| } | ||
| p.Outputf("Updated resource pool %s\n", utils.PtrString(resp.ResourcePool.Name)) | ||
| operationState := "Updated" | ||
| if async { | ||
| operationState = "Triggered update of" | ||
| } | ||
| p.Outputf("%s resource pool %s\n", operationState, utils.PtrString(resp.ResourcePool.Name)) | ||
| return nil | ||
| }) | ||
| } |
@@ -178,4 +178,6 @@ package create | ||
| type args struct { | ||
| model *inputModel | ||
| resp *git.Instance | ||
| outputFormat string | ||
| async bool | ||
| instanceName string | ||
| resp *git.Instance | ||
| } | ||
@@ -188,6 +190,8 @@ tests := []struct { | ||
| { | ||
| name: "nil", | ||
| name: "nil response", | ||
| args: args{ | ||
| model: nil, | ||
| resp: nil, | ||
| outputFormat: "", | ||
| async: false, | ||
| instanceName: "", | ||
| resp: nil, | ||
| }, | ||
@@ -199,4 +203,6 @@ wantErr: true, | ||
| args: args{ | ||
| model: &inputModel{}, | ||
| resp: &git.Instance{}, | ||
| outputFormat: "", | ||
| async: false, | ||
| instanceName: "", | ||
| resp: &git.Instance{Id: utils.Ptr(uuid.NewString())}, | ||
| }, | ||
@@ -208,8 +214,6 @@ wantErr: false, | ||
| args: args{ | ||
| model: &inputModel{ | ||
| GlobalFlagModel: &globalflags.GlobalFlagModel{ | ||
| OutputFormat: print.JSONOutputFormat, | ||
| }, | ||
| }, | ||
| resp: nil, | ||
| outputFormat: print.JSONOutputFormat, | ||
| async: true, | ||
| instanceName: testName, | ||
| resp: &git.Instance{Id: utils.Ptr(uuid.NewString())}, | ||
| }, | ||
@@ -223,3 +227,3 @@ wantErr: false, | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.model, tt.args.resp); (err != nil) != tt.wantErr { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.async, tt.args.instanceName, tt.args.resp); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
@@ -226,0 +230,0 @@ } |
@@ -18,3 +18,2 @@ package create | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/spinner" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/git" | ||
@@ -32,3 +31,2 @@ "github.com/stackitcloud/stackit-sdk-go/services/git/wait" | ||
| *globalflags.GlobalFlagModel | ||
| Id *string | ||
| Name string | ||
@@ -85,3 +83,2 @@ Flavor string | ||
| } | ||
| model.Id = result.Id | ||
@@ -92,3 +89,3 @@ // Wait for async operation, if async mode not enabled | ||
| s.Start("Creating stackit git instance") | ||
| _, err = wait.CreateGitInstanceWaitHandler(ctx, apiClient, model.ProjectId, *model.Id).WaitWithContext(ctx) | ||
| _, err = wait.CreateGitInstanceWaitHandler(ctx, apiClient, model.ProjectId, *result.Id).WaitWithContext(ctx) | ||
| if err != nil { | ||
@@ -100,3 +97,3 @@ return fmt.Errorf("wait for stackit git Instance creation: %w", err) | ||
| return outputResult(params.Printer, model, result) | ||
| return outputResult(params.Printer, model.OutputFormat, model.Async, model.Name, result) | ||
| }, | ||
@@ -151,15 +148,18 @@ } | ||
| func outputResult(p *print.Printer, model *inputModel, resp *git.Instance) error { | ||
| if model == nil { | ||
| return fmt.Errorf("input model is nil") | ||
| func outputResult(p *print.Printer, outputFormat string, async bool, instanceName string, resp *git.Instance) error { | ||
| if resp == nil { | ||
| return fmt.Errorf("API resp is nil") | ||
| } | ||
| var outputFormat string | ||
| if model.GlobalFlagModel != nil { | ||
| outputFormat = model.OutputFormat | ||
| if resp.Id == nil { | ||
| return fmt.Errorf("API resp is missing instance id") | ||
| } | ||
| return p.OutputResult(outputFormat, resp, func() error { | ||
| p.Outputf("Created instance %q with id %s\n", model.Name, utils.PtrString(model.Id)) | ||
| operationState := "Created" | ||
| if async { | ||
| operationState = "Triggered creation of" | ||
| } | ||
| p.Outputf("%s instance %q with id %s\n", operationState, instanceName, *resp.Id) | ||
| return nil | ||
| }) | ||
| } |
@@ -5,3 +5,2 @@ package create | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
@@ -11,3 +10,2 @@ | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
@@ -164,18 +162,3 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| switch model.OutputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(resp, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS key: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS key: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| default: | ||
| return p.OutputResult(model.OutputFormat, resp, func() error { | ||
| operationState := "Created" | ||
@@ -186,4 +169,4 @@ if model.Async { | ||
| p.Outputf("%s the KMS key %q. Key ID: %s\n", operationState, utils.PtrString(resp.DisplayName), utils.PtrString(resp.Id)) | ||
| } | ||
| return nil | ||
| return nil | ||
| }) | ||
| } | ||
@@ -190,0 +173,0 @@ |
@@ -5,3 +5,2 @@ package delete | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
@@ -11,3 +10,2 @@ | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
@@ -132,20 +130,6 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| switch outputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(resp, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal output to JSON: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal output to YAML: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| default: | ||
| return p.OutputResult(outputFormat, resp, func() error { | ||
| p.Outputf("Deletion of KMS key %s scheduled successfully for the deletion date: %s\n", utils.PtrString(resp.DisplayName), utils.PtrString(resp.DeletionDate)) | ||
| } | ||
| return nil | ||
| return nil | ||
| }) | ||
| } |
@@ -6,3 +6,2 @@ package importKey | ||
| "encoding/base64" | ||
| "encoding/json" | ||
| "fmt" | ||
@@ -12,3 +11,2 @@ | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
@@ -153,22 +151,6 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| switch outputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(resp, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS key: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS key: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| default: | ||
| return p.OutputResult(outputFormat, resp, func() error { | ||
| p.Outputf("Imported a new version for the key %q inside the key ring %q\n", keyName, keyRingName) | ||
| } | ||
| return nil | ||
| return nil | ||
| }) | ||
| } | ||
@@ -175,0 +157,0 @@ |
@@ -5,3 +5,2 @@ package list | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
@@ -11,3 +10,2 @@ | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
@@ -111,18 +109,3 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| switch outputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(keys, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS Keys list: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(keys, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS Keys list: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| default: | ||
| return p.OutputResult(outputFormat, keys, func() error { | ||
| if len(keys) == 0 { | ||
@@ -150,4 +133,4 @@ p.Outputf("No keys found for project %q under the key ring %q\n", projectId, keyRingId) | ||
| } | ||
| } | ||
| return nil | ||
| return nil | ||
| }) | ||
| } |
@@ -5,3 +5,2 @@ package restore | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
@@ -11,3 +10,2 @@ | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
@@ -131,20 +129,6 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| switch outputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(resp, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal output to JSON: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal output to YAML: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| default: | ||
| return p.OutputResult(outputFormat, resp, func() error { | ||
| p.Outputf("Successfully restored KMS key %q\n", utils.PtrString(resp.DisplayName)) | ||
| } | ||
| return nil | ||
| return nil | ||
| }) | ||
| } |
@@ -5,3 +5,2 @@ package rotate | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
@@ -11,3 +10,2 @@ | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
@@ -125,22 +123,6 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| switch outputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(resp, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS key version: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS key version: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| default: | ||
| return p.OutputResult(outputFormat, resp, func() error { | ||
| p.Outputf("Rotated key %s\n", utils.PtrString(resp.KeyId)) | ||
| } | ||
| return nil | ||
| return nil | ||
| }) | ||
| } |
@@ -5,3 +5,2 @@ package create | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
@@ -11,3 +10,2 @@ | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
@@ -152,18 +150,3 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| switch model.OutputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(resp, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS key ring: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS key ring: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| default: | ||
| return p.OutputResult(model.OutputFormat, resp, func() error { | ||
| operationState := "Created" | ||
@@ -174,4 +157,4 @@ if model.Async { | ||
| p.Outputf("%s key ring. KMS key ring ID: %s\n", operationState, utils.PtrString(resp.Id)) | ||
| } | ||
| return nil | ||
| return nil | ||
| }) | ||
| } | ||
@@ -178,0 +161,0 @@ |
@@ -5,3 +5,2 @@ package list | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
@@ -11,3 +10,2 @@ | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
@@ -96,18 +94,3 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| switch outputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(keyRings, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS key rings list: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(keyRings, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS key rings list: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| default: | ||
| return p.OutputResult(outputFormat, keyRings, func() error { | ||
| if len(keyRings) == 0 { | ||
@@ -134,5 +117,4 @@ p.Outputf("No key rings found for project %q\n", projectId) | ||
| } | ||
| } | ||
| return nil | ||
| return nil | ||
| }) | ||
| } |
@@ -5,3 +5,2 @@ package destroy | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
@@ -12,3 +11,2 @@ "strconv" | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
@@ -130,22 +128,6 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| switch outputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(resp, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS key: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS key: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| default: | ||
| return p.OutputResult(outputFormat, resp, func() error { | ||
| p.Outputf("Destroyed version %d of the key %q\n", utils.PtrValue(resp.Number), utils.PtrValue(resp.KeyId)) | ||
| } | ||
| return nil | ||
| return nil | ||
| }) | ||
| } |
@@ -285,2 +285,3 @@ package disable | ||
| outputFormat string | ||
| async bool | ||
| resp *kms.Version | ||
@@ -316,3 +317,3 @@ }{ | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| err := outputResult(p, tt.outputFormat, tt.resp) | ||
| err := outputResult(p, tt.outputFormat, tt.async, tt.resp) | ||
| if (err != nil) != tt.wantErr { | ||
@@ -319,0 +320,0 @@ t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) |
@@ -5,3 +5,2 @@ package disable | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
@@ -12,3 +11,2 @@ "strconv" | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
@@ -92,3 +90,3 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| return outputResult(params.Printer, model.OutputFormat, resp) | ||
| return outputResult(params.Printer, model.OutputFormat, model.Async, resp) | ||
| }, | ||
@@ -139,3 +137,3 @@ } | ||
| func outputResult(p *print.Printer, outputFormat string, resp *kms.Version) error { | ||
| func outputResult(p *print.Printer, outputFormat string, async bool, resp *kms.Version) error { | ||
| if resp == nil { | ||
@@ -145,22 +143,10 @@ return fmt.Errorf("response is nil") | ||
| switch outputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(resp, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS key: %w", err) | ||
| return p.OutputResult(outputFormat, resp, func() error { | ||
| operationState := "Disabled" | ||
| if async { | ||
| operationState = "Triggered disable of" | ||
| } | ||
| p.Outputln(string(details)) | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS key: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| default: | ||
| p.Outputf("Disabled version %d of the key %q\n", utils.PtrValue(resp.Number), utils.PtrValue(resp.KeyId)) | ||
| } | ||
| return nil | ||
| p.Outputf("%s version %d of the key %q\n", operationState, utils.PtrValue(resp.Number), utils.PtrValue(resp.KeyId)) | ||
| return nil | ||
| }) | ||
| } |
@@ -285,2 +285,3 @@ package enable | ||
| outputFormat string | ||
| async bool | ||
| resp *kms.Version | ||
@@ -316,3 +317,3 @@ }{ | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| err := outputResult(p, tt.outputFormat, tt.resp) | ||
| err := outputResult(p, tt.outputFormat, tt.async, tt.resp) | ||
| if (err != nil) != tt.wantErr { | ||
@@ -319,0 +320,0 @@ t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) |
@@ -5,3 +5,2 @@ package enable | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
@@ -12,3 +11,2 @@ "strconv" | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
@@ -92,3 +90,3 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| return outputResult(params.Printer, model.OutputFormat, resp) | ||
| return outputResult(params.Printer, model.OutputFormat, model.Async, resp) | ||
| }, | ||
@@ -139,3 +137,3 @@ } | ||
| func outputResult(p *print.Printer, outputFormat string, resp *kms.Version) error { | ||
| func outputResult(p *print.Printer, outputFormat string, async bool, resp *kms.Version) error { | ||
| if resp == nil { | ||
@@ -145,22 +143,10 @@ return fmt.Errorf("response is nil") | ||
| switch outputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(resp, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS key: %w", err) | ||
| return p.OutputResult(outputFormat, resp, func() error { | ||
| operationState := "Enabled" | ||
| if async { | ||
| operationState = "Triggered enable of" | ||
| } | ||
| p.Outputln(string(details)) | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS key: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| default: | ||
| p.Outputf("Enabled version %d of the key %q\n", utils.PtrValue(resp.Number), utils.PtrValue(resp.KeyId)) | ||
| } | ||
| return nil | ||
| p.Outputf("%s version %d of the key %q\n", operationState, utils.PtrValue(resp.Number), utils.PtrValue(resp.KeyId)) | ||
| return nil | ||
| }) | ||
| } |
@@ -5,3 +5,2 @@ package list | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
@@ -11,3 +10,2 @@ | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
@@ -105,18 +103,3 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| switch outputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(versions, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal key versions list: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(versions, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal key versions list: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| default: | ||
| return p.OutputResult(outputFormat, versions, func() error { | ||
| if len(versions) == 0 { | ||
@@ -143,5 +126,4 @@ p.Outputf("No key versions found for project %q for the key %q\n", projectId, keyId) | ||
| } | ||
| } | ||
| return nil | ||
| return nil | ||
| }) | ||
| } | ||
@@ -148,0 +130,0 @@ |
@@ -5,3 +5,2 @@ package restore | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
@@ -12,3 +11,2 @@ "strconv" | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
@@ -130,20 +128,6 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| switch outputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(resp, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal output to JSON: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal output to YAML: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| default: | ||
| return p.OutputResult(outputFormat, resp, func() error { | ||
| p.Outputf("Restored version %d of the key %q\n", utils.PtrValue(resp.Number), utils.PtrValue(resp.KeyId)) | ||
| } | ||
| return nil | ||
| return nil | ||
| }) | ||
| } |
@@ -5,3 +5,2 @@ package create | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
@@ -11,3 +10,2 @@ | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
@@ -149,18 +147,3 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| switch model.OutputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(resp, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS wrapping key: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(resp, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS wrapping key: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| default: | ||
| return p.OutputResult(model.OutputFormat, resp, func() error { | ||
| operationState := "Created" | ||
@@ -171,5 +154,4 @@ if model.Async { | ||
| p.Outputf("%s wrapping key. Wrapping key ID: %s\n", operationState, utils.PtrString(resp.Id)) | ||
| } | ||
| return nil | ||
| return nil | ||
| }) | ||
| } | ||
@@ -176,0 +158,0 @@ |
@@ -5,3 +5,2 @@ package list | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
@@ -11,3 +10,2 @@ | ||
| "github.com/goccy/go-yaml" | ||
| "github.com/spf13/cobra" | ||
@@ -110,18 +108,3 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| switch outputFormat { | ||
| case print.JSONOutputFormat: | ||
| details, err := json.MarshalIndent(wrappingKeys, "", " ") | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS wrapping keys list: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| case print.YAMLOutputFormat: | ||
| details, err := yaml.MarshalWithOptions(wrappingKeys, yaml.IndentSequence(true), yaml.UseJSONMarshaler()) | ||
| if err != nil { | ||
| return fmt.Errorf("marshal KMS wrapping keys list: %w", err) | ||
| } | ||
| p.Outputln(string(details)) | ||
| default: | ||
| return p.OutputResult(outputFormat, wrappingKeys, func() error { | ||
| if len(wrappingKeys) == 0 { | ||
@@ -150,5 +133,4 @@ p.Outputf("No wrapping keys found under the key ring %q\n", keyRingId) | ||
| } | ||
| } | ||
| return nil | ||
| return nil | ||
| }) | ||
| } |
@@ -111,3 +111,7 @@ package restore | ||
| params.Printer.Outputf("Restored instance %q with backup %q\n", model.InstanceId, model.BackupId) | ||
| operationState := "Restored" | ||
| if model.Async { | ||
| operationState = "Triggered restore of" | ||
| } | ||
| params.Printer.Outputf("%s instance %q with backup %q\n", operationState, model.InstanceId, model.BackupId) | ||
| return nil | ||
@@ -133,3 +137,7 @@ } | ||
| params.Printer.Outputf("Cloned instance %q from backup with timestamp %q\n", model.InstanceId, model.Timestamp) | ||
| operationState := "Cloned" | ||
| if model.Async { | ||
| operationState = "Triggered clone of" | ||
| } | ||
| params.Printer.Outputf("%s instance %q from backup with timestamp %q\n", operationState, model.InstanceId, model.Timestamp) | ||
| return nil | ||
@@ -136,0 +144,0 @@ }, |
@@ -270,2 +270,3 @@ package create | ||
| outputFormat string | ||
| async bool | ||
| region string | ||
@@ -304,3 +305,3 @@ networkAreaLabel string | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.region, tt.args.networkAreaLabel, tt.args.regionalArea); (err != nil) != tt.wantErr { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.async, tt.args.region, tt.args.networkAreaLabel, tt.args.regionalArea); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
@@ -307,0 +308,0 @@ } |
@@ -120,3 +120,3 @@ package create | ||
| return outputResult(params.Printer, model.OutputFormat, model.Region, networkAreaLabel, *resp) | ||
| return outputResult(params.Printer, model.OutputFormat, model.Async, model.Region, networkAreaLabel, *resp) | ||
| }, | ||
@@ -190,7 +190,11 @@ } | ||
| func outputResult(p *print.Printer, outputFormat, region, networkAreaLabel string, regionalArea iaas.RegionalArea) error { | ||
| func outputResult(p *print.Printer, outputFormat string, async bool, region, networkAreaLabel string, regionalArea iaas.RegionalArea) error { | ||
| return p.OutputResult(outputFormat, regionalArea, func() error { | ||
| p.Outputf("Create region configuration for SNA %q.\nRegion: %s\n", networkAreaLabel, region) | ||
| operationState := "Created" | ||
| if async { | ||
| operationState = "Triggered creation of" | ||
| } | ||
| p.Outputf("%s region configuration for SNA %q.\nRegion: %s\n", operationState, networkAreaLabel, region) | ||
| return nil | ||
| }) | ||
| } |
@@ -9,2 +9,3 @@ package routingtable | ||
| rtList "github.com/stackitcloud/stackit-cli/internal/cmd/network-area/routingtable/list" | ||
| rtRoute "github.com/stackitcloud/stackit-cli/internal/cmd/network-area/routingtable/route" | ||
| rtUpdate "github.com/stackitcloud/stackit-cli/internal/cmd/network-area/routingtable/update" | ||
@@ -38,3 +39,4 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
| rtDelete.NewCmd(params), | ||
| rtRoute.NewCmd(params), | ||
| ) | ||
| } |
@@ -16,3 +16,3 @@ package create | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -23,3 +23,3 @@ | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &objectstorage.APIClient{} | ||
| var testClient = &objectstorage.APIClient{DefaultAPI: &objectstorage.DefaultAPIService{}} | ||
| var testProjectId = uuid.NewString() | ||
@@ -60,3 +60,4 @@ | ||
| }, | ||
| BucketName: testBucketName, | ||
| BucketName: testBucketName, | ||
| ObjectLockEnabled: false, | ||
| } | ||
@@ -69,6 +70,6 @@ for _, mod := range mods { | ||
| func fixtureRequest(mods ...func(request *objectstorage.ApiCreateBucketRequest)) objectstorage.ApiCreateBucketRequest { | ||
| request := testClient.CreateBucket(testCtx, testProjectId, testRegion, testBucketName) | ||
| func fixtureRequest(mods ...func(request objectstorage.ApiCreateBucketRequest) objectstorage.ApiCreateBucketRequest) objectstorage.ApiCreateBucketRequest { | ||
| request := testClient.DefaultAPI.CreateBucket(testCtx, testProjectId, testRegion, testBucketName).ObjectLockEnabled(false) | ||
| for _, mod := range mods { | ||
| mod(&request) | ||
| request = mod(request) | ||
| } | ||
@@ -141,2 +142,13 @@ return request | ||
| }, | ||
| { | ||
| description: "enable object-lock", | ||
| argValues: fixtureArgValues(), | ||
| flagValues: fixtureFlagValues(func(flagValues map[string]string) { | ||
| flagValues[objectLockEnabledFlag] = "true" | ||
| }), | ||
| expectedModel: fixtureInputModel(func(model *inputModel) { | ||
| model.ObjectLockEnabled = true | ||
| }), | ||
| isValid: true, | ||
| }, | ||
| } | ||
@@ -162,2 +174,11 @@ | ||
| }, | ||
| { | ||
| description: "object-lock enabled", | ||
| model: fixtureInputModel(func(model *inputModel) { | ||
| model.ObjectLockEnabled = true | ||
| }), | ||
| expectedRequest: fixtureRequest(func(request objectstorage.ApiCreateBucketRequest) objectstorage.ApiCreateBucketRequest { | ||
| return request.ObjectLockEnabled(true) | ||
| }), | ||
| }, | ||
| } | ||
@@ -170,3 +191,3 @@ | ||
| diff := cmp.Diff(request, tt.expectedRequest, | ||
| cmp.AllowUnexported(tt.expectedRequest), | ||
| cmp.AllowUnexported(tt.expectedRequest, objectstorage.DefaultAPIService{}), | ||
| cmpopts.EquateComparable(testCtx), | ||
@@ -173,0 +194,0 @@ ) |
@@ -7,2 +7,3 @@ package create | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/flags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
@@ -20,8 +21,9 @@ | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/wait" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api/wait" | ||
| ) | ||
| const ( | ||
| bucketNameArg = "BUCKET_NAME" | ||
| bucketNameArg = "BUCKET_NAME" | ||
| objectLockEnabledFlag = "object-lock-enabled" | ||
| ) | ||
@@ -31,3 +33,4 @@ | ||
| *globalflags.GlobalFlagModel | ||
| BucketName string | ||
| BucketName string | ||
| ObjectLockEnabled bool | ||
| } | ||
@@ -45,2 +48,5 @@ | ||
| "$ stackit object-storage bucket create my-bucket"), | ||
| examples.NewExample( | ||
| `Create an Object Storage bucket with enabled object-lock`, | ||
| `$ stackit object-storage bucket create my-bucket --object-lock-enabled`), | ||
| ), | ||
@@ -67,3 +73,3 @@ RunE: func(cmd *cobra.Command, args []string) error { | ||
| // Check if the project is enabled before trying to create | ||
| enabled, err := utils.ProjectEnabled(ctx, apiClient, model.ProjectId, model.Region) | ||
| enabled, err := utils.ProjectEnabled(ctx, apiClient.DefaultAPI, model.ProjectId, model.Region) | ||
| if err != nil { | ||
@@ -89,3 +95,3 @@ return fmt.Errorf("check if Object Storage is enabled: %w", err) | ||
| s.Start("Creating bucket") | ||
| _, err = wait.CreateBucketWaitHandler(ctx, apiClient, model.ProjectId, model.Region, model.BucketName).WaitWithContext(ctx) | ||
| _, err = wait.CreateBucketWaitHandler(ctx, apiClient.DefaultAPI, model.ProjectId, model.Region, model.BucketName).WaitWithContext(ctx) | ||
| if err != nil { | ||
@@ -100,5 +106,10 @@ return fmt.Errorf("wait for Object Storage bucket creation: %w", err) | ||
| } | ||
| configureFlags(cmd) | ||
| return cmd | ||
| } | ||
| func configureFlags(cmd *cobra.Command) { | ||
| cmd.Flags().Bool(objectLockEnabledFlag, false, "is the object-lock enabled for the bucket") | ||
| } | ||
| func parseInput(p *print.Printer, cmd *cobra.Command, inputArgs []string) (*inputModel, error) { | ||
@@ -113,4 +124,5 @@ bucketName := inputArgs[0] | ||
| model := inputModel{ | ||
| GlobalFlagModel: globalFlags, | ||
| BucketName: bucketName, | ||
| GlobalFlagModel: globalFlags, | ||
| BucketName: bucketName, | ||
| ObjectLockEnabled: flags.FlagToBoolValue(p, cmd, objectLockEnabledFlag), | ||
| } | ||
@@ -123,3 +135,3 @@ | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *objectstorage.APIClient) objectstorage.ApiCreateBucketRequest { | ||
| req := apiClient.CreateBucket(ctx, model.ProjectId, model.Region, model.BucketName) | ||
| req := apiClient.DefaultAPI.CreateBucket(ctx, model.ProjectId, model.Region, model.BucketName).ObjectLockEnabled(model.ObjectLockEnabled) | ||
| return req | ||
@@ -126,0 +138,0 @@ } |
@@ -13,3 +13,3 @@ package delete | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -20,3 +20,3 @@ | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &objectstorage.APIClient{} | ||
| var testClient = &objectstorage.APIClient{DefaultAPI: &objectstorage.DefaultAPIService{}} | ||
| var testProjectId = uuid.NewString() | ||
@@ -66,3 +66,3 @@ | ||
| func fixtureRequest(mods ...func(request *objectstorage.ApiDeleteBucketRequest)) objectstorage.ApiDeleteBucketRequest { | ||
| request := testClient.DeleteBucket(testCtx, testProjectId, testRegion, testBucketName) | ||
| request := testClient.DefaultAPI.DeleteBucket(testCtx, testProjectId, testRegion, testBucketName) | ||
| for _, mod := range mods { | ||
@@ -164,3 +164,3 @@ mod(&request) | ||
| diff := cmp.Diff(request, tt.expectedRequest, | ||
| cmp.AllowUnexported(tt.expectedRequest), | ||
| cmp.AllowUnexported(tt.expectedRequest, objectstorage.DefaultAPIService{}), | ||
| cmpopts.EquateComparable(testCtx), | ||
@@ -167,0 +167,0 @@ ) |
@@ -18,4 +18,4 @@ package delete | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/wait" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api/wait" | ||
| ) | ||
@@ -73,3 +73,3 @@ | ||
| s.Start("Deleting bucket") | ||
| _, err = wait.DeleteBucketWaitHandler(ctx, apiClient, model.ProjectId, model.Region, model.BucketName).WaitWithContext(ctx) | ||
| _, err = wait.DeleteBucketWaitHandler(ctx, apiClient.DefaultAPI, model.ProjectId, model.Region, model.BucketName).WaitWithContext(ctx) | ||
| if err != nil { | ||
@@ -110,4 +110,4 @@ return fmt.Errorf("wait for Object Storage bucket deletion: %w", err) | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *objectstorage.APIClient) objectstorage.ApiDeleteBucketRequest { | ||
| req := apiClient.DeleteBucket(ctx, model.ProjectId, model.Region, model.BucketName) | ||
| req := apiClient.DefaultAPI.DeleteBucket(ctx, model.ProjectId, model.Region, model.BucketName) | ||
| return req | ||
| } |
@@ -16,3 +16,3 @@ package describe | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -23,3 +23,3 @@ | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &objectstorage.APIClient{} | ||
| var testClient = &objectstorage.APIClient{DefaultAPI: &objectstorage.DefaultAPIService{}} | ||
| var testProjectId = uuid.NewString() | ||
@@ -69,3 +69,3 @@ | ||
| func fixtureRequest(mods ...func(request *objectstorage.ApiGetBucketRequest)) objectstorage.ApiGetBucketRequest { | ||
| request := testClient.GetBucket(testCtx, testProjectId, testRegion, testBucketName) | ||
| request := testClient.DefaultAPI.GetBucket(testCtx, testProjectId, testRegion, testBucketName) | ||
| for _, mod := range mods { | ||
@@ -167,3 +167,3 @@ mod(&request) | ||
| diff := cmp.Diff(request, tt.expectedRequest, | ||
| cmp.AllowUnexported(tt.expectedRequest), | ||
| cmp.AllowUnexported(tt.expectedRequest, objectstorage.DefaultAPIService{}), | ||
| cmpopts.EquateComparable(testCtx), | ||
@@ -181,3 +181,3 @@ ) | ||
| outputFormat string | ||
| bucket *objectstorage.Bucket | ||
| resp *objectstorage.GetBucketResponse | ||
| } | ||
@@ -195,5 +195,5 @@ tests := []struct { | ||
| { | ||
| name: "set empty bucket", | ||
| name: "set empty response", | ||
| args: args{ | ||
| bucket: &objectstorage.Bucket{}, | ||
| resp: &objectstorage.GetBucketResponse{}, | ||
| }, | ||
@@ -207,3 +207,3 @@ wantErr: false, | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.bucket); (err != nil) != tt.wantErr { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.resp); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
@@ -210,0 +210,0 @@ } |
@@ -16,6 +16,5 @@ package describe | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/tables" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -65,3 +64,3 @@ | ||
| return outputResult(params.Printer, model.OutputFormat, resp.Bucket) | ||
| return outputResult(params.Printer, model.OutputFormat, resp) | ||
| }, | ||
@@ -90,21 +89,23 @@ } | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *objectstorage.APIClient) objectstorage.ApiGetBucketRequest { | ||
| req := apiClient.GetBucket(ctx, model.ProjectId, model.Region, model.BucketName) | ||
| req := apiClient.DefaultAPI.GetBucket(ctx, model.ProjectId, model.Region, model.BucketName) | ||
| return req | ||
| } | ||
| func outputResult(p *print.Printer, outputFormat string, bucket *objectstorage.Bucket) error { | ||
| if bucket == nil { | ||
| return fmt.Errorf("bucket is empty") | ||
| func outputResult(p *print.Printer, outputFormat string, resp *objectstorage.GetBucketResponse) error { | ||
| if resp == nil { | ||
| return fmt.Errorf("response is nil") | ||
| } | ||
| return p.OutputResult(outputFormat, bucket, func() error { | ||
| return p.OutputResult(outputFormat, resp.Bucket, func() error { | ||
| table := tables.NewTable() | ||
| table.AddRow("Name", utils.PtrString(bucket.Name)) | ||
| table.AddRow("Name", resp.Bucket.Name) | ||
| table.AddSeparator() | ||
| table.AddRow("Region", utils.PtrString(bucket.Region)) | ||
| table.AddRow("Region", resp.Bucket.Region) | ||
| table.AddSeparator() | ||
| table.AddRow("URL (Path Style)", utils.PtrString(bucket.UrlPathStyle)) | ||
| table.AddRow("URL (Path Style)", resp.Bucket.UrlPathStyle) | ||
| table.AddSeparator() | ||
| table.AddRow("URL (Virtual Hosted Style)", utils.PtrString(bucket.UrlVirtualHostedStyle)) | ||
| table.AddRow("URL (Virtual Hosted Style)", resp.Bucket.UrlVirtualHostedStyle) | ||
| table.AddSeparator() | ||
| table.AddRow("Object Lock Enabled", resp.Bucket.ObjectLockEnabled) | ||
| table.AddSeparator() | ||
| err := table.Display(p) | ||
@@ -111,0 +112,0 @@ if err != nil { |
@@ -17,3 +17,3 @@ package list | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -24,3 +24,3 @@ | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &objectstorage.APIClient{} | ||
| var testClient = &objectstorage.APIClient{DefaultAPI: &objectstorage.DefaultAPIService{}} | ||
| var testProjectId = uuid.NewString() | ||
@@ -57,3 +57,3 @@ var testRegion = "eu01" | ||
| func fixtureRequest(mods ...func(request *objectstorage.ApiListBucketsRequest)) objectstorage.ApiListBucketsRequest { | ||
| request := testClient.ListBuckets(testCtx, testProjectId, testRegion) | ||
| request := testClient.DefaultAPI.ListBuckets(testCtx, testProjectId, testRegion) | ||
| for _, mod := range mods { | ||
@@ -146,3 +146,3 @@ mod(&request) | ||
| diff := cmp.Diff(request, tt.expectedRequest, | ||
| cmp.AllowUnexported(tt.expectedRequest), | ||
| cmp.AllowUnexported(tt.expectedRequest, objectstorage.DefaultAPIService{}), | ||
| cmpopts.EquateComparable(testCtx), | ||
@@ -149,0 +149,0 @@ ) |
@@ -19,4 +19,3 @@ package list | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/tables" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -118,3 +117,3 @@ | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *objectstorage.APIClient) objectstorage.ApiListBucketsRequest { | ||
| req := apiClient.ListBuckets(ctx, model.ProjectId, model.Region) | ||
| req := apiClient.DefaultAPI.ListBuckets(ctx, model.ProjectId, model.Region) | ||
| return req | ||
@@ -135,10 +134,11 @@ } | ||
| table := tables.NewTable() | ||
| table.SetHeader("NAME", "REGION", "URL (PATH STYLE)", "URL (VIRTUAL HOSTED STYLE)") | ||
| table.SetHeader("NAME", "REGION", "URL (PATH STYLE)", "URL (VIRTUAL HOSTED STYLE)", "OBJECT LOCK ENABLED") | ||
| for i := range buckets { | ||
| bucket := buckets[i] | ||
| table.AddRow( | ||
| utils.PtrString(bucket.Name), | ||
| utils.PtrString(bucket.Region), | ||
| utils.PtrString(bucket.UrlPathStyle), | ||
| utils.PtrString(bucket.UrlVirtualHostedStyle), | ||
| bucket.Name, | ||
| bucket.Region, | ||
| bucket.UrlPathStyle, | ||
| bucket.UrlVirtualHostedStyle, | ||
| bucket.ObjectLockEnabled, | ||
| ) | ||
@@ -145,0 +145,0 @@ } |
@@ -9,11 +9,9 @@ package create | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/print" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/testutils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/google/go-cmp/cmp" | ||
| "github.com/google/go-cmp/cmp/cmpopts" | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -24,3 +22,3 @@ | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &objectstorage.APIClient{} | ||
| var testClient = &objectstorage.APIClient{DefaultAPI: &objectstorage.DefaultAPIService{}} | ||
| var testProjectId = uuid.NewString() | ||
@@ -62,3 +60,3 @@ | ||
| payload := objectstorage.CreateCredentialsGroupPayload{ | ||
| DisplayName: utils.Ptr(testCredentialsGroupName), | ||
| DisplayName: testCredentialsGroupName, | ||
| } | ||
@@ -72,3 +70,3 @@ for _, mod := range mods { | ||
| func fixtureRequest(mods ...func(request *objectstorage.ApiCreateCredentialsGroupRequest)) objectstorage.ApiCreateCredentialsGroupRequest { | ||
| request := testClient.CreateCredentialsGroup(testCtx, testProjectId, testRegion) | ||
| request := testClient.DefaultAPI.CreateCredentialsGroup(testCtx, testProjectId, testRegion) | ||
| request = request.CreateCredentialsGroupPayload(fixturePayload()) | ||
@@ -155,3 +153,3 @@ for _, mod := range mods { | ||
| diff := cmp.Diff(request, tt.expectedRequest, | ||
| cmp.AllowUnexported(tt.expectedRequest), | ||
| cmp.AllowUnexported(tt.expectedRequest, objectstorage.DefaultAPIService{}), | ||
| cmpopts.EquateComparable(testCtx), | ||
@@ -186,3 +184,3 @@ ) | ||
| }, | ||
| wantErr: true, | ||
| wantErr: false, | ||
| }, | ||
@@ -193,3 +191,3 @@ { | ||
| createCredentialsGroupResponse: &objectstorage.CreateCredentialsGroupResponse{ | ||
| CredentialsGroup: &objectstorage.CredentialsGroup{}, | ||
| CredentialsGroup: objectstorage.CredentialsGroup{}, | ||
| }, | ||
@@ -196,0 +194,0 @@ }, |
@@ -9,2 +9,3 @@ package create | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/args" | ||
@@ -17,6 +18,3 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/errors" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/services/object-storage/client" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -100,5 +98,5 @@ | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *objectstorage.APIClient) objectstorage.ApiCreateCredentialsGroupRequest { | ||
| req := apiClient.CreateCredentialsGroup(ctx, model.ProjectId, model.Region) | ||
| req := apiClient.DefaultAPI.CreateCredentialsGroup(ctx, model.ProjectId, model.Region) | ||
| req = req.CreateCredentialsGroupPayload(objectstorage.CreateCredentialsGroupPayload{ | ||
| DisplayName: utils.Ptr(model.CredentialsGroupName), | ||
| DisplayName: model.CredentialsGroupName, | ||
| }) | ||
@@ -109,14 +107,14 @@ return req | ||
| func outputResult(p *print.Printer, outputFormat string, resp *objectstorage.CreateCredentialsGroupResponse) error { | ||
| if resp == nil || resp.CredentialsGroup == nil { | ||
| return fmt.Errorf("create createndials group response is empty") | ||
| } | ||
| return p.OutputResult(outputFormat, resp, func() error { | ||
| if resp == nil { | ||
| return fmt.Errorf("create credentials group response is empty") | ||
| } | ||
| return p.OutputResult(outputFormat, resp, func() error { | ||
| p.Outputf("Created credentials group %q. Credentials group ID: %s\n\n", | ||
| utils.PtrString(resp.CredentialsGroup.DisplayName), | ||
| utils.PtrString(resp.CredentialsGroup.CredentialsGroupId), | ||
| resp.CredentialsGroup.DisplayName, | ||
| resp.CredentialsGroup.CredentialsGroupId, | ||
| ) | ||
| p.Outputf("URN: %s\n", utils.PtrString(resp.CredentialsGroup.Urn)) | ||
| p.Outputf("URN: %s\n", resp.CredentialsGroup.Urn) | ||
| return nil | ||
| }) | ||
| } |
@@ -13,3 +13,3 @@ package delete | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -20,3 +20,3 @@ | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &objectstorage.APIClient{} | ||
| var testClient = &objectstorage.APIClient{DefaultAPI: &objectstorage.DefaultAPIService{}} | ||
| var testProjectId = uuid.NewString() | ||
@@ -64,3 +64,3 @@ var testCredentialsGroupId = uuid.NewString() | ||
| func fixtureRequest(mods ...func(request *objectstorage.ApiDeleteCredentialsGroupRequest)) objectstorage.ApiDeleteCredentialsGroupRequest { | ||
| request := testClient.DeleteCredentialsGroup(testCtx, testProjectId, testRegion, testCredentialsGroupId) | ||
| request := testClient.DefaultAPI.DeleteCredentialsGroup(testCtx, testProjectId, testRegion, testCredentialsGroupId) | ||
| for _, mod := range mods { | ||
@@ -168,3 +168,3 @@ mod(&request) | ||
| diff := cmp.Diff(request, tt.expectedRequest, | ||
| cmp.AllowUnexported(tt.expectedRequest), | ||
| cmp.AllowUnexported(tt.expectedRequest, objectstorage.DefaultAPIService{}), | ||
| cmpopts.EquateComparable(testCtx), | ||
@@ -171,0 +171,0 @@ ) |
@@ -19,3 +19,3 @@ package delete | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -56,3 +56,3 @@ | ||
| credentialsGroupLabel, err := objectStorageUtils.GetCredentialsGroupName(ctx, apiClient, model.ProjectId, model.CredentialsGroupId, model.Region) | ||
| credentialsGroupLabel, err := objectStorageUtils.GetCredentialsGroupName(ctx, apiClient.DefaultAPI, model.ProjectId, model.CredentialsGroupId, model.Region) | ||
| if err != nil { | ||
@@ -101,4 +101,4 @@ params.Printer.Debug(print.ErrorLevel, "get credentials group name: %v", err) | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *objectstorage.APIClient) objectstorage.ApiDeleteCredentialsGroupRequest { | ||
| req := apiClient.DeleteCredentialsGroup(ctx, model.ProjectId, model.Region, model.CredentialsGroupId) | ||
| req := apiClient.DefaultAPI.DeleteCredentialsGroup(ctx, model.ProjectId, model.Region, model.CredentialsGroupId) | ||
| return req | ||
| } |
@@ -17,3 +17,3 @@ package list | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -24,3 +24,3 @@ | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &objectstorage.APIClient{} | ||
| var testClient = &objectstorage.APIClient{DefaultAPI: &objectstorage.DefaultAPIService{}} | ||
| var testProjectId = uuid.NewString() | ||
@@ -58,3 +58,3 @@ | ||
| func fixtureRequest(mods ...func(request *objectstorage.ApiListCredentialsGroupsRequest)) objectstorage.ApiListCredentialsGroupsRequest { | ||
| request := testClient.ListCredentialsGroups(testCtx, testProjectId, testRegion) | ||
| request := testClient.DefaultAPI.ListCredentialsGroups(testCtx, testProjectId, testRegion) | ||
| for _, mod := range mods { | ||
@@ -147,3 +147,3 @@ mod(&request) | ||
| diff := cmp.Diff(request, tt.expectedRequest, | ||
| cmp.AllowUnexported(tt.expectedRequest), | ||
| cmp.AllowUnexported(tt.expectedRequest, objectstorage.DefaultAPIService{}), | ||
| cmpopts.EquateComparable(testCtx), | ||
@@ -150,0 +150,0 @@ ) |
@@ -18,4 +18,3 @@ package list | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/tables" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -109,3 +108,3 @@ | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *objectstorage.APIClient) objectstorage.ApiListCredentialsGroupsRequest { | ||
| req := apiClient.ListCredentialsGroups(ctx, model.ProjectId, model.Region) | ||
| req := apiClient.DefaultAPI.ListCredentialsGroups(ctx, model.ProjectId, model.Region) | ||
| return req | ||
@@ -126,5 +125,5 @@ } | ||
| table.AddRow( | ||
| utils.PtrString(c.CredentialsGroupId), | ||
| utils.PtrString(c.DisplayName), | ||
| utils.PtrString(c.Urn), | ||
| c.CredentialsGroupId, | ||
| c.DisplayName, | ||
| c.Urn, | ||
| ) | ||
@@ -131,0 +130,0 @@ } |
@@ -18,3 +18,3 @@ package create | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -25,3 +25,3 @@ | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &objectstorage.APIClient{} | ||
| var testClient = &objectstorage.APIClient{DefaultAPI: &objectstorage.DefaultAPIService{}} | ||
| var testProjectId = uuid.NewString() | ||
@@ -84,3 +84,3 @@ var testCredentialsGroupId = uuid.NewString() | ||
| func fixtureRequest(mods ...func(request *objectstorage.ApiCreateAccessKeyRequest)) objectstorage.ApiCreateAccessKeyRequest { | ||
| request := testClient.CreateAccessKey(testCtx, testProjectId, testRegion) | ||
| request := testClient.DefaultAPI.CreateAccessKey(testCtx, testProjectId, testRegion) | ||
| request = request.CreateAccessKeyPayload(fixturePayload()) | ||
@@ -213,3 +213,3 @@ request = request.CredentialsGroup(testCredentialsGroupId) | ||
| diff := cmp.Diff(request, tt.expectedRequest, | ||
| cmp.AllowUnexported(tt.expectedRequest), | ||
| cmp.AllowUnexported(tt.expectedRequest, objectstorage.DefaultAPIService{}), | ||
| cmpopts.EquateComparable(testCtx), | ||
@@ -216,0 +216,0 @@ ) |
@@ -19,4 +19,3 @@ package create | ||
| objectStorageUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/object-storage/utils" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -64,3 +63,3 @@ | ||
| credentialsGroupLabel, err := objectStorageUtils.GetCredentialsGroupName(ctx, apiClient, model.ProjectId, model.CredentialsGroupId, model.Region) | ||
| credentialsGroupLabel, err := objectStorageUtils.GetCredentialsGroupName(ctx, apiClient.DefaultAPI, model.ProjectId, model.CredentialsGroupId, model.Region) | ||
| if err != nil { | ||
@@ -124,3 +123,3 @@ params.Printer.Debug(print.ErrorLevel, "get credentials group name: %v", err) | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *objectstorage.APIClient) objectstorage.ApiCreateAccessKeyRequest { | ||
| req := apiClient.CreateAccessKey(ctx, model.ProjectId, model.Region) | ||
| req := apiClient.DefaultAPI.CreateAccessKey(ctx, model.ProjectId, model.Region) | ||
| req = req.CredentialsGroup(model.CredentialsGroupId) | ||
@@ -140,9 +139,9 @@ req = req.CreateAccessKeyPayload(objectstorage.CreateAccessKeyPayload{ | ||
| expireDate := "Never" | ||
| if resp.Expires != nil && resp.Expires.IsSet() && *resp.Expires.Get() != "" { | ||
| if resp.Expires.IsSet() && *resp.Expires.Get() != "" { | ||
| expireDate = *resp.Expires.Get() | ||
| } | ||
| p.Outputf("Created credentials in group %q. Credentials ID: %s\n\n", credentialsGroupLabel, utils.PtrString(resp.KeyId)) | ||
| p.Outputf("Access Key ID: %s\n", utils.PtrString(resp.AccessKey)) | ||
| p.Outputf("Secret Access Key: %s\n", utils.PtrString(resp.SecretAccessKey)) | ||
| p.Outputf("Created credentials in group %q. Credentials ID: %s\n\n", credentialsGroupLabel, resp.KeyId) | ||
| p.Outputf("Access Key ID: %s\n", resp.AccessKey) | ||
| p.Outputf("Secret Access Key: %s\n", resp.SecretAccessKey) | ||
| p.Outputf("Expire Date: %s\n", expireDate) | ||
@@ -149,0 +148,0 @@ |
@@ -13,3 +13,3 @@ package delete | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -20,3 +20,3 @@ | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &objectstorage.APIClient{} | ||
| var testClient = &objectstorage.APIClient{DefaultAPI: &objectstorage.DefaultAPIService{}} | ||
| var testProjectId = uuid.NewString() | ||
@@ -69,3 +69,3 @@ var testCredentialsGroupId = uuid.NewString() | ||
| func fixtureRequest(mods ...func(request *objectstorage.ApiDeleteAccessKeyRequest)) objectstorage.ApiDeleteAccessKeyRequest { | ||
| request := testClient.DeleteAccessKey(testCtx, testProjectId, testRegion, testCredentialsId) | ||
| request := testClient.DefaultAPI.DeleteAccessKey(testCtx, testProjectId, testRegion, testCredentialsId) | ||
| request = request.CredentialsGroup(testCredentialsGroupId) | ||
@@ -189,3 +189,3 @@ for _, mod := range mods { | ||
| diff := cmp.Diff(request, tt.expectedRequest, | ||
| cmp.AllowUnexported(tt.expectedRequest), | ||
| cmp.AllowUnexported(tt.expectedRequest, objectstorage.DefaultAPIService{}), | ||
| cmpopts.EquateComparable(testCtx), | ||
@@ -192,0 +192,0 @@ ) |
@@ -18,3 +18,3 @@ package delete | ||
| objectStorageUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/object-storage/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -57,3 +57,3 @@ | ||
| credentialsGroupLabel, err := objectStorageUtils.GetCredentialsGroupName(ctx, apiClient, model.ProjectId, model.CredentialsGroupId, model.Region) | ||
| credentialsGroupLabel, err := objectStorageUtils.GetCredentialsGroupName(ctx, apiClient.DefaultAPI, model.ProjectId, model.CredentialsGroupId, model.Region) | ||
| if err != nil { | ||
@@ -64,3 +64,3 @@ params.Printer.Debug(print.ErrorLevel, "get credentials group name: %v", err) | ||
| credentialsLabel, err := objectStorageUtils.GetCredentialsName(ctx, apiClient, model.ProjectId, model.CredentialsGroupId, model.CredentialsId, model.Region) | ||
| credentialsLabel, err := objectStorageUtils.GetCredentialsName(ctx, apiClient.DefaultAPI, model.ProjectId, model.CredentialsGroupId, model.CredentialsId, model.Region) | ||
| if err != nil { | ||
@@ -118,5 +118,5 @@ params.Printer.Debug(print.ErrorLevel, "get credentials name: %v", err) | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *objectstorage.APIClient) objectstorage.ApiDeleteAccessKeyRequest { | ||
| req := apiClient.DeleteAccessKey(ctx, model.ProjectId, model.Region, model.CredentialsId) | ||
| req := apiClient.DefaultAPI.DeleteAccessKey(ctx, model.ProjectId, model.Region, model.CredentialsId) | ||
| req = req.CredentialsGroup(model.CredentialsGroupId) | ||
| return req | ||
| } |
@@ -17,3 +17,3 @@ package list | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -24,3 +24,3 @@ | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &objectstorage.APIClient{} | ||
| var testClient = &objectstorage.APIClient{DefaultAPI: &objectstorage.DefaultAPIService{}} | ||
| var testProjectId = uuid.NewString() | ||
@@ -60,3 +60,3 @@ var testCredentialsGroupId = uuid.NewString() | ||
| func fixtureRequest(mods ...func(request *objectstorage.ApiListAccessKeysRequest)) objectstorage.ApiListAccessKeysRequest { | ||
| request := testClient.ListAccessKeys(testCtx, testProjectId, testRegion) | ||
| request := testClient.DefaultAPI.ListAccessKeys(testCtx, testProjectId, testRegion) | ||
| request = request.CredentialsGroup(testCredentialsGroupId) | ||
@@ -171,3 +171,3 @@ for _, mod := range mods { | ||
| diff := cmp.Diff(request, tt.expectedRequest, | ||
| cmp.AllowUnexported(tt.expectedRequest), | ||
| cmp.AllowUnexported(tt.expectedRequest, objectstorage.DefaultAPIService{}), | ||
| cmpopts.EquateComparable(testCtx), | ||
@@ -174,0 +174,0 @@ ) |
@@ -19,4 +19,3 @@ package list | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/tables" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -73,3 +72,3 @@ | ||
| credentialsGroupLabel, err := objectStorageUtils.GetCredentialsGroupName(ctx, apiClient, model.ProjectId, model.CredentialsGroupId, model.Region) | ||
| credentialsGroupLabel, err := objectStorageUtils.GetCredentialsGroupName(ctx, apiClient.DefaultAPI, model.ProjectId, model.CredentialsGroupId, model.Region) | ||
| if err != nil { | ||
@@ -124,3 +123,3 @@ params.Printer.Debug(print.ErrorLevel, "get credentials group name: %v", err) | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *objectstorage.APIClient) objectstorage.ApiListAccessKeysRequest { | ||
| req := apiClient.ListAccessKeys(ctx, model.ProjectId, model.Region) | ||
| req := apiClient.DefaultAPI.ListAccessKeys(ctx, model.ProjectId, model.Region) | ||
| req = req.CredentialsGroup(model.CredentialsGroupId) | ||
@@ -142,4 +141,7 @@ return req | ||
| expiresAt := utils.PtrStringDefault(c.Expires, "Never") | ||
| table.AddRow(utils.PtrString(c.KeyId), utils.PtrString(c.DisplayName), expiresAt) | ||
| expiresAt := "Never" | ||
| if c.Expires != "" { | ||
| expiresAt = c.Expires | ||
| } | ||
| table.AddRow(c.KeyId, c.DisplayName, expiresAt) | ||
| } | ||
@@ -146,0 +148,0 @@ err := table.Display(p) |
@@ -13,3 +13,3 @@ package disable | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -20,3 +20,3 @@ | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &objectstorage.APIClient{} | ||
| var testClient = &objectstorage.APIClient{DefaultAPI: &objectstorage.DefaultAPIService{}} | ||
| var testProjectId = uuid.NewString() | ||
@@ -52,3 +52,3 @@ | ||
| func fixtureRequest(mods ...func(request *objectstorage.ApiDisableServiceRequest)) objectstorage.ApiDisableServiceRequest { | ||
| request := testClient.DisableService(testCtx, testProjectId, testRegion) | ||
| request := testClient.DefaultAPI.DisableService(testCtx, testProjectId, testRegion) | ||
| for _, mod := range mods { | ||
@@ -127,3 +127,3 @@ mod(&request) | ||
| diff := cmp.Diff(request, tt.expectedRequest, | ||
| cmp.AllowUnexported(tt.expectedRequest), | ||
| cmp.AllowUnexported(tt.expectedRequest, objectstorage.DefaultAPIService{}), | ||
| cmpopts.EquateComparable(testCtx), | ||
@@ -130,0 +130,0 @@ ) |
@@ -18,3 +18,3 @@ package disable | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -95,4 +95,4 @@ | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *objectstorage.APIClient) objectstorage.ApiDisableServiceRequest { | ||
| req := apiClient.DisableService(ctx, model.ProjectId, model.Region) | ||
| req := apiClient.DefaultAPI.DisableService(ctx, model.ProjectId, model.Region) | ||
| return req | ||
| } |
@@ -13,3 +13,3 @@ package enable | ||
| "github.com/google/uuid" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -20,3 +20,3 @@ | ||
| var testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo") | ||
| var testClient = &objectstorage.APIClient{} | ||
| var testClient = &objectstorage.APIClient{DefaultAPI: &objectstorage.DefaultAPIService{}} | ||
| var testProjectId = uuid.NewString() | ||
@@ -52,3 +52,3 @@ | ||
| func fixtureRequest(mods ...func(request *objectstorage.ApiEnableServiceRequest)) objectstorage.ApiEnableServiceRequest { | ||
| request := testClient.EnableService(testCtx, testProjectId, testRegion) | ||
| request := testClient.DefaultAPI.EnableService(testCtx, testProjectId, testRegion) | ||
| for _, mod := range mods { | ||
@@ -127,3 +127,3 @@ mod(&request) | ||
| diff := cmp.Diff(request, tt.expectedRequest, | ||
| cmp.AllowUnexported(tt.expectedRequest), | ||
| cmp.AllowUnexported(tt.expectedRequest, objectstorage.DefaultAPIService{}), | ||
| cmpopts.EquateComparable(testCtx), | ||
@@ -130,0 +130,0 @@ ) |
@@ -18,3 +18,3 @@ package enable | ||
| "github.com/spf13/cobra" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -95,4 +95,4 @@ | ||
| func buildRequest(ctx context.Context, model *inputModel, apiClient *objectstorage.APIClient) objectstorage.ApiEnableServiceRequest { | ||
| req := apiClient.EnableService(ctx, model.ProjectId, model.Region) | ||
| req := apiClient.DefaultAPI.EnableService(ctx, model.ProjectId, model.Region) | ||
| return req | ||
| } |
@@ -8,2 +8,3 @@ package describe | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
@@ -224,2 +225,31 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/globalflags" | ||
| }, | ||
| { | ||
| name: "host and hosts", | ||
| args: args{ | ||
| credentials: &opensearch.CredentialsResponse{ | ||
| Raw: &opensearch.RawCredentials{ | ||
| Credentials: &opensearch.Credentials{ | ||
| Host: utils.Ptr("host"), | ||
| Hosts: utils.Ptr([]string{ | ||
| "hosts-a", | ||
| "hosts-b", | ||
| }), | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| { | ||
| name: "raw credentials nil host & hosts", | ||
| args: args{ | ||
| credentials: &opensearch.CredentialsResponse{ | ||
| Raw: &opensearch.RawCredentials{ | ||
| Credentials: &opensearch.Credentials{ | ||
| Host: nil, | ||
| Hosts: nil, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| } | ||
@@ -226,0 +256,0 @@ p := print.NewPrinter() |
@@ -6,2 +6,3 @@ package describe | ||
| "fmt" | ||
| "strings" | ||
@@ -125,2 +126,9 @@ "github.com/stackitcloud/stackit-cli/internal/pkg/types" | ||
| table.AddRow("URI", utils.PtrString(credentials.Raw.Credentials.Uri)) | ||
| table.AddSeparator() | ||
| table.AddRow("HOST", utils.PtrString(credentials.Raw.Credentials.Host)) | ||
| hosts := credentials.Raw.Credentials.Hosts | ||
| if hosts != nil && len(*hosts) > 0 { | ||
| table.AddSeparator() | ||
| table.AddRow("HOSTS", strings.Join(*hosts, "\n")) | ||
| } | ||
| } | ||
@@ -127,0 +135,0 @@ err := table.Display(p) |
@@ -385,2 +385,3 @@ package create | ||
| outputFormat string | ||
| async bool | ||
| projectLabel string | ||
@@ -411,3 +412,3 @@ server *iaas.Server | ||
| t.Run(tt.name, func(t *testing.T) { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.projectLabel, tt.args.server); (err != nil) != tt.wantErr { | ||
| if err := outputResult(p, tt.args.outputFormat, tt.args.async, tt.args.projectLabel, tt.args.server); (err != nil) != tt.wantErr { | ||
| t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr) | ||
@@ -414,0 +415,0 @@ } |
@@ -156,3 +156,3 @@ package create | ||
| return outputResult(params.Printer, model.OutputFormat, projectLabel, resp) | ||
| return outputResult(params.Printer, model.OutputFormat, model.Async, projectLabel, resp) | ||
| }, | ||
@@ -326,3 +326,3 @@ } | ||
| func outputResult(p *print.Printer, outputFormat, projectLabel string, server *iaas.Server) error { | ||
| func outputResult(p *print.Printer, outputFormat string, async bool, projectLabel string, server *iaas.Server) error { | ||
| if server == nil { | ||
@@ -332,5 +332,9 @@ return fmt.Errorf("server response is empty") | ||
| return p.OutputResult(outputFormat, server, func() error { | ||
| p.Outputf("Created server for project %q.\nServer ID: %s\n", projectLabel, utils.PtrString(server.Id)) | ||
| operationState := "Created" | ||
| if async { | ||
| operationState = "Triggered creation of" | ||
| } | ||
| p.Outputf("%s server for project %q.\nServer ID: %s\n", operationState, projectLabel, utils.PtrString(server.Id)) | ||
| return nil | ||
| }) | ||
| } |
@@ -20,2 +20,3 @@ package server | ||
| "github.com/stackitcloud/stackit-cli/internal/cmd/server/resize" | ||
| securitygroup "github.com/stackitcloud/stackit-cli/internal/cmd/server/security-group" | ||
| serviceaccount "github.com/stackitcloud/stackit-cli/internal/cmd/server/service-account" | ||
@@ -55,2 +56,3 @@ "github.com/stackitcloud/stackit-cli/internal/cmd/server/start" | ||
| cmd.AddCommand(publicip.NewCmd(params)) | ||
| cmd.AddCommand(securitygroup.NewCmd(params)) | ||
| cmd.AddCommand(serviceaccount.NewCmd(params)) | ||
@@ -57,0 +59,0 @@ cmd.AddCommand(update.NewCmd(params)) |
@@ -66,3 +66,3 @@ package detach | ||
| prompt := fmt.Sprintf("Are your sure you want to detach service account %q from a server %q?", model.ServiceAccMail, serverLabel) | ||
| prompt := fmt.Sprintf("Are you sure you want to detach service account %q from a server %q?", model.ServiceAccMail, serverLabel) | ||
| err = params.Printer.PromptForConfirmation(prompt) | ||
@@ -69,0 +69,0 @@ if err != nil { |
@@ -85,2 +85,3 @@ package generatepayload | ||
| payload = &ske.CreateOrUpdateClusterPayload{ | ||
| Access: resp.Access, | ||
| Extensions: resp.Extensions, | ||
@@ -90,2 +91,3 @@ Hibernation: resp.Hibernation, | ||
| Maintenance: resp.Maintenance, | ||
| Network: resp.Network, | ||
| Nodepools: resp.Nodepools, | ||
@@ -92,0 +94,0 @@ Status: resp.Status, |
@@ -187,5 +187,9 @@ package create | ||
| return p.OutputResult(model.OutputFormat, volume, func() error { | ||
| p.Outputf("Created volume for project %q.\nVolume ID: %s\n", projectLabel, utils.PtrString(volume.Id)) | ||
| operationState := "Created" | ||
| if model.Async { | ||
| operationState = "Triggered creation of" | ||
| } | ||
| p.Outputf("%s volume for project %q.\nVolume ID: %s\n", operationState, projectLabel, utils.PtrString(volume.Id)) | ||
| return nil | ||
| }) | ||
| } |
@@ -88,2 +88,4 @@ package auth | ||
| var listenerErr error | ||
| var ipv6Listener net.Listener | ||
| var ipv6ListenerErr error | ||
| var port int | ||
@@ -98,6 +100,12 @@ startingPort := defaultPort | ||
| port = startingPort + i | ||
| portString := fmt.Sprintf(":%s", strconv.Itoa(port)) | ||
| ipv4addr := fmt.Sprintf("127.0.0.1:%d", port) | ||
| ipv6addr := fmt.Sprintf("[::1]:%d", port) | ||
| p.Debug(print.DebugLevel, "trying to bind port %d for login redirect", port) | ||
| listener, listenerErr = net.Listen("tcp", portString) | ||
| ipv6Listener, ipv6ListenerErr = net.Listen("tcp6", ipv6addr) | ||
| if ipv6ListenerErr != nil { | ||
| continue | ||
| } | ||
| listener, listenerErr = net.Listen("tcp4", ipv4addr) | ||
| if listenerErr == nil { | ||
| _ = ipv6Listener.Close() | ||
| redirectURL = fmt.Sprintf("http://localhost:%d", port) | ||
@@ -109,4 +117,7 @@ p.Debug(print.DebugLevel, "bound port %d for login redirect", port) | ||
| } | ||
| if ipv6ListenerErr != nil { | ||
| return fmt.Errorf("unable to bind port for login redirect, tried from port %d to %d: %w", startingPort, port, ipv6ListenerErr) | ||
| } | ||
| if listenerErr != nil { | ||
| return fmt.Errorf("unable to bind port for login redirect, tried from port %d to %d: %w", defaultPort, port, listenerErr) | ||
| return fmt.Errorf("unable to bind port for login redirect, tried from port %d to %d: %w", startingPort, port, listenerErr) | ||
| } | ||
@@ -113,0 +124,0 @@ |
@@ -9,7 +9,7 @@ package client | ||
| "github.com/spf13/viper" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
| func ConfigureClient(p *print.Printer, cliVersion string) (*objectstorage.APIClient, error) { | ||
| return genericclient.ConfigureClientGeneric(p, cliVersion, viper.GetString(config.ObjectStorageCustomEndpointKey), true, genericclient.CreateApiClient[*objectstorage.APIClient](objectstorage.NewAPIClient)) | ||
| return genericclient.ConfigureClientGeneric(p, cliVersion, viper.GetString(config.ObjectStorageCustomEndpointKey), false, genericclient.CreateApiClient[*objectstorage.APIClient](objectstorage.NewAPIClient)) | ||
| } |
@@ -5,7 +5,5 @@ package utils | ||
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
| "net/http" | ||
| "net/http/httptest" | ||
| "testing" | ||
@@ -15,5 +13,4 @@ | ||
| "github.com/stackitcloud/stackit-cli/internal/pkg/utils" | ||
| "github.com/stackitcloud/stackit-sdk-go/core/config" | ||
| "github.com/stackitcloud/stackit-sdk-go/core/oapierror" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
@@ -24,4 +21,2 @@ | ||
| testCredentialsGroupId = uuid.NewString() | ||
| testCredentialsId = "credentialsID" //nolint:gosec // linter false positive | ||
| testRegion = "eu01" | ||
| ) | ||
@@ -32,33 +27,47 @@ | ||
| testCredentialsName = "testCredential" | ||
| testCredentialsId = "credentialsID" //nolint:gosec // linter false positive | ||
| testRegion = "eu01" | ||
| ) | ||
| type objectStorageClientMocked struct { | ||
| serviceDisabled bool | ||
| getServiceStatusFails bool | ||
| type mockSettings struct { | ||
| serviceDisabled bool | ||
| getServiceStatusFails bool | ||
| listCredentialsGroupsFails bool | ||
| listCredentialsGroupsResp *objectstorage.ListCredentialsGroupsResponse | ||
| listAccessKeysReq objectstorage.ApiListAccessKeysRequest | ||
| } | ||
| func (m *objectStorageClientMocked) GetServiceStatusExecute(_ context.Context, _, _ string) (*objectstorage.ProjectStatus, error) { | ||
| if m.getServiceStatusFails { | ||
| return nil, fmt.Errorf("could not get service status") | ||
| } | ||
| if m.serviceDisabled { | ||
| return nil, &oapierror.GenericOpenAPIError{StatusCode: 404} | ||
| } | ||
| return &objectstorage.ProjectStatus{}, nil | ||
| listAccessKeysFails bool | ||
| listAccessKeysResp *objectstorage.ListAccessKeysResponse | ||
| } | ||
| func (m *objectStorageClientMocked) ListCredentialsGroupsExecute(_ context.Context, _, _ string) (*objectstorage.ListCredentialsGroupsResponse, error) { | ||
| if m.listCredentialsGroupsFails { | ||
| return nil, fmt.Errorf("could not list credentials groups") | ||
| func newAPIMock(settings *mockSettings) objectstorage.DefaultAPI { | ||
| return &objectstorage.DefaultAPIServiceMock{ | ||
| GetServiceStatusExecuteMock: utils.Ptr(func(_ objectstorage.ApiGetServiceStatusRequest) (*objectstorage.ProjectStatus, error) { | ||
| if settings.getServiceStatusFails { | ||
| return nil, fmt.Errorf("could not get service status") | ||
| } | ||
| if settings.serviceDisabled { | ||
| return nil, &oapierror.GenericOpenAPIError{StatusCode: http.StatusNotFound} | ||
| } | ||
| return &objectstorage.ProjectStatus{}, nil | ||
| }), | ||
| ListCredentialsGroupsExecuteMock: utils.Ptr(func(_ objectstorage.ApiListCredentialsGroupsRequest) (*objectstorage.ListCredentialsGroupsResponse, error) { | ||
| if settings.listCredentialsGroupsFails { | ||
| return nil, fmt.Errorf("could not list credentials groups") | ||
| } | ||
| return settings.listCredentialsGroupsResp, nil | ||
| }), | ||
| ListAccessKeysExecuteMock: utils.Ptr(func(_ objectstorage.ApiListAccessKeysRequest) (*objectstorage.ListAccessKeysResponse, error) { | ||
| if settings.listAccessKeysFails { | ||
| return nil, &oapierror.GenericOpenAPIError{StatusCode: http.StatusBadGateway} | ||
| } | ||
| return settings.listAccessKeysResp, nil | ||
| }), | ||
| } | ||
| return m.listCredentialsGroupsResp, nil | ||
| } | ||
| func (m *objectStorageClientMocked) ListAccessKeys(_ context.Context, _, _ string) objectstorage.ApiListAccessKeysRequest { | ||
| return m.listAccessKeysReq | ||
| } | ||
| func TestProjectEnabled(t *testing.T) { | ||
@@ -92,6 +101,6 @@ tests := []struct { | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| client := &objectStorageClientMocked{ | ||
| client := newAPIMock(&mockSettings{ | ||
| serviceDisabled: tt.serviceDisabled, | ||
| getServiceStatusFails: tt.getProjectFails, | ||
| } | ||
| }) | ||
@@ -128,6 +137,6 @@ output, err := ProjectEnabled(context.Background(), client, testProjectId, testRegion) | ||
| listCredentialsGroupsResp: &objectstorage.ListCredentialsGroupsResponse{ | ||
| CredentialsGroups: &[]objectstorage.CredentialsGroup{ | ||
| CredentialsGroups: []objectstorage.CredentialsGroup{ | ||
| { | ||
| CredentialsGroupId: utils.Ptr(testCredentialsGroupId), | ||
| DisplayName: utils.Ptr(testCredentialsGroupName), | ||
| CredentialsGroupId: testCredentialsGroupId, | ||
| DisplayName: testCredentialsGroupName, | ||
| }, | ||
@@ -147,10 +156,10 @@ }, | ||
| listCredentialsGroupsResp: &objectstorage.ListCredentialsGroupsResponse{ | ||
| CredentialsGroups: &[]objectstorage.CredentialsGroup{ | ||
| CredentialsGroups: []objectstorage.CredentialsGroup{ | ||
| { | ||
| CredentialsGroupId: utils.Ptr("test-UUID"), | ||
| DisplayName: utils.Ptr("test-name"), | ||
| CredentialsGroupId: "test-UUID", | ||
| DisplayName: "test-name", | ||
| }, | ||
| { | ||
| CredentialsGroupId: utils.Ptr(testCredentialsGroupId), | ||
| DisplayName: utils.Ptr(testCredentialsGroupName), | ||
| CredentialsGroupId: testCredentialsGroupId, | ||
| DisplayName: testCredentialsGroupName, | ||
| }, | ||
@@ -170,7 +179,7 @@ }, | ||
| { | ||
| description: "nil credentials group id", | ||
| description: "empty credentials group id", | ||
| listCredentialsGroupsResp: &objectstorage.ListCredentialsGroupsResponse{ | ||
| CredentialsGroups: &[]objectstorage.CredentialsGroup{ | ||
| CredentialsGroups: []objectstorage.CredentialsGroup{ | ||
| { | ||
| CredentialsGroupId: nil, | ||
| CredentialsGroupId: "", | ||
| }, | ||
@@ -182,20 +191,8 @@ }, | ||
| { | ||
| description: "nil credentials group name", | ||
| listCredentialsGroupsResp: &objectstorage.ListCredentialsGroupsResponse{ | ||
| CredentialsGroups: &[]objectstorage.CredentialsGroup{ | ||
| { | ||
| CredentialsGroupId: utils.Ptr(testCredentialsGroupId), | ||
| DisplayName: nil, | ||
| }, | ||
| }, | ||
| }, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "empty credentials group name", | ||
| listCredentialsGroupsResp: &objectstorage.ListCredentialsGroupsResponse{ | ||
| CredentialsGroups: &[]objectstorage.CredentialsGroup{ | ||
| CredentialsGroups: []objectstorage.CredentialsGroup{ | ||
| { | ||
| CredentialsGroupId: utils.Ptr(testCredentialsGroupId), | ||
| DisplayName: utils.Ptr(""), | ||
| CredentialsGroupId: testCredentialsGroupId, | ||
| DisplayName: "", | ||
| }, | ||
@@ -210,6 +207,6 @@ }, | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| client := &objectStorageClientMocked{ | ||
| client := newAPIMock(&mockSettings{ | ||
| listCredentialsGroupsFails: tt.listCredentialsGroupsFails, | ||
| listCredentialsGroupsResp: tt.listCredentialsGroupsResp, | ||
| } | ||
| }) | ||
@@ -245,6 +242,6 @@ output, err := GetCredentialsGroupName(context.Background(), client, testProjectId, testCredentialsGroupId, testRegion) | ||
| listAccessKeysResp: &objectstorage.ListAccessKeysResponse{ | ||
| AccessKeys: &[]objectstorage.AccessKey{ | ||
| AccessKeys: []objectstorage.AccessKey{ | ||
| { | ||
| KeyId: utils.Ptr(testCredentialsId), | ||
| DisplayName: utils.Ptr(testCredentialsName), | ||
| KeyId: testCredentialsId, | ||
| DisplayName: testCredentialsName, | ||
| }, | ||
@@ -264,10 +261,10 @@ }, | ||
| listAccessKeysResp: &objectstorage.ListAccessKeysResponse{ | ||
| AccessKeys: &[]objectstorage.AccessKey{ | ||
| AccessKeys: []objectstorage.AccessKey{ | ||
| { | ||
| KeyId: utils.Ptr("test-UUID"), | ||
| DisplayName: utils.Ptr("test-name"), | ||
| KeyId: "test-UUID", | ||
| DisplayName: "test-name", | ||
| }, | ||
| { | ||
| KeyId: utils.Ptr(testCredentialsId), | ||
| DisplayName: utils.Ptr(testCredentialsName), | ||
| KeyId: testCredentialsId, | ||
| DisplayName: testCredentialsName, | ||
| }, | ||
@@ -287,7 +284,7 @@ }, | ||
| { | ||
| description: "nil credentials id", | ||
| description: "empty credentials id", | ||
| listAccessKeysResp: &objectstorage.ListAccessKeysResponse{ | ||
| AccessKeys: &[]objectstorage.AccessKey{ | ||
| AccessKeys: []objectstorage.AccessKey{ | ||
| { | ||
| KeyId: nil, | ||
| KeyId: "", | ||
| }, | ||
@@ -299,20 +296,8 @@ }, | ||
| { | ||
| description: "nil credentials name", | ||
| listAccessKeysResp: &objectstorage.ListAccessKeysResponse{ | ||
| AccessKeys: &[]objectstorage.AccessKey{ | ||
| { | ||
| KeyId: utils.Ptr(testCredentialsId), | ||
| DisplayName: nil, | ||
| }, | ||
| }, | ||
| }, | ||
| isValid: false, | ||
| }, | ||
| { | ||
| description: "empty credentials name", | ||
| listAccessKeysResp: &objectstorage.ListAccessKeysResponse{ | ||
| AccessKeys: &[]objectstorage.AccessKey{ | ||
| AccessKeys: []objectstorage.AccessKey{ | ||
| { | ||
| KeyId: utils.Ptr(testCredentialsId), | ||
| DisplayName: utils.Ptr(""), | ||
| KeyId: testCredentialsId, | ||
| DisplayName: "", | ||
| }, | ||
@@ -327,33 +312,6 @@ }, | ||
| t.Run(tt.description, func(t *testing.T) { | ||
| mockedRespBytes, err := json.Marshal(tt.listAccessKeysResp) | ||
| if err != nil { | ||
| t.Fatalf("Failed to marshal mocked response: %v", err) | ||
| } | ||
| handler := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { | ||
| w.Header().Set("Content-Type", "application/json") | ||
| if tt.getCredentialsNameFails { | ||
| w.WriteHeader(http.StatusBadGateway) | ||
| w.Header().Set("Content-Type", "application/json") | ||
| _, err := w.Write([]byte("{\"message\": \"Something bad happened\"")) | ||
| if err != nil { | ||
| t.Errorf("Failed to write bad response: %v", err) | ||
| } | ||
| return | ||
| } | ||
| _, err := w.Write(mockedRespBytes) | ||
| if err != nil { | ||
| t.Errorf("Failed to write response: %v", err) | ||
| } | ||
| client := newAPIMock(&mockSettings{ | ||
| listAccessKeysFails: tt.getCredentialsNameFails, | ||
| listAccessKeysResp: tt.listAccessKeysResp, | ||
| }) | ||
| mockedServer := httptest.NewServer(handler) | ||
| defer mockedServer.Close() | ||
| client, err := objectstorage.NewAPIClient( | ||
| config.WithEndpoint(mockedServer.URL), | ||
| config.WithoutAuthentication(), | ||
| ) | ||
| if err != nil { | ||
| t.Fatalf("Failed to initialize client: %v", err) | ||
| } | ||
@@ -360,0 +318,0 @@ output, err := GetCredentialsName(context.Background(), client, testProjectId, testCredentialsGroupId, testCredentialsId, testRegion) |
@@ -9,13 +9,7 @@ package utils | ||
| "github.com/stackitcloud/stackit-sdk-go/core/oapierror" | ||
| "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" | ||
| objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" | ||
| ) | ||
| type ObjectStorageClient interface { | ||
| GetServiceStatusExecute(ctx context.Context, projectId, region string) (*objectstorage.ProjectStatus, error) | ||
| ListCredentialsGroupsExecute(ctx context.Context, projectId, region string) (*objectstorage.ListCredentialsGroupsResponse, error) | ||
| ListAccessKeys(ctx context.Context, projectId, region string) objectstorage.ApiListAccessKeysRequest | ||
| } | ||
| func ProjectEnabled(ctx context.Context, apiClient ObjectStorageClient, projectId, region string) (bool, error) { | ||
| _, err := apiClient.GetServiceStatusExecute(ctx, projectId, region) | ||
| func ProjectEnabled(ctx context.Context, apiClient objectstorage.DefaultAPI, projectId, region string) (bool, error) { | ||
| _, err := apiClient.GetServiceStatus(ctx, projectId, region).Execute() | ||
| if err != nil { | ||
@@ -34,4 +28,4 @@ oapiErr, ok := err.(*oapierror.GenericOpenAPIError) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped | ||
| func GetCredentialsGroupName(ctx context.Context, apiClient ObjectStorageClient, projectId, credentialsGroupId, region string) (string, error) { | ||
| resp, err := apiClient.ListCredentialsGroupsExecute(ctx, projectId, region) | ||
| func GetCredentialsGroupName(ctx context.Context, apiClient objectstorage.DefaultAPI, projectId, credentialsGroupId, region string) (string, error) { | ||
| resp, err := apiClient.ListCredentialsGroups(ctx, projectId, region).Execute() | ||
| if err != nil { | ||
@@ -46,5 +40,5 @@ return "", fmt.Errorf("list Object Storage credentials groups: %w", err) | ||
| for _, group := range *credentialsGroups { | ||
| if group.CredentialsGroupId != nil && *group.CredentialsGroupId == credentialsGroupId && group.DisplayName != nil && *group.DisplayName != "" { | ||
| return *group.DisplayName, nil | ||
| for _, group := range credentialsGroups { | ||
| if group.CredentialsGroupId == credentialsGroupId && group.DisplayName != "" { | ||
| return group.DisplayName, nil | ||
| } | ||
@@ -56,3 +50,3 @@ } | ||
| func GetCredentialsName(ctx context.Context, apiClient ObjectStorageClient, projectId, credentialsGroupId, keyId, region string) (string, error) { | ||
| func GetCredentialsName(ctx context.Context, apiClient objectstorage.DefaultAPI, projectId, credentialsGroupId, keyId, region string) (string, error) { | ||
| req := apiClient.ListAccessKeys(ctx, projectId, region) | ||
@@ -71,5 +65,5 @@ req = req.CredentialsGroup(credentialsGroupId) | ||
| for _, credential := range *credentials { | ||
| if credential.KeyId != nil && *credential.KeyId == keyId && credential.DisplayName != nil && *credential.DisplayName != "" { | ||
| return *credential.DisplayName, nil | ||
| for _, credential := range credentials { | ||
| if credential.KeyId == keyId && credential.DisplayName != "" { | ||
| return credential.DisplayName, nil | ||
| } | ||
@@ -76,0 +70,0 @@ } |
Sorry, the diff of this file is too big to display