Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --force flag to the console command #1491

Merged
merged 7 commits into from
Dec 11, 2024
Merged
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: 8 additions & 0 deletions client/incus_instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -2528,6 +2528,10 @@ func (r *ProtocolIncus) ConsoleInstance(instanceName string, console api.Instanc
return nil, fmt.Errorf("The server is missing the required \"console_vga_type\" API extension")
}

if console.Force && !r.HasExtension("console_force") {
return nil, fmt.Errorf(`The server is missing the required "console_force" API extension`)
}

// Send the request
op, _, err := r.queryOperation("POST", fmt.Sprintf("%s/%s/console", path, url.PathEscape(instanceName)), console, "")
if err != nil {
Expand Down Expand Up @@ -2616,6 +2620,10 @@ func (r *ProtocolIncus) ConsoleInstanceDynamic(instanceName string, console api.
return nil, nil, fmt.Errorf("The server is missing the required \"console_vga_type\" API extension")
}

if console.Force && !r.HasExtension("console_force") {
return nil, nil, fmt.Errorf(`The server is missing the required "console_force" API extension`)
}

// Send the request.
op, _, err := r.queryOperation("POST", fmt.Sprintf("%s/%s/console", path, url.PathEscape(instanceName)), console, "")
if err != nil {
Expand Down
6 changes: 5 additions & 1 deletion cmd/incus/console.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
type cmdConsole struct {
global *cmdGlobal

flagForce bool
flagShowLog bool
flagType string
}
Expand All @@ -41,6 +42,7 @@ This command allows you to interact with the boot console of an instance
as well as retrieve past log entries from it.`))

cmd.RunE = c.Run
cmd.Flags().BoolVarP(&c.flagForce, "force", "f", false, i18n.G("Forces a connection to the console, even if there is already an active session"))
cmd.Flags().BoolVar(&c.flagShowLog, "show-log", false, i18n.G("Retrieve the instance's console log"))
cmd.Flags().StringVarP(&c.flagType, "type", "t", "console", i18n.G("Type of connection to establish: 'console' for serial console, 'vga' for SPICE graphical output")+"``")

Expand Down Expand Up @@ -188,6 +190,7 @@ func (c *cmdConsole) text(d incus.InstanceServer, name string) error {
Width: width,
Height: height,
Type: "console",
Force: c.flagForce,
}

consoleDisconnect := make(chan bool)
Expand Down Expand Up @@ -243,7 +246,8 @@ func (c *cmdConsole) vga(d incus.InstanceServer, name string) error {

// Prepare the remote console.
req := api.InstanceConsolePost{
Type: "vga",
Type: "vga",
Force: c.flagForce,
}

chDisconnect := make(chan bool)
Expand Down
57 changes: 56 additions & 1 deletion cmd/incusd/instance_console.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"net/http"
"net/url"
"os"
"path"
"slices"
"strconv"
"sync"
Expand Down Expand Up @@ -391,6 +392,26 @@ func (s *consoleWs) doVGA(op *operations.Operation) error {
return err
}

// Cancel is responsible for closing websocket connections.
func (s *consoleWs) Cancel(op *operations.Operation) error {
s.connsLock.Lock()
conn := s.conns[-1]
s.connsLock.Unlock()

err := conn.Close()
if err != nil {
return err
}

// Close all dynamic connections.
for conn, console := range s.dynamic {
_ = conn.Close()
_ = console.Close()
}

return nil
}

// swagger:operation POST /1.0/instances/{name}/console instances instance_console_post
//
// Connect to console
Expand Down Expand Up @@ -500,6 +521,40 @@ func instanceConsolePost(d *Daemon, r *http.Request) response.Response {
return response.BadRequest(fmt.Errorf("Instance is frozen"))
}

// Find any running 'ConsoleShow' operation for the instance.
// If the '--force' flag was used, cancel the running operation. Otherwise, notify the user about the operation.
for _, op := range operations.Clone() {
// Consider only console show operations with Running status.
if op.Type() != operationtype.ConsoleShow || op.Project() != projectName || op.Status() != api.Running {
continue
}

// Fetch instance name from operation.
r := op.Resources()
apiUrls := r["instances"]
if len(apiUrls) < 1 {
return response.SmartError(fmt.Errorf("Operation does not have an instance URL defined"))
}

urlPrefix, instanceName := path.Split(apiUrls[0].URL.Path)
if urlPrefix == "" || instanceName == "" {
return response.SmartError(fmt.Errorf("Instance URL has incorrect format"))
}

if instanceName != inst.Name() {
continue
}

if !post.Force {
return response.SmartError(fmt.Errorf("This console is already connected. Force is required to take it over."))
}

_, err = op.Cancel()
if err != nil {
return response.SmartError(err)
}
}

ws := &consoleWs{}
ws.fds = map[int]string{}
ws.conns = map[int]*websocket.Conn{}
Expand All @@ -523,7 +578,7 @@ func instanceConsolePost(d *Daemon, r *http.Request) response.Response {
resources := map[string][]api.URL{}
resources["instances"] = []api.URL{*api.NewURL().Path(version.APIVersion, "instances", ws.instance.Name())}

op, err := operations.OperationCreate(s, projectName, operations.OperationClassWebsocket, operationtype.ConsoleShow, resources, ws.Metadata(), ws.Do, nil, ws.Connect, r)
op, err := operations.OperationCreate(s, projectName, operations.OperationClassWebsocket, operationtype.ConsoleShow, resources, ws.Metadata(), ws.Do, ws.Cancel, ws.Connect, r)
if err != nil {
return response.InternalError(err)
}
Expand Down
5 changes: 5 additions & 0 deletions doc/api-extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2672,3 +2672,8 @@ Adds a new `X-Incus-aliases` HTTP header to set aliases while uploading an image
## `authorization_scriptlet`

This adds the ability to define a scriptlet in a new configuration key, `authorization.scriptlet`, managing authorization on the Incus cluster.

## `console_force`

This adds support for forcing a connection to the console, even if there is already an active session.
It introduces the new `--force` flag for connecting to the instance console.
5 changes: 5 additions & 0 deletions doc/rest-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1511,6 +1511,11 @@ definitions:
x-go-package: github.com/lxc/incus/v6/shared/api
InstanceConsolePost:
properties:
force:
description: Forces a connection to the console
example: true
type: boolean
x-go-name: Force
height:
description: Console height in rows (console type only)
example: 24
Expand Down
1 change: 1 addition & 0 deletions internal/version/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ var APIExtensions = []string{
"instance_console_screenshot",
"image_import_alias",
"authorization_scriptlet",
"console_force",
}

// APIExtensionsCount returns the number of available API extensions.
Expand Down
34 changes: 20 additions & 14 deletions po/de.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: lxc-devel@lists.linuxcontainers.org\n"
"POT-Creation-Date: 2024-12-06 15:12-0500\n"
"POT-Creation-Date: 2024-12-11 17:46+0000\n"
"PO-Revision-Date: 2024-10-06 20:15+0000\n"
"Last-Translator: Jan Mittelstädter <jan@mittelstaedter.de>\n"
"Language-Team: German <https://hosted.weblate.org/projects/incus/cli/de/>\n"
Expand Down Expand Up @@ -1064,7 +1064,7 @@ msgstr ""
"Sind Sie sicher, dass sie das Cluster Mitglied %q %s? (ja/nein) "
"[default=nein]: "

#: cmd/incus/console.go:384
#: cmd/incus/console.go:388
msgid "As neither could be found, the raw SPICE socket can be found at:"
msgstr ""
"Da keines der genannten Programme gefunden werden konnte, finden Sie hier "
Expand Down Expand Up @@ -1109,11 +1109,11 @@ msgstr "Storage Volumes zu Profilen hinzufügen"
msgid "Attach new network interfaces to instances"
msgstr "Netzwerkschnittstellen (network interfaces) zu Instanzen hinzufügen"

#: cmd/incus/console.go:36
#: cmd/incus/console.go:37
msgid "Attach to instance consoles"
msgstr "Konsole der Instanz öffnen"

#: cmd/incus/console.go:37
#: cmd/incus/console.go:38
#, fuzzy
msgid ""
"Attach to instance consoles\n"
Expand Down Expand Up @@ -2216,7 +2216,7 @@ msgstr ""
#: cmd/incus/config_trust.go:171 cmd/incus/config_trust.go:275
#: cmd/incus/config_trust.go:400 cmd/incus/config_trust.go:590
#: cmd/incus/config_trust.go:743 cmd/incus/config_trust.go:789
#: cmd/incus/config_trust.go:860 cmd/incus/console.go:37 cmd/incus/copy.go:41
#: cmd/incus/config_trust.go:860 cmd/incus/console.go:38 cmd/incus/copy.go:41
#: cmd/incus/create.go:43 cmd/incus/delete.go:32 cmd/incus/exec.go:41
#: cmd/incus/export.go:32 cmd/incus/file.go:88 cmd/incus/file.go:135
#: cmd/incus/file.go:331 cmd/incus/file.go:413 cmd/incus/file.go:491
Expand Down Expand Up @@ -3182,7 +3182,7 @@ msgstr "Akzeptiere Zertifikat"
msgid "Failed parsing validation response: %w"
msgstr "Akzeptiere Zertifikat"

#: cmd/incus/console.go:362
#: cmd/incus/console.go:366
#, fuzzy, c-format
msgid "Failed starting command: %w"
msgstr "Akzeptiere Zertifikat"
Expand Down Expand Up @@ -3506,6 +3506,12 @@ msgid ""
"Are you really sure you want to force removing %s? (yes/no): "
msgstr ""

#: cmd/incus/console.go:45
msgid ""
"Forces a connection to the console, even if there is already an active "
"session"
msgstr ""

#: cmd/incus/admin_sql.go:55 cmd/incus/alias.go:113 cmd/incus/cluster.go:151
#: cmd/incus/cluster.go:1091 cmd/incus/cluster_group.go:478
#: cmd/incus/config_template.go:290 cmd/incus/config_trust.go:422
Expand Down Expand Up @@ -7031,7 +7037,7 @@ msgstr ""
msgid "Resume instances"
msgstr "kann nicht zum selben Container Namen kopieren"

#: cmd/incus/console.go:44
#: cmd/incus/console.go:46
#, fuzzy
msgid "Retrieve the instance's console log"
msgstr "Herunterfahren des Containers erzwingen."
Expand Down Expand Up @@ -8116,7 +8122,7 @@ msgstr ""
msgid "The %s storage pool already exists"
msgstr "entfernte Instanz %s existiert bereits"

#: cmd/incus/console.go:134
#: cmd/incus/console.go:136
msgid "The --show-log flag is only supported for by 'console' output type"
msgstr ""

Expand Down Expand Up @@ -8147,7 +8153,7 @@ msgid ""
"You can invoke it through \"incusd cluster\"."
msgstr ""

#: cmd/incus/console.go:383
#: cmd/incus/console.go:387
msgid ""
"The client automatically uses either spicy or remote-viewer when present."
msgstr ""
Expand Down Expand Up @@ -8421,7 +8427,7 @@ msgstr ""
msgid "To create a new network, use: incus network create"
msgstr ""

#: cmd/incus/console.go:221
#: cmd/incus/console.go:224
msgid "To detach from the console, press: <ctrl>+a q"
msgstr ""

Expand Down Expand Up @@ -8510,7 +8516,7 @@ msgstr ""
msgid "Type of certificate"
msgstr "Akzeptiere Zertifikat"

#: cmd/incus/console.go:45
#: cmd/incus/console.go:47
msgid ""
"Type of connection to establish: 'console' for serial console, 'vga' for "
"SPICE graphical output"
Expand Down Expand Up @@ -8612,7 +8618,7 @@ msgstr "Unbekannter Befehl %s für Abbild"
msgid "Unknown column shorthand char '%c' in '%s'"
msgstr ""

#: cmd/incus/console.go:164
#: cmd/incus/console.go:166
#, fuzzy, c-format
msgid "Unknown console type %q"
msgstr "Unbekannter Befehl %s für Abbild"
Expand All @@ -8627,7 +8633,7 @@ msgstr "Unbekannter Befehl %s für Abbild"
msgid "Unknown key: %s"
msgstr "Unbekannter Befehl %s für Abbild"

#: cmd/incus/console.go:113
#: cmd/incus/console.go:115
#, fuzzy, c-format
msgid "Unknown output type %q"
msgstr "Unbekannter Befehl %s für Abbild"
Expand Down Expand Up @@ -9461,7 +9467,7 @@ msgstr ""

#: cmd/incus/config_device.go:320 cmd/incus/config_device.go:752
#: cmd/incus/config_metadata.go:52 cmd/incus/config_metadata.go:185
#: cmd/incus/config_template.go:286 cmd/incus/console.go:35
#: cmd/incus/config_template.go:286 cmd/incus/console.go:36
#: cmd/incus/snapshot.go:292
#, fuzzy
msgid "[<remote>:]<instance>"
Expand Down
Loading
Loading