@@ -176,6 +176,10 @@ func serverCreateCommand() *core.Command {
176176 Short : "Create an instance with 2 local volumes (10GB and 10GB)" ,
177177 ArgsJSON : `{"image":"ubuntu_focal","root_volume":"local:10GB","additional_volumes":["local:10GB"]}` ,
178178 },
179+ {
180+ Short : "Create an instance with volumes from snapshots" ,
181+ ArgsJSON : `{"image":"ubuntu_focal","root_volume":"local:<snapshot_id>","additional_volumes":["block:<snapshot_id>"]}` ,
182+ },
179183 {
180184 Short : "Use an existing IP" ,
181185 Raw : `ip=$(scw instance ip create | grep id | awk '{ print $2 }')
@@ -493,6 +497,7 @@ func buildVolumes(api *instance.API, zone scw.Zone, serverName, rootVolume strin
493497//
494498// A valid volume format is either
495499// - a "creation" format: ^((local|l|block|b):)?\d+GB?$ (size is handled by go-humanize, so other sizes are supported)
500+ // - a "creation" format with a snapshot id: l:<uuid> b:<uuid>
496501// - a UUID format
497502func buildVolumeTemplate (api * instance.API , zone scw.Zone , flagV string ) (* instance.VolumeServerTemplate , error ) {
498503 parts := strings .Split (strings .TrimSpace (flagV ), ":" )
@@ -510,6 +515,10 @@ func buildVolumeTemplate(api *instance.API, zone scw.Zone, flagV string) (*insta
510515 return nil , fmt .Errorf ("invalid volume type %s in %s volume" , parts [0 ], flagV )
511516 }
512517
518+ if validation .IsUUID (parts [1 ]) {
519+ return buildVolumeTemplateFromSnapshot (api , zone , parts [1 ], vt .VolumeType )
520+ }
521+
513522 size , err := humanize .ParseBytes (parts [1 ])
514523 if err != nil {
515524 return nil , fmt .Errorf ("invalid size format %s in %s volume" , parts [1 ], flagV )
@@ -534,14 +543,17 @@ func buildVolumeTemplate(api *instance.API, zone scw.Zone, flagV string) (*insta
534543// buildVolumeTemplateFromUUID validate an UUID volume and add their types and sizes.
535544// Add volume types and sizes allow US to treat UUID volumes like the others and simplify the implementation.
536545// The instance API refuse the type and the size for UUID volumes, therefore,
537- // buildVolumeMap function will remove them.
546+ // sanitizeVolumeMap function will remove them.
538547func buildVolumeTemplateFromUUID (api * instance.API , zone scw.Zone , volumeUUID string ) (* instance.VolumeServerTemplate , error ) {
539548 res , err := api .GetVolume (& instance.GetVolumeRequest {
540549 Zone : zone ,
541550 VolumeID : volumeUUID ,
542551 })
543- if err != nil { // FIXME: isNotFoundError
544- return nil , fmt .Errorf ("volume %s does not exist" , volumeUUID )
552+ if err != nil {
553+ if core .IsNotFoundError (err ) {
554+ return nil , fmt .Errorf ("volume %s does not exist" , volumeUUID )
555+ }
556+ return nil , err
545557 }
546558
547559 // Check that volume is not already attached to a server.
@@ -556,6 +568,35 @@ func buildVolumeTemplateFromUUID(api *instance.API, zone scw.Zone, volumeUUID st
556568 }, nil
557569}
558570
571+ // buildVolumeTemplateFromUUID validate a snapshot UUID and check that requested volume type is compatible.
572+ // The instance API refuse the size for Snapshot volumes, therefore,
573+ // sanitizeVolumeMap function will remove them.
574+ func buildVolumeTemplateFromSnapshot (api * instance.API , zone scw.Zone , snapshotUUID string , volumeType instance.VolumeVolumeType ) (* instance.VolumeServerTemplate , error ) {
575+ res , err := api .GetSnapshot (& instance.GetSnapshotRequest {
576+ Zone : zone ,
577+ SnapshotID : snapshotUUID ,
578+ })
579+ if err != nil {
580+ if core .IsNotFoundError (err ) {
581+ return nil , fmt .Errorf ("snapshot %s does not exist" , snapshotUUID )
582+ }
583+ return nil , err
584+ }
585+
586+ snapshotType := res .Snapshot .VolumeType
587+
588+ if snapshotType != instance .VolumeVolumeTypeUnified && snapshotType != volumeType {
589+ return nil , fmt .Errorf ("snapshot of type %s not compatible with requested volume type %s" , snapshotType , volumeType )
590+ }
591+
592+ return & instance.VolumeServerTemplate {
593+ Name : res .Snapshot .Name ,
594+ VolumeType : volumeType ,
595+ BaseSnapshot : res .Snapshot .ID ,
596+ Size : res .Snapshot .Size ,
597+ }, nil
598+ }
599+
559600func validateImageServerTypeCompatibility (image * instance.Image , serverType * instance.ServerType , CommercialType string ) error {
560601 // An instance might not have any constraints on the local volume size
561602 if serverType .VolumesConstraint .MaxSize == 0 {
@@ -637,6 +678,12 @@ func sanitizeVolumeMap(serverName string, volumes map[string]*instance.VolumeSer
637678 ID : v .ID ,
638679 Name : v .Name ,
639680 }
681+ case v .BaseSnapshot != "" :
682+ v = & instance.VolumeServerTemplate {
683+ BaseSnapshot : v .BaseSnapshot ,
684+ Name : v .Name ,
685+ VolumeType : v .VolumeType ,
686+ }
640687 case index == "0" && v .Size != 0 :
641688 v = & instance.VolumeServerTemplate {
642689 VolumeType : v .VolumeType ,
0 commit comments