From 5a68770c94000ea52b664466d1e3f2c074285343 Mon Sep 17 00:00:00 2001 From: Thiago Dantas Date: Mon, 6 Oct 2025 20:02:54 +0100 Subject: [PATCH 1/2] exo cli with application-consistent-snapshot-enabled flag --- cmd/compute/instance/instance_create.go | 41 +++++----- cmd/compute/instance/instance_show.go | 49 ++++++------ .../instance/instance_snapshot_show.go | 30 +++---- cmd/compute/instance/instance_update.go | 20 +++-- .../instance_template_register.go | 78 ++++++++++--------- .../instance_template_show.go | 67 ++++++++-------- 6 files changed, 155 insertions(+), 130 deletions(-) diff --git a/cmd/compute/instance/instance_create.go b/cmd/compute/instance/instance_create.go index 55970262f..b186a4b6b 100644 --- a/cmd/compute/instance/instance_create.go +++ b/cmd/compute/instance/instance_create.go @@ -32,23 +32,24 @@ type instanceCreateCmd struct { Name string `cli-arg:"#" cli-usage:"NAME"` - AntiAffinityGroups []string `cli-flag:"anti-affinity-group" cli-usage:"instance Anti-Affinity Group NAME|ID (can be specified multiple times)"` - CloudInitFile string `cli-flag:"cloud-init" cli-usage:"instance cloud-init user data configuration file path"` - CloudInitCompress bool `cli-flag:"cloud-init-compress" cli-usage:"compress instance cloud-init user data"` - DeployTarget string `cli-usage:"instance Deploy Target NAME|ID"` - DiskSize int64 `cli-usage:"instance disk size"` - TPM bool `cli-flag:"tpm" cli-usage:"enable TPM on instance"` - SecureBoot bool `cli-flag:"secureboot" cli-usage:"enable Secure boot on instance"` - InstanceType string `cli-usage:"instance type (format: [FAMILY.]SIZE)"` - Labels map[string]string `cli-flag:"label" cli-usage:"instance label (format: key=value)"` - PrivateNetworks []string `cli-flag:"private-network" cli-usage:"instance Private Network NAME|ID (can be specified multiple times)"` - PublicIPAssignment string `cli-flag:"public-ip" cli-usage:"Configures public IP assignment of the Instances (none|inet4|dual). (default: inet4)"` - SSHKeys []string `cli-flag:"ssh-key" cli-usage:"SSH key to deploy on the instance (can be specified multiple times)"` - Protection bool `cli-flag:"protection" cli-usage:"enable delete protection"` - SecurityGroups []string `cli-flag:"security-group" cli-usage:"instance Security Group NAME|ID (can be specified multiple times)"` - Template string `cli-usage:"instance template NAME|ID"` - TemplateVisibility string `cli-usage:"instance template visibility (public|private)"` - Zone v3.ZoneName `cli-short:"z" cli-usage:"instance zone"` + AppConsistentSnapshot bool `cli-flag:"application-consistent-snapshot-enabled" cli-usage:"enable application-consistent snapshots when supported; false disables; omit for template default"` + AntiAffinityGroups []string `cli-flag:"anti-affinity-group" cli-usage:"instance Anti-Affinity Group NAME|ID (can be specified multiple times)"` + CloudInitFile string `cli-flag:"cloud-init" cli-usage:"instance cloud-init user data configuration file path"` + CloudInitCompress bool `cli-flag:"cloud-init-compress" cli-usage:"compress instance cloud-init user data"` + DeployTarget string `cli-usage:"instance Deploy Target NAME|ID"` + DiskSize int64 `cli-usage:"instance disk size"` + TPM bool `cli-flag:"tpm" cli-usage:"enable TPM on instance"` + SecureBoot bool `cli-flag:"secureboot" cli-usage:"enable Secure boot on instance"` + InstanceType string `cli-usage:"instance type (format: [FAMILY.]SIZE)"` + Labels map[string]string `cli-flag:"label" cli-usage:"instance label (format: key=value)"` + PrivateNetworks []string `cli-flag:"private-network" cli-usage:"instance Private Network NAME|ID (can be specified multiple times)"` + PublicIPAssignment string `cli-flag:"public-ip" cli-usage:"Configures public IP assignment of the Instances (none|inet4|dual). (default: inet4)"` + SSHKeys []string `cli-flag:"ssh-key" cli-usage:"SSH key to deploy on the instance (can be specified multiple times)"` + Protection bool `cli-flag:"protection" cli-usage:"enable delete protection"` + SecurityGroups []string `cli-flag:"security-group" cli-usage:"instance Security Group NAME|ID (can be specified multiple times)"` + Template string `cli-usage:"instance template NAME|ID"` + TemplateVisibility string `cli-usage:"instance template visibility (public|private)"` + Zone v3.ZoneName `cli-short:"z" cli-usage:"instance zone"` } func (c *instanceCreateCmd) CmdAliases() []string { return exocmd.GCreateAlias } @@ -74,7 +75,7 @@ func (c *instanceCreateCmd) CmdPreRun(cmd *cobra.Command, args []string) error { return exocmd.CliCommandDefaultPreRun(c, cmd, args) } -func (c *instanceCreateCmd) CmdRun(_ *cobra.Command, _ []string) error { //nolint:gocyclo +func (c *instanceCreateCmd) CmdRun(cmd *cobra.Command, _ []string) error { //nolint:gocyclo var ( singleUseSSHPrivateKey *rsa.PrivateKey singleUseSSHPublicKey ssh.PublicKey @@ -247,6 +248,10 @@ func (c *instanceCreateCmd) CmdRun(_ *cobra.Command, _ []string) error { //nolin instanceReq.UserData = userData } + if cmd.Flags().Changed(exocmd.MustCLICommandFlagName(c, &c.AppConsistentSnapshot)) { + instanceReq.ApplicationConsistentSnapshotEnabled = &c.AppConsistentSnapshot + } + var instanceID v3.UUID utils.DecorateAsyncOperation(fmt.Sprintf("Creating instance %q...", c.Name), func() { var op *v3.Operation diff --git a/cmd/compute/instance/instance_show.go b/cmd/compute/instance/instance_show.go index 1581e3172..c82bc20ad 100644 --- a/cmd/compute/instance/instance_show.go +++ b/cmd/compute/instance/instance_show.go @@ -18,28 +18,29 @@ import ( ) type InstanceShowOutput struct { - ID v3.UUID `json:"id"` - Name string `json:"name"` - CreationDate string `json:"creation_date"` - InstanceType string `json:"instance_type"` - Template string `json:"template"` - Zone v3.ZoneName `json:"zone"` - AntiAffinityGroups []string `json:"anti_affinity_groups" outputLabel:"Anti-Affinity Groups"` - DeployTarget string `json:"deploy_target"` - SecurityGroups []string `json:"security_groups"` - PrivateInstance string `json:"private-instance" outputLabel:"Private Instance"` - PrivateNetworks []string `json:"private_networks"` - ElasticIPs []string `json:"elastic_ips" outputLabel:"Elastic IPs"` - PublicIPAssignment v3.PublicIPAssignment `json:"public-ip" outputLabel:"Public IP"` - IPAddress string `json:"ip_address"` - IPv6Address string `json:"ipv6_address" outputLabel:"IPv6 Address"` - SSHKey string `json:"ssh_key"` - DiskSize string `json:"disk_size"` - State v3.InstanceState `json:"state"` - Labels map[string]string `json:"labels"` - SecureBoot bool `json:"secureboot"` - Tpm bool `json:"tpm"` - ReverseDNS v3.DomainName `json:"reverse_dns" outputLabel:"Reverse DNS"` + ID v3.UUID `json:"id"` + Name string `json:"name"` + CreationDate string `json:"creation_date"` + InstanceType string `json:"instance_type"` + Template string `json:"template"` + Zone v3.ZoneName `json:"zone"` + AntiAffinityGroups []string `json:"anti_affinity_groups" outputLabel:"Anti-Affinity Groups"` + DeployTarget string `json:"deploy_target"` + SecurityGroups []string `json:"security_groups"` + PrivateInstance string `json:"private-instance" outputLabel:"Private Instance"` + PrivateNetworks []string `json:"private_networks"` + ElasticIPs []string `json:"elastic_ips" outputLabel:"Elastic IPs"` + PublicIPAssignment v3.PublicIPAssignment `json:"public-ip" outputLabel:"Public IP"` + IPAddress string `json:"ip_address"` + IPv6Address string `json:"ipv6_address" outputLabel:"IPv6 Address"` + SSHKey string `json:"ssh_key"` + DiskSize string `json:"disk_size"` + State v3.InstanceState `json:"state"` + Labels map[string]string `json:"labels"` + SecureBoot bool `json:"secureboot"` + Tpm bool `json:"tpm"` + ReverseDNS v3.DomainName `json:"reverse_dns" outputLabel:"Reverse DNS"` + AppConsistentSnapshot bool `json:"application_consistent_snapshot_enabled" outputLabel:"Application-Consistent Snapshot enabled"` } func (o *InstanceShowOutput) Type() string { return "Compute instance" } @@ -144,6 +145,10 @@ func (c *instanceShowCmd) CmdRun(cmd *cobra.Command, _ []string) error { Zone: c.Zone, } + if instance.ApplicationConsistentSnapshotEnabled != nil { + out.AppConsistentSnapshot = *instance.ApplicationConsistentSnapshotEnabled + } + out.PrivateInstance = "No" if instance.PublicIPAssignment == "none" { out.PrivateInstance = "Yes" diff --git a/cmd/compute/instance/instance_snapshot_show.go b/cmd/compute/instance/instance_snapshot_show.go index c4358d680..983eb418c 100644 --- a/cmd/compute/instance/instance_snapshot_show.go +++ b/cmd/compute/instance/instance_snapshot_show.go @@ -13,13 +13,14 @@ import ( ) type instanceSnapshotShowOutput struct { - ID string `json:"id"` - Name string `json:"name"` - CreationDate string `json:"creation_date"` - State string `json:"state"` - Size int64 `json:"size" outputLabel:"Size (GB)"` - Instance string `json:"instance"` - Zone string `json:"zone"` + ID string `json:"id"` + Name string `json:"name"` + CreationDate string `json:"creation_date"` + State string `json:"state"` + Size int64 `json:"size" outputLabel:"Size (GB)"` + Instance string `json:"instance"` + Zone string `json:"zone"` + AppConsistentSnapshot bool `json:"application_consistent_snapshot" outputLabel:"Application Consistent Snapshot"` } func (o *instanceSnapshotShowOutput) Type() string { return "Snapshot" } @@ -76,13 +77,14 @@ func (c *instanceSnapshotShowCmd) CmdRun(_ *cobra.Command, _ []string) error { } return c.OutputFunc(&instanceSnapshotShowOutput{ - ID: snapshot.ID.String(), - Name: snapshot.Name, - CreationDate: snapshot.CreatedAT.String(), - State: string(snapshot.State), - Size: snapshot.Size, - Instance: instance.Name, - Zone: c.Zone, + ID: snapshot.ID.String(), + Name: snapshot.Name, + CreationDate: snapshot.CreatedAT.String(), + State: string(snapshot.State), + Size: snapshot.Size, + Instance: instance.Name, + Zone: c.Zone, + AppConsistentSnapshot: *snapshot.ApplicationConsistent, }, nil) } diff --git a/cmd/compute/instance/instance_update.go b/cmd/compute/instance/instance_update.go index 6383f14ce..c02db9552 100644 --- a/cmd/compute/instance/instance_update.go +++ b/cmd/compute/instance/instance_update.go @@ -21,13 +21,14 @@ type instanceUpdateCmd struct { Instance string `cli-arg:"#" cli-usage:"NAME|ID"` - CloudInitFile string `cli-flag:"cloud-init" cli-short:"c" cli-usage:"instance cloud-init user data configuration file path"` - CloudInitCompress bool `cli-flag:"cloud-init-compress" cli-usage:"compress instance cloud-init user data"` - Labels map[string]string `cli-flag:"label" cli-usage:"instance label (format: key=value)"` - Name string `cli-short:"n" cli-usage:"instance name"` - Protection bool `cli-flag:"protection" cli-usage:"delete protection; set --protection=false to disable instance protection"` - Zone string `cli-short:"z" cli-usage:"instance zone"` - ReverseDNS string `cli-usage:"Reverse DNS Domain"` + AppConsistentSnapshot bool `cli-flag:"application-consistent-snapshot-enabled" cli-usage:"update instance application-consistent snapshots"` + CloudInitFile string `cli-flag:"cloud-init" cli-short:"c" cli-usage:"instance cloud-init user data configuration file path"` + CloudInitCompress bool `cli-flag:"cloud-init-compress" cli-usage:"compress instance cloud-init user data"` + Labels map[string]string `cli-flag:"label" cli-usage:"instance label (format: key=value)"` + Name string `cli-short:"n" cli-usage:"instance name"` + Protection bool `cli-flag:"protection" cli-usage:"delete protection; set --protection=false to disable instance protection"` + Zone string `cli-short:"z" cli-usage:"instance zone"` + ReverseDNS string `cli-usage:"Reverse DNS Domain"` } func (c *instanceUpdateCmd) CmdAliases() []string { return nil } @@ -95,6 +96,11 @@ func (c *instanceUpdateCmd) CmdRun(cmd *cobra.Command, _ []string) error { updatedRDNS = true } + if cmd.Flags().Changed(exocmd.MustCLICommandFlagName(c, &c.AppConsistentSnapshot)) { + updateRequest.ApplicationConsistentSnapshotEnabled = &c.AppConsistentSnapshot + updatedInstance = true + } + if updatedInstance || updatedRDNS || cmd.Flags().Changed(exocmd.MustCLICommandFlagName(c, &c.Protection)) { if updatedInstance { diff --git a/cmd/compute/instance_template/instance_template_register.go b/cmd/compute/instance_template/instance_template_register.go index 32a4c5116..9714dcfa6 100644 --- a/cmd/compute/instance_template/instance_template_register.go +++ b/cmd/compute/instance_template/instance_template_register.go @@ -22,17 +22,18 @@ type instanceTemplateRegisterCmd struct { URL string `cli-arg:"#"` Checksum string `cli-arg:"#"` - BootMode string `cli-usage:"template boot mode (legacy|uefi)"` - Description string `cli-usage:"template description"` - Build string `cli-usage:"template build"` - Version string `cli-usage:"template version"` - Maintainer string `cli-usage:"template maintainer"` - DisablePassword bool `cli-usage:"disable password-based authentication"` - DisableSSHKey bool `cli-flag:"disable-ssh-key" cli-usage:"disable SSH key-based authentication"` - FromSnapshot string `cli-usage:"ID of a Compute instance snapshot to register as template"` - Timeout int64 `cli-usage:"registration timeout duration in seconds"` - Username string `cli-usage:"template default username"` - Zone string `cli-short:"z" cli-usage:"zone to register the template into (default: current account's default zone)"` + AppConsistentSnapshotEnabled bool `cli-flag:"application-consistent-snapshot-enabled" cli-usage:"template with support for application-consistent-snapshots"` + BootMode string `cli-usage:"template boot mode (legacy|uefi)"` + Description string `cli-usage:"template description"` + Build string `cli-usage:"template build"` + Version string `cli-usage:"template version"` + Maintainer string `cli-usage:"template maintainer"` + DisablePassword bool `cli-usage:"disable password-based authentication"` + DisableSSHKey bool `cli-flag:"disable-ssh-key" cli-usage:"disable SSH key-based authentication"` + FromSnapshot string `cli-usage:"ID of a Compute instance snapshot to register as template"` + Timeout int64 `cli-usage:"registration timeout duration in seconds"` + Username string `cli-usage:"template default username"` + Zone string `cli-short:"z" cli-usage:"zone to register the template into (default: current account's default zone)"` } func (c *instanceTemplateRegisterCmd) CmdAliases() []string { return exocmd.GCreateAlias } @@ -84,16 +85,17 @@ func (c *instanceTemplateRegisterCmd) CmdRun(cmd *cobra.Command, _ []string) err sshKeyEnabled := !c.DisableSSHKey templateRequest = v3.RegisterTemplateRequest{ - Checksum: c.Checksum, - DefaultUser: c.Username, - Description: c.Description, - Build: c.Build, - Version: c.Version, - Maintainer: c.Maintainer, - Name: c.Name, - PasswordEnabled: &passwordEnabled, - SSHKeyEnabled: &sshKeyEnabled, - URL: c.URL, + ApplicationConsistentSnapshotEnabled: &c.AppConsistentSnapshotEnabled, + Checksum: c.Checksum, + DefaultUser: c.Username, + Description: c.Description, + Build: c.Build, + Version: c.Version, + Maintainer: c.Maintainer, + Name: c.Name, + PasswordEnabled: &passwordEnabled, + SSHKeyEnabled: &sshKeyEnabled, + URL: c.URL, } if c.FromSnapshot != "" { @@ -155,6 +157,7 @@ func (c *instanceTemplateRegisterCmd) CmdRun(cmd *cobra.Command, _ []string) err } else { templateRequest.DefaultUser = srcTemplate.DefaultUser } + } if cmd.Flags().Changed(exocmd.MustCLICommandFlagName(c, &c.BootMode)) { @@ -180,22 +183,23 @@ func (c *instanceTemplateRegisterCmd) CmdRun(cmd *cobra.Command, _ []string) err if !globalstate.Quiet { return c.OutputFunc(&instanceTemplateShowOutput{ - ID: template.ID.String(), - Zone: c.Zone, - Family: template.Family, - Name: template.Name, - Description: template.Description, - CreationDate: template.CreatedAT.String(), - Visibility: string(template.Visibility), - Size: template.Size, - Version: template.Version, - Build: template.Build, - Maintainer: template.Maintainer, - Checksum: template.Checksum, - DefaultUser: template.DefaultUser, - SSHKeyEnabled: utils.DefaultBool(template.SSHKeyEnabled, false), - PasswordEnabled: utils.DefaultBool(template.PasswordEnabled, false), - BootMode: string(template.BootMode), + ID: template.ID.String(), + Zone: c.Zone, + Family: template.Family, + Name: template.Name, + Description: template.Description, + CreationDate: template.CreatedAT.String(), + Visibility: string(template.Visibility), + Size: template.Size, + Version: template.Version, + Build: template.Build, + Maintainer: template.Maintainer, + Checksum: template.Checksum, + DefaultUser: template.DefaultUser, + SSHKeyEnabled: utils.DefaultBool(template.SSHKeyEnabled, false), + PasswordEnabled: utils.DefaultBool(template.PasswordEnabled, false), + BootMode: string(template.BootMode), + AppConsistentSnapshotEnabled: utils.DefaultBool(template.ApplicationConsistentSnapshotEnabled, false), }, nil) } diff --git a/cmd/compute/instance_template/instance_template_show.go b/cmd/compute/instance_template/instance_template_show.go index edd6a5b73..b256b0000 100644 --- a/cmd/compute/instance_template/instance_template_show.go +++ b/cmd/compute/instance_template/instance_template_show.go @@ -17,22 +17,23 @@ import ( ) type instanceTemplateShowOutput struct { - ID string `json:"id"` - Zone string `json:"zone"` - Name string `json:"name"` - Description string `json:"description"` - Family string `json:"family"` - CreationDate string `json:"creation_date"` - Visibility string `json:"visibility"` - Size int64 `json:"size"` - Version string `json:"version"` - Build string `json:"build"` - Maintainer string `json:"maintainer"` - DefaultUser string `json:"default_user"` - SSHKeyEnabled bool `json:"ssh_key_enabled"` - PasswordEnabled bool `json:"password_enabled"` - BootMode string `json:"boot_mode"` - Checksum string `json:"checksum"` + ID string `json:"id"` + Zone string `json:"zone"` + Name string `json:"name"` + Description string `json:"description"` + Family string `json:"family"` + CreationDate string `json:"creation_date"` + Visibility string `json:"visibility"` + Size int64 `json:"size"` + Version string `json:"version"` + Build string `json:"build"` + Maintainer string `json:"maintainer"` + DefaultUser string `json:"default_user"` + SSHKeyEnabled bool `json:"ssh_key_enabled"` + PasswordEnabled bool `json:"password_enabled"` + BootMode string `json:"boot_mode"` + Checksum string `json:"checksum"` + AppConsistentSnapshotEnabled bool `json:"application_consistent_snapshot_enabled"` } func (o *instanceTemplateShowOutput) ToJSON() { output.JSON(o) } @@ -58,6 +59,7 @@ func (o *instanceTemplateShowOutput) ToTable() { t.Append([]string{"Password enabled", fmt.Sprint(o.PasswordEnabled)}) t.Append([]string{"Boot Mode", o.BootMode}) t.Append([]string{"Checksum", o.Checksum}) + t.Append([]string{"Application Consistent Snapshot Enabled", fmt.Sprint(o.AppConsistentSnapshotEnabled)}) } type instanceTemplateShowCmd struct { @@ -107,22 +109,23 @@ func (c *instanceTemplateShowCmd) CmdRun(_ *cobra.Command, _ []string) error { } return c.OutputFunc(&instanceTemplateShowOutput{ - ID: template.ID.String(), - Zone: c.Zone, - Family: template.Family, - Name: template.Name, - Description: template.Description, - CreationDate: template.CreatedAT.String(), - Visibility: string(template.Visibility), - Size: template.Size, - Version: template.Version, - Build: template.Build, - Maintainer: template.Maintainer, - Checksum: template.Checksum, - DefaultUser: template.DefaultUser, - SSHKeyEnabled: utils.DefaultBool(template.SSHKeyEnabled, false), - PasswordEnabled: utils.DefaultBool(template.PasswordEnabled, false), - BootMode: string(template.BootMode), + ID: template.ID.String(), + Zone: c.Zone, + Family: template.Family, + Name: template.Name, + Description: template.Description, + CreationDate: template.CreatedAT.String(), + Visibility: string(template.Visibility), + Size: template.Size, + Version: template.Version, + Build: template.Build, + Maintainer: template.Maintainer, + Checksum: template.Checksum, + DefaultUser: template.DefaultUser, + SSHKeyEnabled: utils.DefaultBool(template.SSHKeyEnabled, false), + PasswordEnabled: utils.DefaultBool(template.PasswordEnabled, false), + BootMode: string(template.BootMode), + AppConsistentSnapshotEnabled: utils.DefaultBool(template.ApplicationConsistentSnapshotEnabled, false), }, nil) } From 40b58d0eb451eaa3bf881c783694f96c7bc4be33 Mon Sep 17 00:00:00 2001 From: Thiago Dantas Date: Mon, 2 Feb 2026 15:53:06 +0000 Subject: [PATCH 2/2] updated changelog --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ebc69c66..9ee43cef9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ ## Unreleased +### Features + +- instance create: added support for the new --application-consistent-snapshot-enabled flag +- instance update: added support for the new --application-consistent-snapshot-enabled flag +- instance show: now displays the Application Consistent Snapshot Enabled field +- instance snapshot create: added support for the new --application-consistent-snapshot-enabled flag +- instance snapshot show: now displays the Application Consistent Snapshot Enabled field +- instance-template register: added support for the new --application-consistent-snapshot-enabled flag +- instance-template show: now displays the Application Consistent Snapshot Enabled field + + ### Bug fixes - fix(instance): create instance with an IPv6 #788