Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ Versioning follows [SemVer](https://semver.org/). Sections: **Added**, **Changed
- `server create --confidential` creates a Confidential VM from a confidential boot image. Requires `--type ENTERPRISE` and `--image-id` (a private, SEV-SNP image; `--image-id` tab-completion is filtered to these). A boot volume is built from the image and attached in the same request (size it with `--size`/`--storage-type`), so the API can derive `cores` and `cpuFamily` from the image's `launch-config.json` — `--cores` and `--cpu-family` must not be set.
- `RequiredFeatures` and `EnabledFeatures` columns.

### Changed
- Consistent `delete --all` across all resources: Bulk deletion now prints a preview of every resource that will be deleted (with identifying details such as name, ID, public IP, location, and description), confirms per item, reports per-item success, and ends with a `deleted / skipped / failed` summary instead of staying silent unless an error occurred. On regional APIs the preview and deletion span all locations by default (use `--location` to target one). This also fixes a bug where when deleting certain resources and answering 'no' for only one of them, all the remaining resources would be skipped.

### Fixed
- `dbaas mariadb cluster delete --all --name` and `dbaas mongo cluster delete --all --name` now filter by the given name. The `--name` flag was previously registered as a boolean and could not accept a value, so the filter never applied.
- `vpn wireguard peer delete` no longer reports success when the deletion fails. The single-peer path was discarding the API error and always exiting 0.

## [v6.10.2] - June 2026

### Added
Expand Down Expand Up @@ -48,7 +55,6 @@ Versioning follows [SemVer](https://semver.org/). Sections: **Added**, **Changed
- Colored syntax highlighting
- Prevent 'ionosctl shell' from being called inside another 'ionosctl shell' instance.


## [v6.9.9] – April 2026

### Changed
Expand Down
25 changes: 17 additions & 8 deletions commands/cdn/distribution/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"github.com/ionos-cloud/ionosctl/v6/internal/client"
"github.com/ionos-cloud/ionosctl/v6/internal/constants"
"github.com/ionos-cloud/ionosctl/v6/pkg/confirm"
"github.com/ionos-cloud/ionosctl/v6/pkg/functional"
"github.com/ionos-cloud/sdk-go-bundle/products/cdn/v2"
"github.com/spf13/viper"

Expand Down Expand Up @@ -58,13 +57,23 @@ func Delete() *core.Command {
}

func deleteAll(c *core.CommandConfig) error {
records, err := completer.Distributions()
if err != nil {
return fmt.Errorf("failed getting all distributions: %w", err)
}

return functional.ApplyAndAggregateErrors(records.GetItems(), func(d cdn.Distribution) error {
return deleteSingle(c, d.Id)
return core.DeleteAll(c, core.DeleteAllOptions[cdn.Distribution]{
Resource: "distribution",
List: func() ([]cdn.Distribution, error) {
records, err := completer.Distributions()
if err != nil {
return nil, fmt.Errorf("failed getting all distributions: %w", err)
}
return records.GetItems(), nil
},
Summary: func(d cdn.Distribution) string {
return fmt.Sprintf("%s (domain: %s)", d.Id, d.Properties.Domain)
},
ID: func(d cdn.Distribution) string { return d.Id },
Delete: func(d cdn.Distribution) error {
_, err := client.Must().CDNClient.DistributionsApi.DistributionsDelete(context.Background(), d.Id).Execute()
return err
},
})
}

Expand Down
35 changes: 18 additions & 17 deletions commands/cert/autocertificate/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"github.com/ionos-cloud/ionosctl/v6/internal/constants"
"github.com/ionos-cloud/ionosctl/v6/internal/core"
"github.com/ionos-cloud/ionosctl/v6/pkg/confirm"
"github.com/ionos-cloud/ionosctl/v6/pkg/functional"
"github.com/ionos-cloud/sdk-go-bundle/products/cert/v2"
"github.com/spf13/viper"
)
Expand Down Expand Up @@ -65,22 +64,24 @@ func AutocertificateDeleteCmd() *core.Command {
}

func deleteAll(c *core.CommandConfig) error {
c.Verbose("Deleting all AutoCertificates!")
xs, _, err := client.Must().CertManagerClient.AutoCertificateApi.AutoCertificatesGet(context.Background()).Execute()
if err != nil {
return fmt.Errorf("failed getting the AutoCertificates: %w", err)
}
err = functional.ApplyAndAggregateErrors(xs.GetItems(), func(z cert.AutoCertificateRead) error {
yes := confirm.FAsk(c.Command.Command.InOrStdin(), fmt.Sprintf("Are you sure you want to delete AutoCertificate with name: %s, id: %s ", z.Properties.Name, z.Id),
viper.GetBool(constants.ArgForce))
if yes {
_, delErr := client.Must().CertManagerClient.AutoCertificateApi.AutoCertificatesDelete(context.Background(), z.Id).Execute()
if delErr != nil {
return fmt.Errorf("failed deleting %s (name: %s): %w", z.Id, z.Properties.Name, delErr)
return core.DeleteAll(c, core.DeleteAllOptions[cert.AutoCertificateRead]{
Resource: "AutoCertificate",
List: func() ([]cert.AutoCertificateRead, error) {
xs, _, err := client.Must().CertManagerClient.AutoCertificateApi.AutoCertificatesGet(context.Background()).Execute()
if err != nil {
return nil, fmt.Errorf("failed getting the AutoCertificates: %w", err)
}
}
return nil
return xs.GetItems(), nil
},
Summary: func(z cert.AutoCertificateRead) string {
return fmt.Sprintf("name: %s, id: %s", z.Properties.Name, z.Id)
},
ID: func(z cert.AutoCertificateRead) string {
return z.Id
},
Delete: func(z cert.AutoCertificateRead) error {
_, err := client.Must().CertManagerClient.AutoCertificateApi.AutoCertificatesDelete(context.Background(), z.Id).Execute()
return err
},
})

return err
}
40 changes: 20 additions & 20 deletions commands/cert/certificate/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/ionos-cloud/ionosctl/v6/internal/constants"
"github.com/ionos-cloud/ionosctl/v6/internal/core"
"github.com/ionos-cloud/ionosctl/v6/pkg/confirm"
"github.com/ionos-cloud/sdk-go-bundle/products/cert/v2"
"github.com/spf13/viper"
)

Expand Down Expand Up @@ -40,32 +41,32 @@ func CertDeleteCmd() *core.Command {
}

func CmdDelete(c *core.CommandConfig) error {
var err error

allFlag, err := c.Command.Command.Flags().GetBool(constants.ArgAll)
if err != nil {
return err
}

if allFlag {
c.Verbose("Deleting all Certificates...")

certs, _, err := client.Must().CertManagerClient.CertificateApi.CertificatesGet(context.Background()).Execute()
if err != nil {
return err
}

for _, cert := range certs.Items {
msg := fmt.Sprintf("delete Certificate ID: %s", cert.Id)
if !confirm.FAsk(c.Command.Command.InOrStdin(), msg, viper.GetBool(constants.ArgForce)) {
return fmt.Errorf(confirm.UserDenied)
}

_, err = client.Must().CertManagerClient.CertificateApi.CertificatesDelete(context.Background(), cert.Id).Execute()
if err != nil {
return core.DeleteAll(c, core.DeleteAllOptions[cert.CertificateRead]{
Resource: "Certificate",
List: func() ([]cert.CertificateRead, error) {
certs, _, err := client.Must().CertManagerClient.CertificateApi.CertificatesGet(context.Background()).Execute()
if err != nil {
return nil, err
}
return certs.Items, nil
},
Summary: func(z cert.CertificateRead) string {
return fmt.Sprintf("name: %s, id: %s", z.Properties.Name, z.Id)
},
ID: func(z cert.CertificateRead) string {
return z.Id
},
Delete: func(z cert.CertificateRead) error {
_, err := client.Must().CertManagerClient.CertificateApi.CertificatesDelete(context.Background(), z.Id).Execute()
return err
}
}
},
})
} else {
id, err := c.Command.Command.Flags().GetString(constants.FlagCertId)
if err != nil {
Expand All @@ -81,7 +82,6 @@ func CmdDelete(c *core.CommandConfig) error {

return err
}
return err
}

func PreCmdDelete(c *core.PreCommandConfig) error {
Expand Down
37 changes: 19 additions & 18 deletions commands/cert/provider/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"github.com/ionos-cloud/ionosctl/v6/internal/constants"
"github.com/ionos-cloud/ionosctl/v6/internal/core"
"github.com/ionos-cloud/ionosctl/v6/pkg/confirm"
"github.com/ionos-cloud/ionosctl/v6/pkg/functional"
"github.com/ionos-cloud/sdk-go-bundle/products/cert/v2"
"github.com/spf13/viper"
)
Expand Down Expand Up @@ -56,7 +55,7 @@ func ProviderDeleteCmd() *core.Command {
}, constants.CertApiRegionalURL, constants.CertLocations),
)

cmd.AddBoolFlag(constants.ArgAll, constants.ArgAllShort, false, fmt.Sprintf("Delete all Providers. Required or -%s", constants.FlagGatewayShort))
cmd.AddBoolFlag(constants.ArgAll, constants.ArgAllShort, false, fmt.Sprintf("Delete all Providers. Required or --%s", constants.FlagProviderID))

cmd.Command.SilenceUsage = true
cmd.Command.Flags().SortFlags = false
Expand All @@ -65,22 +64,24 @@ func ProviderDeleteCmd() *core.Command {
}

func deleteAll(c *core.CommandConfig) error {
c.Verbose("Deleting all providers!")
xs, _, err := client.Must().CertManagerClient.ProviderApi.ProvidersGet(context.Background()).Execute()
if err != nil {
return fmt.Errorf("failed getting the Providers: %w", err)
}
err = functional.ApplyAndAggregateErrors(xs.GetItems(), func(z cert.ProviderRead) error {
yes := confirm.FAsk(c.Command.Command.InOrStdin(), fmt.Sprintf("Are you sure you want to delete Provider with name: %s, id: %s ", z.Properties.Name, z.Id),
viper.GetBool(constants.ArgForce))
if yes {
_, delErr := client.Must().CertManagerClient.ProviderApi.ProvidersDelete(context.Background(), z.Id).Execute()
if delErr != nil {
return fmt.Errorf("failed deleting %s (name: %s): %w", z.Id, z.Properties.Name, delErr)
return core.DeleteAll(c, core.DeleteAllOptions[cert.ProviderRead]{
Resource: "Provider",
List: func() ([]cert.ProviderRead, error) {
xs, _, err := client.Must().CertManagerClient.ProviderApi.ProvidersGet(context.Background()).Execute()
if err != nil {
return nil, fmt.Errorf("failed getting the Providers: %w", err)
}
}
return nil
return xs.GetItems(), nil
},
Summary: func(z cert.ProviderRead) string {
return fmt.Sprintf("name: %s, id: %s", z.Properties.Name, z.Id)
},
ID: func(z cert.ProviderRead) string {
return z.Id
},
Delete: func(z cert.ProviderRead) error {
_, err := client.Must().CertManagerClient.ProviderApi.ProvidersDelete(context.Background(), z.Id).Execute()
return err
},
})

return err
}
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,10 @@ func TestRunApplicationLoadBalancerDeleteAllErr(t *testing.T) {
})
}

func TestRunApplicationLoadBalancerDeleteAllAskForConfirmErr(t *testing.T) {
// Denying confirmation on an item during --all skips ONLY that item and never
// aborts the operation, so with no items confirmed nothing is deleted and no
// error is returned (no Delete mock is expected).
func TestRunApplicationLoadBalancerDeleteAllDenySkips(t *testing.T) {
var b bytes.Buffer
w := bufio.NewWriter(&b)
core.CmdConfigTest(t, w, func(cfg *core.CommandConfig, rm *core.ResourcesMocksTest) {
Expand All @@ -408,7 +411,7 @@ func TestRunApplicationLoadBalancerDeleteAllAskForConfirmErr(t *testing.T) {
rm.CloudApiV6Mocks.ApplicationLoadBalancer.EXPECT().List(testApplicationLoadBalancerVar).Return(applicationloadbalancers, &testutil.TestResponse, nil)
cfg.Command.Command.SetIn(bytes.NewReader([]byte("\n")))
err := RunApplicationLoadBalancerDelete(cfg)
assert.Error(t, err)
assert.NoError(t, err)
})
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,54 +178,52 @@
return nil
}

func DeleteAllApplicationLoadBalancerFlowLog(c *core.CommandConfig) error {

Check failure on line 181 in commands/compute/applicationloadbalancer/flowlog/run_applicationloadbalancer_flowlog.go

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 20 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=cli-ionosctl&issues=AZ70HucsyPBcQCDpigyB&open=AZ70HucsyPBcQCDpigyB&pullRequest=678
c.Msg("Getting Application Load Balancer FlowLogs...")

applicationLoadBalancerFlowlogs, resp, err := c.CloudApiV6Services.ApplicationLoadBalancers().ListFlowLogs(
viper.GetString(core.GetFlagName(c.NS, cloudapiv6.ArgDataCenterId)),
viper.GetString(core.GetFlagName(c.NS, cloudapiv6.ArgApplicationLoadBalancerId)),
)
if err != nil {
return err
}

albFlowLogItems, ok := applicationLoadBalancerFlowlogs.GetItemsOk()
if !ok || albFlowLogItems == nil {
return errors.New("could not get items of Application Load Balancer Flow Logs")
}

if len(*albFlowLogItems) <= 0 {
return errors.New("no Application Load Balancer Flow Logs found")
}

var multiErr error
for _, fl := range *albFlowLogItems {
id := fl.GetId()
name := fl.GetProperties().Name

if !confirm.FAsk(c.Command.Command.InOrStdin(), fmt.Sprintf("Delete Application Load Balancer FlowLog Id: %s , Name: %s", *id, *name), viper.GetBool(constants.ArgForce)) {
return fmt.Errorf(confirm.UserDenied)
}

resp, err = c.CloudApiV6Services.ApplicationLoadBalancers().DeleteFlowLog(
viper.GetString(core.GetFlagName(c.NS, cloudapiv6.ArgDataCenterId)),
viper.GetString(core.GetFlagName(c.NS, cloudapiv6.ArgApplicationLoadBalancerId)), *id,
)
if resp != nil && request.GetId(resp) != "" {
c.Verbose(constants.MessageRequestInfo, request.GetId(resp), resp.RequestTime)
}
if err != nil {
multiErr = errors.Join(multiErr, fmt.Errorf(constants.ErrDeleteAll, c.Resource, *id, err))
continue
}

}

if multiErr != nil {
return multiErr
}

return nil
dcId := viper.GetString(core.GetFlagName(c.NS, cloudapiv6.ArgDataCenterId))
albId := viper.GetString(core.GetFlagName(c.NS, cloudapiv6.ArgApplicationLoadBalancerId))

c.Verbose(constants.DatacenterId, dcId)
c.Verbose(constants.ApplicationLoadBalancerId, albId)

return core.DeleteAll(c, core.DeleteAllOptions[ionoscloud.FlowLog]{
Resource: "Application Load Balancer FlowLog",
List: func() ([]ionoscloud.FlowLog, error) {
applicationLoadBalancerFlowlogs, _, err := c.CloudApiV6Services.ApplicationLoadBalancers().ListFlowLogs(dcId, albId)
if err != nil {
return nil, err
}
items, ok := applicationLoadBalancerFlowlogs.GetItemsOk()
if !ok || items == nil {
return nil, errors.New("could not get items of Application Load Balancer Flow Logs")
}
return *items, nil
},
Summary: func(fl ionoscloud.FlowLog) string {
summary := ""
if props, ok := fl.GetPropertiesOk(); ok && props != nil {
if name, ok := props.GetNameOk(); ok && name != nil {
summary += *name
}
}
if id, ok := fl.GetIdOk(); ok && id != nil {
summary += fmt.Sprintf(" (id: %s)", *id)
}
return summary
},
ID: func(fl ionoscloud.FlowLog) string {
if id := fl.GetId(); id != nil {
return *id
}
return ""
},
Delete: func(fl ionoscloud.FlowLog) error {
resp, err := c.CloudApiV6Services.ApplicationLoadBalancers().DeleteFlowLog(dcId, albId, *fl.GetId())
if resp != nil && request.GetId(resp) != "" {
c.Verbose(constants.MessageRequestInfo, request.GetId(resp), resp.RequestTime)
}
return err
},
})
}

func PreRunDcApplicationLoadBalancerIds(c *core.PreCommandConfig) error {
Expand Down
Loading
Loading