Skip to content

Commit

Permalink
lxd: Add support for starting instances on creation (from Incus) (can…
Browse files Browse the repository at this point in the history
…onical#13695)

This PR adds support for starting instances on creation.

Includes cherry-picks from lxc/incus#410.

Closes canonical#13322
  • Loading branch information
tomponline authored Jul 25, 2024
2 parents 9098da7 + 846fe00 commit 0021632
Show file tree
Hide file tree
Showing 48 changed files with 974 additions and 531 deletions.
7 changes: 7 additions & 0 deletions doc/api-extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2434,3 +2434,10 @@ This feature was added to prevent data loss which can happen when custom block v
## `instance_import_conversion`

Adds the ability to convert images from different formats (e.g. VMDK or QCow2) into RAW image format and import them as LXD instances.

## `instance_create_start`

Adds a `start` field to the `POST /1.0/instances` API which when set
to `true` will have the instance automatically start upon creation.

In this scenario, the creation and startup is part of a single background operation.
13 changes: 13 additions & 0 deletions doc/howto/instances_create.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,19 @@ To start an instance, send a PUT request to change the instance state:
See {ref}`instances-manage-start` for more information.
If you would like to start the instance upon creation, set the `start` property to true. The following example will create the container, then start it:
lxc query --request POST /1.0/instances --data '{
"name": "<instance_name>",
"source": {
"alias": "<image_alias>",
"protocol": "simplestreams",
"server": "<server_URL>",
"type": "image"
},
"start": true
}'
````
````{group-tab} UI
Expand Down
5 changes: 5 additions & 0 deletions doc/rest-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2752,6 +2752,11 @@ definitions:
x-go-name: Restore
source:
$ref: '#/definitions/InstanceSource'
start:
description: Whether to start the instance after creation
example: true
type: boolean
x-go-name: Start
stateful:
description: Whether the instance currently has saved state on disk
example: false
Expand Down
19 changes: 14 additions & 5 deletions lxc/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,11 @@ func (c *cmdInit) run(cmd *cobra.Command, args []string) error {
return nil
}

_, _, err = c.create(c.global.conf, args)
_, _, err = c.create(c.global.conf, args, false)
return err
}

func (c *cmdInit) create(conf *config.Config, args []string) (lxd.InstanceServer, string, error) {
func (c *cmdInit) create(conf *config.Config, args []string, launch bool) (lxd.InstanceServer, string, error) {
var name string
var image string
var remote string
Expand Down Expand Up @@ -164,10 +164,18 @@ func (c *cmdInit) create(conf *config.Config, args []string) (lxd.InstanceServer
}

if !c.global.flagQuiet {
if name == "" {
fmt.Printf(i18n.G("Creating the instance") + "\n")
if d.HasExtension("instance_create_start") && launch {
if name == "" {
fmt.Printf(i18n.G("Launching the instance") + "\n")
} else {
fmt.Printf(i18n.G("Launching %s")+"\n", name)
}
} else {
fmt.Printf(i18n.G("Creating %s")+"\n", name)
if name == "" {
fmt.Printf(i18n.G("Creating the instance") + "\n")
} else {
fmt.Printf(i18n.G("Creating %s")+"\n", name)
}
}
}

Expand Down Expand Up @@ -252,6 +260,7 @@ func (c *cmdInit) create(conf *config.Config, args []string) (lxd.InstanceServer
Name: name,
InstanceType: c.flagType,
Type: instanceDBType,
Start: launch,
}

req.Config = configMap
Expand Down
7 changes: 6 additions & 1 deletion lxc/launch.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,16 @@ func (c *cmdLaunch) run(cmd *cobra.Command, args []string) error {
}

// Call the matching code from init
d, name, err := c.init.create(conf, args)
d, name, err := c.init.create(conf, args, true)
if err != nil {
return err
}

// Check if the instance was started by the server.
if d.HasExtension("instance_create_start") {
return nil
}

// Get the remote
var remote string
if len(args) == 2 {
Expand Down
40 changes: 35 additions & 5 deletions lxd/instances_post.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,13 @@ func createFromImage(s *state.State, r *http.Request, p api.Project, profiles []
return err
}

return instanceCreateFromImage(s, img, args, op)
// Actually create the instance.
err = instanceCreateFromImage(s, img, args, op)
if err != nil {
return err
}

return instanceCreateFinish(s, req, args)
}

resources := map[string][]api.URL{}
Expand Down Expand Up @@ -175,8 +181,13 @@ func createFromNone(s *state.State, r *http.Request, projectName string, profile
}

run := func(op *operations.Operation) error {
// Actually create the instance.
_, err := instanceCreateAsEmpty(s, args)
return err
if err != nil {
return err
}

return instanceCreateFinish(s, req, args)
}

resources := map[string][]api.URL{}
Expand Down Expand Up @@ -602,6 +613,7 @@ func createFromCopy(s *state.State, r *http.Request, projectName string, profile
}

run := func(op *operations.Operation) error {
// Actually create the instance.
_, err := instanceCreateAsCopy(s, instanceCreateAsCopyOpts{
sourceInstance: source,
targetInstance: args,
Expand All @@ -614,7 +626,7 @@ func createFromCopy(s *state.State, r *http.Request, projectName string, profile
return err
}

return nil
return instanceCreateFinish(s, req, args)
}

resources := map[string][]api.URL{}
Expand Down Expand Up @@ -701,8 +713,9 @@ func createFromBackup(s *state.State, r *http.Request, projectName string, data
}

// Check project permissions.
var req api.InstancesPost
err = s.DB.Cluster.Transaction(s.ShutdownCtx, func(ctx context.Context, tx *db.ClusterTx) error {
req := api.InstancesPost{
req = api.InstancesPost{
InstancePut: bInfo.Config.Container.Writable(),
Name: bInfo.Name,
Source: api.InstanceSource{}, // Only relevant for "copy" or "migration", but may not be nil.
Expand Down Expand Up @@ -838,7 +851,8 @@ func createFromBackup(s *state.State, r *http.Request, projectName string, data
}

runRevert.Success()
return nil

return instanceCreateFinish(s, &req, db.InstanceArgs{Name: bInfo.Name, Project: bInfo.Project})
}

resources := map[string][]api.URL{}
Expand Down Expand Up @@ -1488,3 +1502,19 @@ func clusterCopyContainerInternal(s *state.State, r *http.Request, source instan
// Run the migration
return createFromMigration(s, nil, projectName, profiles, req)
}

// instanceCreateFinish finalizes the creation process of an instance by starting it based on
// the Start field of the request.
func instanceCreateFinish(s *state.State, req *api.InstancesPost, args db.InstanceArgs) error {
if req == nil || !req.Start {
return nil
}

// Start the instance.
inst, err := instance.LoadByProjectAndName(s, args.Project, args.Name)
if err != nil {
return fmt.Errorf("Failed to load the instance: %w", err)
}

return inst.Start(false)
}
35 changes: 22 additions & 13 deletions po/ar.po
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: lxd\n"
"Report-Msgid-Bugs-To: [email protected]\n"
"POT-Creation-Date: 2024-07-02 10:12-0700\n"
"POT-Creation-Date: 2024-07-24 14:26-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
Expand Down Expand Up @@ -766,7 +766,7 @@ msgstr ""
msgid "As neither could be found, the raw SPICE socket can be found at:"
msgstr ""

#: lxc/init.go:324 lxc/rebuild.go:130
#: lxc/init.go:333 lxc/rebuild.go:130
msgid "Asked for a VM but image is of type container"
msgstr ""

Expand Down Expand Up @@ -871,7 +871,7 @@ msgstr ""
msgid "Bad key/value pair: %s"
msgstr ""

#: lxc/copy.go:139 lxc/init.go:224 lxc/move.go:380 lxc/project.go:129
#: lxc/copy.go:139 lxc/init.go:232 lxc/move.go:380 lxc/project.go:129
#, c-format
msgid "Bad key=value pair: %q"
msgstr ""
Expand Down Expand Up @@ -1492,12 +1492,12 @@ msgstr ""
msgid "Created: %s"
msgstr ""

#: lxc/init.go:170
#: lxc/init.go:177
#, c-format
msgid "Creating %s"
msgstr ""

#: lxc/init.go:168
#: lxc/init.go:175
msgid "Creating the instance"
msgstr ""

Expand Down Expand Up @@ -1819,7 +1819,7 @@ msgstr ""
msgid "Device: %s"
msgstr ""

#: lxc/init.go:384
#: lxc/init.go:393
msgid "Didn't get any affected image, instance or snapshot from server"
msgstr ""

Expand Down Expand Up @@ -2864,7 +2864,7 @@ msgstr ""
msgid "Instance name is mandatory"
msgstr ""

#: lxc/init.go:395
#: lxc/init.go:404
#, c-format
msgid "Instance name is: %s"
msgstr ""
Expand Down Expand Up @@ -3051,6 +3051,15 @@ msgstr ""
msgid "Last used: never"
msgstr ""

#: lxc/init.go:171
#, c-format
msgid "Launching %s"
msgstr ""

#: lxc/init.go:169
msgid "Launching the instance"
msgstr ""

#: lxc/info.go:220
#, c-format
msgid "Link detected: %v"
Expand Down Expand Up @@ -4930,7 +4939,7 @@ msgstr ""
msgid "Retrieve the instance's console log"
msgstr ""

#: lxc/init.go:338
#: lxc/init.go:347
#, c-format
msgid "Retrieving image: %s"
msgstr ""
Expand Down Expand Up @@ -5565,7 +5574,7 @@ msgstr ""
msgid "Start instances"
msgstr ""

#: lxc/launch.go:82
#: lxc/launch.go:87
#, c-format
msgid "Starting %s"
msgstr ""
Expand Down Expand Up @@ -5770,7 +5779,7 @@ msgid ""
"restarted"
msgstr ""

#: lxc/init.go:416
#: lxc/init.go:425
msgid "The instance you are starting doesn't have any network attached to it."
msgstr ""

Expand Down Expand Up @@ -5931,11 +5940,11 @@ msgstr ""
msgid "Timestamps:"
msgstr ""

#: lxc/init.go:418
#: lxc/init.go:427
msgid "To attach a network to an instance, use: lxc network attach"
msgstr ""

#: lxc/init.go:417
#: lxc/init.go:426
msgid "To create a new network, use: lxc network create"
msgstr ""

Expand Down Expand Up @@ -6008,7 +6017,7 @@ msgstr ""
msgid "Trust token for %s: "
msgstr ""

#: lxc/action.go:287 lxc/launch.go:114
#: lxc/action.go:287 lxc/launch.go:119
#, c-format
msgid "Try `lxc info --show-log %s` for more info"
msgstr ""
Expand Down
Loading

0 comments on commit 0021632

Please sign in to comment.