[lxc-devel] [lxd/master] Client Instances 2
tomponline on Github
lxc-bot at linuxcontainers.org
Thu Sep 12 11:06:20 UTC 2019
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 511 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20190912/54273cd9/attachment-0001.bin>
-------------- next part --------------
From bdb0a4e19c57f51391609d83df1dd36f19fe155a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 11 Sep 2019 11:11:22 +0100
Subject: [PATCH 1/9] lxd/db/containers: Adds instanceType filter to
ContainerNodeAddress
- Supports instance.TypeAny if no filter is needed.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/db/containers.go | 43 +++++++++++++++++++++++++++++++++----------
1 file changed, 33 insertions(+), 10 deletions(-)
diff --git a/lxd/db/containers.go b/lxd/db/containers.go
index 068da2532a..0334215e06 100644
--- a/lxd/db/containers.go
+++ b/lxd/db/containers.go
@@ -175,30 +175,53 @@ SELECT instances.name FROM instances
// with the given name in the given project.
//
// It returns the empty string if the container is hosted on this node.
-func (c *ClusterTx) ContainerNodeAddress(project string, name string) (string, error) {
+func (c *ClusterTx) ContainerNodeAddress(project string, name string, instanceType instance.Type) (string, error) {
var stmt string
- args := []interface{}{project}
+
+ args := make([]interface{}, 0, 4) // Expect up to 4 filters.
+ var filters strings.Builder
+
+ // Project filter.
+ filters.WriteString("projects.name = ?")
+ args = append(args, project)
+
+ // Instance type filter.
+ if instanceType != instance.TypeAny {
+ filters.WriteString(" AND instances.type = ?")
+ args = append(args, instanceType)
+ }
if strings.Contains(name, shared.SnapshotDelimiter) {
parts := strings.SplitN(name, shared.SnapshotDelimiter, 2)
- stmt = `
+
+ // Instance name filter.
+ filters.WriteString(" AND instances.name = ?")
+ args = append(args, parts[0])
+
+ // Snapshot name filter.
+ filters.WriteString(" AND instances_snapshots.name = ?")
+ args = append(args, parts[1])
+
+ stmt = fmt.Sprintf(`
SELECT nodes.id, nodes.address
FROM nodes
JOIN instances ON instances.node_id = nodes.id
JOIN projects ON projects.id = instances.project_id
JOIN instances_snapshots ON instances_snapshots.instance_id = instances.id
- WHERE projects.name = ? AND instances.name = ? AND instances_snapshots.name = ?
-`
- args = append(args, parts[0], parts[1])
+ WHERE %s
+`, filters.String())
} else {
- stmt = `
+ // Instance name filter.
+ filters.WriteString(" AND instances.name = ?")
+ args = append(args, name)
+
+ stmt = fmt.Sprintf(`
SELECT nodes.id, nodes.address
FROM nodes
JOIN instances ON instances.node_id = nodes.id
JOIN projects ON projects.id = instances.project_id
- WHERE projects.name = ? AND instances.name = ?
-`
- args = append(args, name)
+ WHERE %s
+`, filters.String())
}
var address string
From 95c2715955df6f90095304cbb93a176302577eb9 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 11 Sep 2019 11:12:49 +0100
Subject: [PATCH 2/9] lxd/cluster/connect: Adds instanceType filter to
ConnectIfContainerIsRemote
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/cluster/connect.go | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lxd/cluster/connect.go b/lxd/cluster/connect.go
index 710d164fe6..b97625ffd1 100644
--- a/lxd/cluster/connect.go
+++ b/lxd/cluster/connect.go
@@ -7,6 +7,7 @@ import (
lxd "github.com/lxc/lxd/client"
"github.com/lxc/lxd/lxd/db"
+ "github.com/lxc/lxd/lxd/instance"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
"github.com/pkg/errors"
@@ -37,11 +38,11 @@ func Connect(address string, cert *shared.CertInfo, notify bool) (lxd.ContainerS
// running the container with the given name. If it's not the local node will
// connect to it and return the connected client, otherwise it will just return
// nil.
-func ConnectIfContainerIsRemote(cluster *db.Cluster, project, name string, cert *shared.CertInfo) (lxd.ContainerServer, error) {
+func ConnectIfContainerIsRemote(cluster *db.Cluster, project, name string, cert *shared.CertInfo, instanceType instance.Type) (lxd.ContainerServer, error) {
var address string // Node address
err := cluster.Transaction(func(tx *db.ClusterTx) error {
var err error
- address, err = tx.ContainerNodeAddress(project, name)
+ address, err = tx.ContainerNodeAddress(project, name, instanceType)
return err
})
if err != nil {
From 8d898dffcf39d1183cf47da5b7d6772037b41c30 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 11 Sep 2019 11:13:47 +0100
Subject: [PATCH 3/9] lxd/response: Adds instanceType filter to
ForwardedResponseIfContainerIsRemote
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/response.go | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lxd/response.go b/lxd/response.go
index 76b58b3b3c..bd0980fb6b 100644
--- a/lxd/response.go
+++ b/lxd/response.go
@@ -18,6 +18,7 @@ import (
lxd "github.com/lxc/lxd/client"
"github.com/lxc/lxd/lxd/cluster"
"github.com/lxc/lxd/lxd/db"
+ "github.com/lxc/lxd/lxd/instance"
"github.com/lxc/lxd/lxd/util"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
@@ -191,9 +192,9 @@ func ForwardedResponseIfTargetIsRemote(d *Daemon, request *http.Request) Respons
// ForwardedResponseIfContainerIsRemote redirects a request to the node running
// the container with the given name. If the container is local, nothing gets
// done and nil is returned.
-func ForwardedResponseIfContainerIsRemote(d *Daemon, r *http.Request, project, name string) (Response, error) {
+func ForwardedResponseIfContainerIsRemote(d *Daemon, r *http.Request, project, name string, instanceType instance.Type) (Response, error) {
cert := d.endpoints.NetworkCert()
- client, err := cluster.ConnectIfContainerIsRemote(d.cluster, project, name, cert)
+ client, err := cluster.ConnectIfContainerIsRemote(d.cluster, project, name, cert, instanceType)
if err != nil {
return nil, err
}
From 6d22940c5d1c6870ae5a22b98d7477406948f4b2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 11 Sep 2019 11:14:10 +0100
Subject: [PATCH 4/9] lxd: Updates use of ForwardedResponseIfContainerIsRemote
to supply instanceType
- This has the effect of validating whether the supplied instance name exists as the same type as the context of the endpoint.
- Instance type is derived from the context of the endpoint.
- If context is /1.0/containers then instance.TypeContainer is used.
- If context is /1.0/instances then instance.TypeAny is used.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/container_backup.go | 49 ++++++++++++++++++++++++++++++++++-----
lxd/container_console.go | 18 ++++++++++++--
lxd/container_delete.go | 10 +++++++-
lxd/container_exec.go | 12 +++++++---
lxd/container_file.go | 10 +++++++-
lxd/container_get.go | 11 ++++++++-
lxd/container_logs.go | 26 ++++++++++++++++++---
lxd/container_metadata.go | 44 +++++++++++++++++++++++++++++------
lxd/container_patch.go | 10 +++++++-
lxd/container_post.go | 18 ++++++++++----
lxd/container_put.go | 10 +++++++-
lxd/container_snapshot.go | 25 +++++++++++++++++---
lxd/container_state.go | 18 ++++++++++++--
lxd/containers_post.go | 2 +-
lxd/images.go | 13 +++++++----
15 files changed, 235 insertions(+), 41 deletions(-)
diff --git a/lxd/container_backup.go b/lxd/container_backup.go
index bbf436e126..7f950a222c 100644
--- a/lxd/container_backup.go
+++ b/lxd/container_backup.go
@@ -11,6 +11,7 @@ import (
"github.com/pkg/errors"
"github.com/lxc/lxd/lxd/db"
+ "github.com/lxc/lxd/lxd/instance"
"github.com/lxc/lxd/lxd/util"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
@@ -18,11 +19,17 @@ import (
)
func containerBackupsGet(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
cname := mux.Vars(r)["name"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, cname)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, cname, instanceType)
if err != nil {
return SmartError(err)
}
@@ -64,11 +71,17 @@ func containerBackupsGet(d *Daemon, r *http.Request) Response {
}
func containerBackupsPost(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
@@ -176,12 +189,18 @@ func containerBackupsPost(d *Daemon, r *http.Request) Response {
}
func containerBackupGet(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
backupName := mux.Vars(r)["backupName"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
@@ -199,12 +218,18 @@ func containerBackupGet(d *Daemon, r *http.Request) Response {
}
func containerBackupPost(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
backupName := mux.Vars(r)["backupName"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
@@ -253,12 +278,18 @@ func containerBackupPost(d *Daemon, r *http.Request) Response {
}
func containerBackupDelete(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
backupName := mux.Vars(r)["backupName"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
@@ -294,12 +325,18 @@ func containerBackupDelete(d *Daemon, r *http.Request) Response {
}
func containerBackupExportGet(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
backupName := mux.Vars(r)["backupName"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
diff --git a/lxd/container_console.go b/lxd/container_console.go
index c498f6cad7..2ccf9094f4 100644
--- a/lxd/container_console.go
+++ b/lxd/container_console.go
@@ -8,6 +8,7 @@ import (
"os"
"os/exec"
"strconv"
+ "strings"
"sync"
"syscall"
@@ -18,6 +19,7 @@ import (
"github.com/lxc/lxd/lxd/cluster"
"github.com/lxc/lxd/lxd/db"
+ "github.com/lxc/lxd/lxd/instance"
"github.com/lxc/lxd/lxd/util"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
@@ -254,6 +256,12 @@ func (s *consoleWs) Do(op *operation) error {
}
func containerConsolePost(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
@@ -270,7 +278,7 @@ func containerConsolePost(d *Daemon, r *http.Request) Response {
// Forward the request if the container is remote.
cert := d.endpoints.NetworkCert()
- client, err := cluster.ConnectIfContainerIsRemote(d.cluster, project, name, cert)
+ client, err := cluster.ConnectIfContainerIsRemote(d.cluster, project, name, cert, instanceType)
if err != nil {
return SmartError(err)
}
@@ -343,11 +351,17 @@ func containerConsolePost(d *Daemon, r *http.Request) Response {
}
func containerConsoleLogGet(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
// Forward the request if the container is remote.
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
diff --git a/lxd/container_delete.go b/lxd/container_delete.go
index 6e6d653928..319f7cd853 100644
--- a/lxd/container_delete.go
+++ b/lxd/container_delete.go
@@ -3,17 +3,25 @@ package main
import (
"fmt"
"net/http"
+ "strings"
"github.com/gorilla/mux"
"github.com/lxc/lxd/lxd/db"
+ "github.com/lxc/lxd/lxd/instance"
)
func containerDelete(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
diff --git a/lxd/container_exec.go b/lxd/container_exec.go
index e53fad8727..358b88f555 100644
--- a/lxd/container_exec.go
+++ b/lxd/container_exec.go
@@ -19,13 +19,13 @@ import (
"github.com/lxc/lxd/lxd/cluster"
"github.com/lxc/lxd/lxd/db"
+ "github.com/lxc/lxd/lxd/instance"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
+ log "github.com/lxc/lxd/shared/log15"
"github.com/lxc/lxd/shared/logger"
"github.com/lxc/lxd/shared/netutils"
"github.com/lxc/lxd/shared/version"
-
- log "github.com/lxc/lxd/shared/log15"
)
type execWs struct {
@@ -342,6 +342,12 @@ func (s *execWs) Do(op *operation) error {
}
func containerExecPost(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
@@ -357,7 +363,7 @@ func containerExecPost(d *Daemon, r *http.Request) Response {
// Forward the request if the container is remote.
cert := d.endpoints.NetworkCert()
- client, err := cluster.ConnectIfContainerIsRemote(d.cluster, project, name, cert)
+ client, err := cluster.ConnectIfContainerIsRemote(d.cluster, project, name, cert, instanceType)
if err != nil {
return SmartError(err)
}
diff --git a/lxd/container_file.go b/lxd/container_file.go
index e9358c091a..21f8b33693 100644
--- a/lxd/container_file.go
+++ b/lxd/container_file.go
@@ -7,17 +7,25 @@ import (
"net/http"
"os"
"path/filepath"
+ "strings"
"github.com/gorilla/mux"
+ "github.com/lxc/lxd/lxd/instance"
"github.com/lxc/lxd/shared"
)
func containerFileHandler(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
diff --git a/lxd/container_get.go b/lxd/container_get.go
index 565699364d..7dacde2f60 100644
--- a/lxd/container_get.go
+++ b/lxd/container_get.go
@@ -2,16 +2,25 @@ package main
import (
"net/http"
+ "strings"
"github.com/gorilla/mux"
+
+ "github.com/lxc/lxd/lxd/instance"
)
func containerGet(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
diff --git a/lxd/container_logs.go b/lxd/container_logs.go
index 92e026d3e2..72b3dfa80e 100644
--- a/lxd/container_logs.go
+++ b/lxd/container_logs.go
@@ -9,6 +9,7 @@ import (
"github.com/gorilla/mux"
+ "github.com/lxc/lxd/lxd/instance"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/version"
)
@@ -38,11 +39,18 @@ func containerLogsGet(d *Daemon, r *http.Request) Response {
* However, we should check this name and ensure it's a valid container
* name just so that people can't list arbitrary directories.
*/
+
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
@@ -84,11 +92,17 @@ func validLogFileName(fname string) bool {
}
func containerLogGet(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
@@ -115,11 +129,17 @@ func containerLogGet(d *Daemon, r *http.Request) Response {
}
func containerLogDelete(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
diff --git a/lxd/container_metadata.go b/lxd/container_metadata.go
index adfce53c57..78b7f182c9 100644
--- a/lxd/container_metadata.go
+++ b/lxd/container_metadata.go
@@ -10,20 +10,26 @@ import (
"path/filepath"
"strings"
- "gopkg.in/yaml.v2"
-
"github.com/gorilla/mux"
+ "gopkg.in/yaml.v2"
+ "github.com/lxc/lxd/lxd/instance"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
)
func containerMetadataGet(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
@@ -75,11 +81,17 @@ func containerMetadataGet(d *Daemon, r *http.Request) Response {
}
func containerMetadataPut(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
@@ -124,11 +136,17 @@ func containerMetadataPut(d *Daemon, r *http.Request) Response {
// Return a list of templates used in a container or the content of a template
func containerMetadataTemplatesGet(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
@@ -213,11 +231,17 @@ func containerMetadataTemplatesGet(d *Daemon, r *http.Request) Response {
// Add a container template file
func containerMetadataTemplatesPostPut(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
@@ -280,12 +304,18 @@ func containerMetadataTemplatesPostPut(d *Daemon, r *http.Request) Response {
// Delete a container template
func containerMetadataTemplatesDelete(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
diff --git a/lxd/container_patch.go b/lxd/container_patch.go
index 18f454dcf0..c7be6f8f64 100644
--- a/lxd/container_patch.go
+++ b/lxd/container_patch.go
@@ -6,11 +6,13 @@ import (
"fmt"
"io/ioutil"
"net/http"
+ "strings"
"github.com/gorilla/mux"
"github.com/lxc/lxd/lxd/db"
deviceConfig "github.com/lxc/lxd/lxd/device/config"
+ "github.com/lxc/lxd/lxd/instance"
"github.com/lxc/lxd/lxd/util"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
@@ -18,13 +20,19 @@ import (
)
func containerPatch(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
// Get the container
name := mux.Vars(r)["name"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
diff --git a/lxd/container_post.go b/lxd/container_post.go
index 3865b646c1..e030133472 100644
--- a/lxd/container_post.go
+++ b/lxd/container_post.go
@@ -6,6 +6,7 @@ import (
"fmt"
"io/ioutil"
"net/http"
+ "strings"
"github.com/gorilla/mux"
"github.com/pborman/uuid"
@@ -14,6 +15,7 @@ import (
lxd "github.com/lxc/lxd/client"
"github.com/lxc/lxd/lxd/cluster"
"github.com/lxc/lxd/lxd/db"
+ "github.com/lxc/lxd/lxd/instance"
driver "github.com/lxc/lxd/lxd/storage"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
@@ -27,6 +29,12 @@ var internalClusterContainerMovedCmd = APIEndpoint{
}
func containerPost(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
@@ -70,7 +78,7 @@ func containerPost(d *Daemon, r *http.Request) Response {
targetNodeOffline = node.IsOffline(config.OfflineThreshold())
// Load source node.
- address, err := tx.ContainerNodeAddress(project, name)
+ address, err := tx.ContainerNodeAddress(project, name, instanceType)
if err != nil {
return errors.Wrap(err, "Failed to get address of container's node")
}
@@ -121,7 +129,7 @@ func containerPost(d *Daemon, r *http.Request) Response {
// and we'll either forward the request or load the container.
if targetNode == "" || !sourceNodeOffline {
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
@@ -181,7 +189,7 @@ func containerPost(d *Daemon, r *http.Request) Response {
return SmartError(err)
}
if pool.Driver == "ceph" {
- return containerPostClusteringMigrateWithCeph(d, c, project, name, req.Name, targetNode)
+ return containerPostClusteringMigrateWithCeph(d, c, project, name, req.Name, targetNode, instanceType)
}
// If this is not a ceph-based container, make sure
@@ -393,7 +401,7 @@ func containerPostClusteringMigrate(d *Daemon, c container, oldName, newName, ne
}
// Special case migrating a container backed by ceph across two cluster nodes.
-func containerPostClusteringMigrateWithCeph(d *Daemon, c container, project, oldName, newName, newNode string) Response {
+func containerPostClusteringMigrateWithCeph(d *Daemon, c container, project, oldName, newName, newNode string, instanceType instance.Type) Response {
run := func(*operation) error {
// If source node is online (i.e. we're serving the request on
// it, and c != nil), let's unmap the RBD volume locally
@@ -467,7 +475,7 @@ func containerPostClusteringMigrateWithCeph(d *Daemon, c container, project, old
// Create the container mount point on the target node
cert := d.endpoints.NetworkCert()
- client, err := cluster.ConnectIfContainerIsRemote(d.cluster, project, newName, cert)
+ client, err := cluster.ConnectIfContainerIsRemote(d.cluster, project, newName, cert, instanceType)
if err != nil {
return errors.Wrap(err, "Failed to connect to target node")
}
diff --git a/lxd/container_put.go b/lxd/container_put.go
index 27b5be9f43..6ac1426f96 100644
--- a/lxd/container_put.go
+++ b/lxd/container_put.go
@@ -4,11 +4,13 @@ import (
"encoding/json"
"fmt"
"net/http"
+ "strings"
"github.com/gorilla/mux"
"github.com/lxc/lxd/lxd/db"
deviceConfig "github.com/lxc/lxd/lxd/device/config"
+ "github.com/lxc/lxd/lxd/instance"
"github.com/lxc/lxd/lxd/state"
"github.com/lxc/lxd/lxd/util"
"github.com/lxc/lxd/shared"
@@ -21,13 +23,19 @@ import (
* the named snapshot
*/
func containerPut(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
// Get the container
name := mux.Vars(r)["name"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
diff --git a/lxd/container_snapshot.go b/lxd/container_snapshot.go
index 67fe2e71d4..d800cb0590 100644
--- a/lxd/container_snapshot.go
+++ b/lxd/container_snapshot.go
@@ -13,6 +13,7 @@ import (
"github.com/gorilla/mux"
"github.com/lxc/lxd/lxd/db"
+ "github.com/lxc/lxd/lxd/instance"
"github.com/lxc/lxd/lxd/util"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
@@ -20,11 +21,17 @@ import (
)
func containerSnapshotsGet(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
cname := mux.Vars(r)["name"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, cname)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, cname, instanceType)
if err != nil {
return SmartError(err)
}
@@ -81,11 +88,17 @@ func containerSnapshotsGet(d *Daemon, r *http.Request) Response {
}
func containerSnapshotsPost(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
@@ -170,11 +183,17 @@ func containerSnapshotsPost(d *Daemon, r *http.Request) Response {
}
func containerSnapshotHandler(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
containerName := mux.Vars(r)["name"]
snapshotName := mux.Vars(r)["snapshotName"]
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, containerName)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, containerName, instanceType)
if err != nil {
return SmartError(err)
}
diff --git a/lxd/container_state.go b/lxd/container_state.go
index 8b86a0a362..c47309546a 100644
--- a/lxd/container_state.go
+++ b/lxd/container_state.go
@@ -4,21 +4,29 @@ import (
"encoding/json"
"fmt"
"net/http"
+ "strings"
"time"
"github.com/gorilla/mux"
"github.com/lxc/lxd/lxd/db"
+ "github.com/lxc/lxd/lxd/instance"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
)
func containerState(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
@@ -39,11 +47,17 @@ func containerState(d *Daemon, r *http.Request) Response {
}
func containerStatePut(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
name := mux.Vars(r)["name"]
// Handle requests targeted to a container on a different node
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
return SmartError(err)
}
diff --git a/lxd/containers_post.go b/lxd/containers_post.go
index 106c1e3feb..e6858c0077 100644
--- a/lxd/containers_post.go
+++ b/lxd/containers_post.go
@@ -864,7 +864,7 @@ func clusterCopyContainerInternal(d *Daemon, source container, project string, r
var err error
// Load source node.
- nodeAddress, err = tx.ContainerNodeAddress(project, name)
+ nodeAddress, err = tx.ContainerNodeAddress(project, name, source.Type())
if err != nil {
return errors.Wrap(err, "Failed to get address of container's node")
}
diff --git a/lxd/images.go b/lxd/images.go
index 2ea8330c9f..383ae47643 100644
--- a/lxd/images.go
+++ b/lxd/images.go
@@ -23,12 +23,12 @@ import (
"github.com/gorilla/mux"
"github.com/pkg/errors"
-
"gopkg.in/yaml.v2"
lxd "github.com/lxc/lxd/client"
"github.com/lxc/lxd/lxd/cluster"
"github.com/lxc/lxd/lxd/db"
+ "github.com/lxc/lxd/lxd/instance"
"github.com/lxc/lxd/lxd/node"
"github.com/lxc/lxd/lxd/state"
"github.com/lxc/lxd/lxd/task"
@@ -36,12 +36,11 @@ import (
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/ioprogress"
+ log "github.com/lxc/lxd/shared/log15"
"github.com/lxc/lxd/shared/logger"
"github.com/lxc/lxd/shared/logging"
"github.com/lxc/lxd/shared/osarch"
"github.com/lxc/lxd/shared/version"
-
- log "github.com/lxc/lxd/shared/log15"
)
var imagesCmd = APIEndpoint{
@@ -637,6 +636,12 @@ func imageCreateInPool(d *Daemon, info *api.Image, storagePool string) error {
}
func imagesPost(d *Daemon, r *http.Request) Response {
+ // Instance type.
+ instanceType := instance.TypeAny
+ if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+ instanceType = instance.TypeContainer
+ }
+
project := projectParam(r)
var err error
@@ -698,7 +703,7 @@ func imagesPost(d *Daemon, r *http.Request) Response {
if name != "" {
post.Seek(0, 0)
r.Body = post
- response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name)
+ response, err := ForwardedResponseIfContainerIsRemote(d, r, project, name, instanceType)
if err != nil {
cleanup(builddir, post)
return SmartError(err)
From 74f72089d45036705b0d996efef7326d8de82b7f Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 11 Sep 2019 17:30:59 +0100
Subject: [PATCH 5/9] lxd/instance/instance: Uses API instance types for string
comparison
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/instance/instance.go | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/lxd/instance/instance.go b/lxd/instance/instance.go
index 2c3a497e87..2ae566c1b3 100644
--- a/lxd/instance/instance.go
+++ b/lxd/instance/instance.go
@@ -2,6 +2,8 @@ package instance
import (
"fmt"
+
+ "github.com/lxc/lxd/shared/api"
)
// Type indicates the type of instance.
@@ -19,7 +21,7 @@ const (
// If an invalid name is supplied an error will be returned.
func New(name string) (Type, error) {
// If "container" or "" is supplied, return type as TypeContainer.
- if name == "container" || name == "" {
+ if api.InstanceType(name) == api.InstanceTypeContainer || name == "" {
return TypeContainer, nil
}
@@ -30,7 +32,7 @@ func New(name string) (Type, error) {
// Returns empty string if value is not a valid instance type.
func (instanceType Type) String() string {
if instanceType == TypeContainer {
- return "container"
+ return string(api.InstanceTypeContainer)
}
return ""
From 3835468f0edfd6531d346b45ce464b06e71029cc Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Wed, 11 Sep 2019 17:31:26 +0100
Subject: [PATCH 6/9] shared/api: Adds new instance types
- Removes earlier API extension comments.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
shared/api/instance.go | 132 ++++++++++++++++++++++++++++++++
shared/api/instance_backup.go | 31 ++++++++
shared/api/instance_console.go | 17 ++++
shared/api/instance_exec.go | 26 +++++++
shared/api/instance_snapshot.go | 60 +++++++++++++++
shared/api/instance_state.go | 84 ++++++++++++++++++++
6 files changed, 350 insertions(+)
create mode 100644 shared/api/instance.go
create mode 100644 shared/api/instance_backup.go
create mode 100644 shared/api/instance_console.go
create mode 100644 shared/api/instance_exec.go
create mode 100644 shared/api/instance_snapshot.go
create mode 100644 shared/api/instance_state.go
diff --git a/shared/api/instance.go b/shared/api/instance.go
new file mode 100644
index 0000000000..7806b71adf
--- /dev/null
+++ b/shared/api/instance.go
@@ -0,0 +1,132 @@
+package api
+
+import (
+ "time"
+)
+
+// InstanceType represents the type if instance being returned or requested via the API.
+type InstanceType string
+
+// InstanceTypeAny defines the instance type value for requesting any instance type.
+const InstanceTypeAny = InstanceType("")
+
+// InstanceTypeContainer defines the instance type value for a container.
+const InstanceTypeContainer = InstanceType("container")
+
+// InstancesPost represents the fields available for a new LXD instance.
+//
+// API extension: instances
+type InstancesPost struct {
+ InstancePut `yaml:",inline"`
+
+ Name string `json:"name" yaml:"name"`
+ Source InstanceSource `json:"source" yaml:"source"`
+ InstanceType string `json:"instance_type" yaml:"instance_type"`
+ Type InstanceType `json:"type" yaml:"type"`
+}
+
+// InstancePost represents the fields required to rename/move a LXD instance.
+//
+// API extension: instances
+type InstancePost struct {
+ Name string `json:"name" yaml:"name"`
+ Migration bool `json:"migration" yaml:"migration"`
+ Live bool `json:"live" yaml:"live"`
+ ContainerOnly bool `json:"container_only" yaml:"container_only"`
+ Target *InstancePostTarget `json:"target" yaml:"target"`
+}
+
+// InstancePostTarget represents the migration target host and operation.
+//
+// API extension: instances
+type InstancePostTarget struct {
+ Certificate string `json:"certificate" yaml:"certificate"`
+ Operation string `json:"operation,omitempty" yaml:"operation,omitempty"`
+ Websockets map[string]string `json:"secrets,omitempty" yaml:"secrets,omitempty"`
+}
+
+// InstancePut represents the modifiable fields of a LXD instance.
+//
+// API extension: instances
+type InstancePut struct {
+ Architecture string `json:"architecture" yaml:"architecture"`
+ Config map[string]string `json:"config" yaml:"config"`
+ Devices map[string]map[string]string `json:"devices" yaml:"devices"`
+ Ephemeral bool `json:"ephemeral" yaml:"ephemeral"`
+ Profiles []string `json:"profiles" yaml:"profiles"`
+ Restore string `json:"restore,omitempty" yaml:"restore,omitempty"`
+ Stateful bool `json:"stateful" yaml:"stateful"`
+ Description string `json:"description" yaml:"description"`
+}
+
+// Instance represents a LXD instance.
+//
+// API extension: instances
+type Instance struct {
+ InstancePut `yaml:",inline"`
+
+ CreatedAt time.Time `json:"created_at" yaml:"created_at"`
+ ExpandedConfig map[string]string `json:"expanded_config" yaml:"expanded_config"`
+ ExpandedDevices map[string]map[string]string `json:"expanded_devices" yaml:"expanded_devices"`
+ Name string `json:"name" yaml:"name"`
+ Status string `json:"status" yaml:"status"`
+ StatusCode StatusCode `json:"status_code" yaml:"status_code"`
+ LastUsedAt time.Time `json:"last_used_at" yaml:"last_used_at"`
+ Location string `json:"location" yaml:"location"`
+ Type string `json:"type" yaml:"type"`
+}
+
+// InstanceFull is a combination of Instance, InstanceBackup, InstanceState and InstanceSnapshot.
+//
+// API extension: instances
+type InstanceFull struct {
+ Instance `yaml:",inline"`
+
+ Backups []InstanceBackup `json:"backups" yaml:"backups"`
+ State *InstanceState `json:"state" yaml:"state"`
+ Snapshots []InstanceSnapshot `json:"snapshots" yaml:"snapshots"`
+}
+
+// Writable converts a full Instance struct into a InstancePut struct (filters read-only fields).
+//
+// API extension: instances
+func (c *Instance) Writable() InstancePut {
+ return c.InstancePut
+}
+
+// IsActive checks whether the instance state indicates the instance is active.
+//
+// API extension: instances
+func (c Instance) IsActive() bool {
+ switch c.StatusCode {
+ case Stopped:
+ return false
+ case Error:
+ return false
+ default:
+ return true
+ }
+}
+
+// InstanceSource represents the creation source for a new instance.
+//
+// API extension: instances
+type InstanceSource struct {
+ Type string `json:"type" yaml:"type"`
+ Certificate string `json:"certificate" yaml:"certificate"`
+ Alias string `json:"alias,omitempty" yaml:"alias,omitempty"`
+ Fingerprint string `json:"fingerprint,omitempty" yaml:"fingerprint,omitempty"`
+ Properties map[string]string `json:"properties,omitempty" yaml:"properties,omitempty"`
+ Server string `json:"server,omitempty" yaml:"server,omitempty"`
+ Secret string `json:"secret,omitempty" yaml:"secret,omitempty"`
+ Protocol string `json:"protocol,omitempty" yaml:"protocol,omitempty"`
+ BaseImage string `json:"base-image,omitempty" yaml:"base-image,omitempty"`
+ Mode string `json:"mode,omitempty" yaml:"mode,omitempty"`
+ Operation string `json:"operation,omitempty" yaml:"operation,omitempty"`
+ Websockets map[string]string `json:"secrets,omitempty" yaml:"secrets,omitempty"`
+ Source string `json:"source,omitempty" yaml:"source,omitempty"`
+ Live bool `json:"live,omitempty" yaml:"live,omitempty"`
+ ContainerOnly bool `json:"container_only,omitempty" yaml:"container_only,omitempty"`
+ Refresh bool `json:"refresh,omitempty" yaml:"refresh,omitempty"`
+ Project string `json:"project,omitempty" yaml:"project,omitempty"`
+}
diff --git a/shared/api/instance_backup.go b/shared/api/instance_backup.go
new file mode 100644
index 0000000000..64b282337b
--- /dev/null
+++ b/shared/api/instance_backup.go
@@ -0,0 +1,31 @@
+package api
+
+import "time"
+
+// InstanceBackupsPost represents the fields available for a new LXD instance backup.
+//
+// API extension: instances
+type InstanceBackupsPost struct {
+ Name string `json:"name" yaml:"name"`
+ ExpiresAt time.Time `json:"expires_at" yaml:"expires_at"`
+ ContainerOnly bool `json:"container_only" yaml:"container_only"`
+ OptimizedStorage bool `json:"optimized_storage" yaml:"optimized_storage"`
+}
+
+// InstanceBackup represents a LXD instance backup.
+//
+// API extension: instances
+type InstanceBackup struct {
+ Name string `json:"name" yaml:"name"`
+ CreatedAt time.Time `json:"created_at" yaml:"created_at"`
+ ExpiresAt time.Time `json:"expires_at" yaml:"expires_at"`
+ ContainerOnly bool `json:"container_only" yaml:"container_only"`
+ OptimizedStorage bool `json:"optimized_storage" yaml:"optimized_storage"`
+}
+
+// InstanceBackupPost represents the fields available for the renaming of a instance backup.
+//
+// API extension: instances
+type InstanceBackupPost struct {
+ Name string `json:"name" yaml:"name"`
+}
diff --git a/shared/api/instance_console.go b/shared/api/instance_console.go
new file mode 100644
index 0000000000..614beb1245
--- /dev/null
+++ b/shared/api/instance_console.go
@@ -0,0 +1,17 @@
+package api
+
+// InstanceConsoleControl represents a message on the instance console "control" socket.
+//
+// API extension: instances
+type InstanceConsoleControl struct {
+ Command string `json:"command" yaml:"command"`
+ Args map[string]string `json:"args" yaml:"args"`
+}
+
+// InstanceConsolePost represents a LXD instance console request.
+//
+// API extension: instances
+type InstanceConsolePost struct {
+ Width int `json:"width" yaml:"width"`
+ Height int `json:"height" yaml:"height"`
+}
diff --git a/shared/api/instance_exec.go b/shared/api/instance_exec.go
new file mode 100644
index 0000000000..4579b2c89d
--- /dev/null
+++ b/shared/api/instance_exec.go
@@ -0,0 +1,26 @@
+package api
+
+// InstanceExecControl represents a message on the instance exec "control" socket.
+//
+// API extension: instances
+type InstanceExecControl struct {
+ Command string `json:"command" yaml:"command"`
+ Args map[string]string `json:"args" yaml:"args"`
+ Signal int `json:"signal" yaml:"signal"`
+}
+
+// InstanceExecPost represents a LXD instance exec request.
+//
+// API extension: instances
+type InstanceExecPost struct {
+ Command []string `json:"command" yaml:"command"`
+ WaitForWS bool `json:"wait-for-websocket" yaml:"wait-for-websocket"`
+ Interactive bool `json:"interactive" yaml:"interactive"`
+ Environment map[string]string `json:"environment" yaml:"environment"`
+ Width int `json:"width" yaml:"width"`
+ Height int `json:"height" yaml:"height"`
+ RecordOutput bool `json:"record-output" yaml:"record-output"`
+ User uint32 `json:"user" yaml:"user"`
+ Group uint32 `json:"group" yaml:"group"`
+ Cwd string `json:"cwd" yaml:"cwd"`
+}
diff --git a/shared/api/instance_snapshot.go b/shared/api/instance_snapshot.go
new file mode 100644
index 0000000000..bdd93544b2
--- /dev/null
+++ b/shared/api/instance_snapshot.go
@@ -0,0 +1,60 @@
+package api
+
+import (
+ "time"
+)
+
+// InstanceSnapshotsPost represents the fields available for a new LXD instance snapshot.
+//
+// API extension: instances
+type InstanceSnapshotsPost struct {
+ Name string `json:"name" yaml:"name"`
+ Stateful bool `json:"stateful" yaml:"stateful"`
+
+ // API extension: snapshot_expiry_creation
+ ExpiresAt *time.Time `json:"expires_at" yaml:"expires_at"`
+}
+
+// InstanceSnapshotPost represents the fields required to rename/move a LXD instance snapshot.
+//
+// API extension: instances
+type InstanceSnapshotPost struct {
+ Name string `json:"name" yaml:"name"`
+ Migration bool `json:"migration" yaml:"migration"`
+ Target *InstancePostTarget `json:"target" yaml:"target"`
+ Live bool `json:"live,omitempty" yaml:"live,omitempty"`
+}
+
+// InstanceSnapshotPut represents the modifiable fields of a LXD instance snapshot.
+//
+// API extension: instances
+type InstanceSnapshotPut struct {
+ Architecture string `json:"architecture" yaml:"architecture"`
+ Config map[string]string `json:"config" yaml:"config"`
+ Devices map[string]map[string]string `json:"devices" yaml:"devices"`
+ Ephemeral bool `json:"ephemeral" yaml:"ephemeral"`
+ Profiles []string `json:"profiles" yaml:"profiles"`
+ ExpiresAt time.Time `json:"expires_at" yaml:"expires_at"`
+}
+
+// InstanceSnapshot represents a LXD instance snapshot.
+//
+// API extension: instances
+type InstanceSnapshot struct {
+ InstanceSnapshotPut `yaml:",inline"`
+
+ CreatedAt time.Time `json:"created_at" yaml:"created_at"`
+ ExpandedConfig map[string]string `json:"expanded_config" yaml:"expanded_config"`
+ ExpandedDevices map[string]map[string]string `json:"expanded_devices" yaml:"expanded_devices"`
+ LastUsedAt time.Time `json:"last_used_at" yaml:"last_used_at"`
+ Name string `json:"name" yaml:"name"`
+ Stateful bool `json:"stateful" yaml:"stateful"`
+}
+
+// Writable converts a full InstanceSnapshot struct into a InstanceSnapshotPut struct
+// (filters read-only fields).
+//
+// API extension: instances
+func (c *InstanceSnapshot) Writable() InstanceSnapshotPut {
+ return c.InstanceSnapshotPut
+}
diff --git a/shared/api/instance_state.go b/shared/api/instance_state.go
new file mode 100644
index 0000000000..cd7823cbac
--- /dev/null
+++ b/shared/api/instance_state.go
@@ -0,0 +1,84 @@
+package api
+
+// InstanceStatePut represents the modifiable fields of a LXD instance's state.
+//
+// API extension: instances
+type InstanceStatePut struct {
+ Action string `json:"action" yaml:"action"`
+ Timeout int `json:"timeout" yaml:"timeout"`
+ Force bool `json:"force" yaml:"force"`
+ Stateful bool `json:"stateful" yaml:"stateful"`
+}
+
+// InstanceState represents a LXD instance's state.
+//
+// API extension: instances
+type InstanceState struct {
+ Status string `json:"status" yaml:"status"`
+ StatusCode StatusCode `json:"status_code" yaml:"status_code"`
+ Disk map[string]InstanceStateDisk `json:"disk" yaml:"disk"`
+ Memory InstanceStateMemory `json:"memory" yaml:"memory"`
+ Network map[string]InstanceStateNetwork `json:"network" yaml:"network"`
+ Pid int64 `json:"pid" yaml:"pid"`
+ Processes int64 `json:"processes" yaml:"processes"`
+ CPU InstanceStateCPU `json:"cpu" yaml:"cpu"`
+}
+
+// InstanceStateDisk represents the disk information section of a LXD instance's state.
+//
+// API extension: instances
+type InstanceStateDisk struct {
+ Usage int64 `json:"usage" yaml:"usage"`
+}
+
+// InstanceStateCPU represents the cpu information section of a LXD instance's state.
+//
+// API extension: instances
+type InstanceStateCPU struct {
+ Usage int64 `json:"usage" yaml:"usage"`
+}
+
+// InstanceStateMemory represents the memory information section of a LXD instance's state.
+//
+// API extension: instances
+type InstanceStateMemory struct {
+ Usage int64 `json:"usage" yaml:"usage"`
+ UsagePeak int64 `json:"usage_peak" yaml:"usage_peak"`
+ SwapUsage int64 `json:"swap_usage" yaml:"swap_usage"`
+ SwapUsagePeak int64 `json:"swap_usage_peak" yaml:"swap_usage_peak"`
+}
+
+// InstanceStateNetwork represents the network information section of a LXD instance's state.
+//
+// API extension: instances
+type InstanceStateNetwork struct {
+ Addresses []InstanceStateNetworkAddress `json:"addresses" yaml:"addresses"`
+ Counters InstanceStateNetworkCounters `json:"counters" yaml:"counters"`
+ Hwaddr string `json:"hwaddr" yaml:"hwaddr"`
+ HostName string `json:"host_name" yaml:"host_name"`
+ Mtu int `json:"mtu" yaml:"mtu"`
+ State string `json:"state" yaml:"state"`
+ Type string `json:"type" yaml:"type"`
+}
+
+// InstanceStateNetworkAddress represents a network address as part of the network section of a LXD
+// instance's state.
+//
+// API extension: instances
+type InstanceStateNetworkAddress struct {
+ Family string `json:"family" yaml:"family"`
+ Address string `json:"address" yaml:"address"`
+ Netmask string `json:"netmask" yaml:"netmask"`
+ Scope string `json:"scope" yaml:"scope"`
+}
+
+// InstanceStateNetworkCounters represents packet counters as part of the network section of a LXD
+// instance's state.
+//
+// API extension: instances
+type InstanceStateNetworkCounters struct {
+ BytesReceived int64 `json:"bytes_received" yaml:"bytes_received"`
+ BytesSent int64 `json:"bytes_sent" yaml:"bytes_sent"`
+ PacketsReceived int64 `json:"packets_received" yaml:"packets_received"`
+ PacketsSent int64 `json:"packets_sent" yaml:"packets_sent"`
+}
From 4fcc56d0bd0008b5c6e9cbe213f34947bcc8bd52 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 12 Sep 2019 11:57:02 +0100
Subject: [PATCH 7/9] client/connection: Adds connect functions that return
InstanceServer type
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
client/connection.go | 30 +++++++++++++++++++++++++-----
1 file changed, 25 insertions(+), 5 deletions(-)
diff --git a/client/connection.go b/client/connection.go
index c9a25e1066..6cfa0fb199 100644
--- a/client/connection.go
+++ b/client/connection.go
@@ -51,14 +51,14 @@ type ConnectionArgs struct {
SkipGetServer bool
}
-// ConnectLXD lets you connect to a remote LXD daemon over HTTPs.
+// ConnectLXDInstance lets you connect to a remote LXD daemon over HTTPs.
//
// A client certificate (TLSClientCert) and key (TLSClientKey) must be provided.
//
// If connecting to a LXD daemon running in PKI mode, the PKI CA (TLSCA) must also be provided.
//
// Unless the remote server is trusted by the system CA, the remote certificate must be provided (TLSServerCert).
-func ConnectLXD(url string, args *ConnectionArgs) (ContainerServer, error) {
+func ConnectLXDInstance(url string, args *ConnectionArgs) (InstanceServer, error) {
logger.Debugf("Connecting to a remote LXD over HTTPs")
// Cleanup URL
@@ -67,12 +67,23 @@ func ConnectLXD(url string, args *ConnectionArgs) (ContainerServer, error) {
return httpsLXD(url, args)
}
-// ConnectLXDUnix lets you connect to a remote LXD daemon over a local unix socket.
+// ConnectLXD lets you connect to a remote LXD daemon over HTTPs.
+//
+// A client certificate (TLSClientCert) and key (TLSClientKey) must be provided.
+//
+// If connecting to a LXD daemon running in PKI mode, the PKI CA (TLSCA) must also be provided.
+//
+// Unless the remote server is trusted by the system CA, the remote certificate must be provided (TLSServerCert).
+func ConnectLXD(url string, args *ConnectionArgs) (ContainerServer, error) {
+ return ConnectLXDInstance(url, args)
+}
+
+// ConnectLXDInstanceUnix lets you connect to a remote LXD daemon over a local unix socket.
//
// If the path argument is empty, then $LXD_SOCKET will be used, if
// unset $LXD_DIR/unix.socket will be used and if that one isn't set
// either, then the path will default to /var/lib/lxd/unix.socket.
-func ConnectLXDUnix(path string, args *ConnectionArgs) (ContainerServer, error) {
+func ConnectLXDInstanceUnix(path string, args *ConnectionArgs) (InstanceServer, error) {
logger.Debugf("Connecting to a local LXD over a Unix socket")
// Use empty args if not specified
@@ -122,6 +133,15 @@ func ConnectLXDUnix(path string, args *ConnectionArgs) (ContainerServer, error)
return &server, nil
}
+// ConnectLXDUnix lets you connect to a remote LXD daemon over a local unix socket.
+//
+// If the path argument is empty, then $LXD_SOCKET will be used, if
+// unset $LXD_DIR/unix.socket will be used and if that one isn't set
+// either, then the path will default to /var/lib/lxd/unix.socket.
+func ConnectLXDUnix(path string, args *ConnectionArgs) (ContainerServer, error) {
+ return ConnectLXDInstanceUnix(path, args)
+}
+
// ConnectPublicLXD lets you connect to a remote public LXD daemon over HTTPs.
//
// Unless the remote server is trusted by the system CA, the remote certificate must be provided (TLSServerCert).
@@ -170,7 +190,7 @@ func ConnectSimpleStreams(url string, args *ConnectionArgs) (ImageServer, error)
}
// Internal function called by ConnectLXD and ConnectPublicLXD
-func httpsLXD(url string, args *ConnectionArgs) (ContainerServer, error) {
+func httpsLXD(url string, args *ConnectionArgs) (InstanceServer, error) {
// Use empty args if not specified
if args == nil {
args = &ConnectionArgs{}
From 056d14da15c48a1eb82cedf68a6f9375fcd77e6a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 12 Sep 2019 11:57:49 +0100
Subject: [PATCH 8/9] client/interfaces: Creates InstanceServer interface
- Creates new arg types for instances and aliases existing types.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
client/interfaces.go | 68 +++++++++++++++++++++++++++++++++-----------
1 file changed, 52 insertions(+), 16 deletions(-)
diff --git a/client/interfaces.go b/client/interfaces.go
index 6e8cc7df3e..ae5cf82c9b 100644
--- a/client/interfaces.go
+++ b/client/interfaces.go
@@ -233,6 +233,16 @@ type ContainerServer interface {
RawOperation(method string, path string, data interface{}, queryETag string) (op Operation, ETag string, err error)
}
+// The InstanceServer type represents a full featured LXD server with Instances support.
+//
+// API extension: instances
+type InstanceServer interface {
+ ContainerServer
+
+ UseInstanceTarget(name string) (client InstanceServer)
+ UseInstanceProject(name string) (client InstanceServer)
+}
+
// The ConnectionInfo struct represents general information for a connection
type ConnectionInfo struct {
Addresses []string
@@ -243,8 +253,8 @@ type ConnectionInfo struct {
Project string
}
-// The ContainerBackupArgs struct is used when creating a container from a backup
-type ContainerBackupArgs struct {
+// The InstanceBackupArgs struct is used when creating a container from a backup
+type InstanceBackupArgs struct {
// The backup file
BackupFile io.Reader
@@ -252,6 +262,9 @@ type ContainerBackupArgs struct {
PoolName string
}
+// The ContainerBackupArgs struct is used when creating a container from a backup
+type ContainerBackupArgs InstanceBackupArgs
+
// The BackupFileRequest struct is used for a backup download request
type BackupFileRequest struct {
// Writer for the backup file
@@ -356,8 +369,8 @@ type StoragePoolVolumeMoveArgs struct {
StoragePoolVolumeCopyArgs
}
-// The ContainerCopyArgs struct is used to pass additional options during container copy
-type ContainerCopyArgs struct {
+// The InstanceCopyArgs struct is used to pass additional options during container copy
+type InstanceCopyArgs struct {
// If set, the container will be renamed on copy
Name string
@@ -375,8 +388,11 @@ type ContainerCopyArgs struct {
Refresh bool
}
-// The ContainerSnapshotCopyArgs struct is used to pass additional options during container copy
-type ContainerSnapshotCopyArgs struct {
+// The ContainerCopyArgs struct is used to pass additional options during container copy
+type ContainerCopyArgs InstanceCopyArgs
+
+// The InstanceSnapshotCopyArgs struct is used to pass additional options during container copy
+type InstanceSnapshotCopyArgs struct {
// If set, the container will be renamed on copy
Name string
@@ -388,9 +404,12 @@ type ContainerSnapshotCopyArgs struct {
Live bool
}
-// The ContainerConsoleArgs struct is used to pass additional options during a
+// The ContainerSnapshotCopyArgs struct is used to pass additional options during container copy
+type ContainerSnapshotCopyArgs InstanceSnapshotCopyArgs
+
+// The InstanceConsoleArgs struct is used to pass additional options during a
// container console session
-type ContainerConsoleArgs struct {
+type InstanceConsoleArgs struct {
// Bidirectional fd to pass to the container
Terminal io.ReadWriteCloser
@@ -401,13 +420,21 @@ type ContainerConsoleArgs struct {
ConsoleDisconnect chan bool
}
-// The ContainerConsoleLogArgs struct is used to pass additional options during a
+// The ContainerConsoleArgs struct is used to pass additional options during a
+// container console session
+type ContainerConsoleArgs InstanceConsoleArgs
+
+// The InstanceConsoleLogArgs struct is used to pass additional options during a
// container console log request
-type ContainerConsoleLogArgs struct {
+type InstanceConsoleLogArgs struct {
}
-// The ContainerExecArgs struct is used to pass additional options during container exec
-type ContainerExecArgs struct {
+// The ContainerConsoleLogArgs struct is used to pass additional options during a
+// container console log request
+type ContainerConsoleLogArgs InstanceConsoleLogArgs
+
+// The InstanceExecArgs struct is used to pass additional options during container exec
+type InstanceExecArgs struct {
// Standard input
Stdin io.ReadCloser
@@ -424,8 +451,11 @@ type ContainerExecArgs struct {
DataDone chan bool
}
-// The ContainerFileArgs struct is used to pass the various options for a container file upload
-type ContainerFileArgs struct {
+// The ContainerExecArgs struct is used to pass additional options during container exec
+type ContainerExecArgs InstanceExecArgs
+
+// The InstanceFileArgs struct is used to pass the various options for a container file upload
+type InstanceFileArgs struct {
// File content
Content io.ReadSeeker
@@ -445,8 +475,11 @@ type ContainerFileArgs struct {
WriteMode string
}
-// The ContainerFileResponse struct is used as part of the response for a container file download
-type ContainerFileResponse struct {
+// The ContainerFileArgs struct is used to pass the various options for a container file upload
+type ContainerFileArgs InstanceFileArgs
+
+// The InstanceFileResponse struct is used as part of the response for a container file download
+type InstanceFileResponse struct {
// User id that owns the file
UID int64
@@ -462,3 +495,6 @@ type ContainerFileResponse struct {
// If a directory, the list of files inside it
Entries []string
}
+
+// The ContainerFileResponse struct is used as part of the response for a container file download
+type ContainerFileResponse InstanceFileResponse
From fd53023acfd4a307a6263c79e97f299d75232639 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Thu, 12 Sep 2019 11:58:41 +0100
Subject: [PATCH 9/9] client/lxd/server: Adds UseInstanceTarget and
UseInstanceProject functions
And aliases existing functions to return old type.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
client/lxd_server.go | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/client/lxd_server.go b/client/lxd_server.go
index 4cc20cc1c6..c1f3ec656c 100644
--- a/client/lxd_server.go
+++ b/client/lxd_server.go
@@ -89,8 +89,8 @@ func (r *ProtocolLXD) GetServerResources() (*api.Resources, error) {
return &resources, nil
}
-// UseProject returns a client that will use a specific project.
-func (r *ProtocolLXD) UseProject(name string) ContainerServer {
+// UseInstanceProject returns a client that will use a specific project.
+func (r *ProtocolLXD) UseInstanceProject(name string) InstanceServer {
return &ProtocolLXD{
server: r.server,
http: r.http,
@@ -106,10 +106,15 @@ func (r *ProtocolLXD) UseProject(name string) ContainerServer {
}
}
-// UseTarget returns a client that will target a specific cluster member.
+// UseProject returns a client that will use a specific project.
+func (r *ProtocolLXD) UseProject(name string) ContainerServer {
+ return ContainerServer(r.UseInstanceProject(name))
+}
+
+// UseInstanceTarget returns a client that will target a specific cluster member.
// Use this member-specific operations such as specific container
// placement, preparing a new storage pool or network, ...
-func (r *ProtocolLXD) UseTarget(name string) ContainerServer {
+func (r *ProtocolLXD) UseInstanceTarget(name string) InstanceServer {
return &ProtocolLXD{
server: r.server,
http: r.http,
@@ -124,3 +129,10 @@ func (r *ProtocolLXD) UseTarget(name string) ContainerServer {
clusterTarget: name,
}
}
+
+// UseTarget returns a client that will target a specific cluster member.
+// Use this member-specific operations such as specific container
+// placement, preparing a new storage pool or network, ...
+func (r *ProtocolLXD) UseTarget(name string) ContainerServer {
+ return ContainerServer(r.UseInstanceTarget(name))
+}
More information about the lxc-devel
mailing list