The following pull request was submitted through Github. It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6187
This e-mail was sent by the LXC bot, direct replies will not reach the author unless they happen to be subscribed to this list. === Description (from pull-request) === Includes https://github.com/lxc/lxd/pull/6186
From 62949ba5eaec705943bdeb0adc8183958763f618 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 11 Sep 2019 09:44:29 +0100 Subject: [PATCH 01/16] lxd/instance/instance: Adds functions to convert to/from instance.Type and string Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- lxd/instance/instance.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lxd/instance/instance.go b/lxd/instance/instance.go index 10952e4821..bf15662dfc 100644 --- a/lxd/instance/instance.go +++ b/lxd/instance/instance.go @@ -1,5 +1,11 @@ package instance +import ( + "fmt" + + "github.com/lxc/lxd/shared/api" +) + // Type indicates the type of instance. type Type int @@ -9,3 +15,25 @@ const ( // TypeContainer represents a container instance type. TypeContainer = Type(0) ) + +// New validates the supplied string against the allowed types of instance and returns the internal +// representation of that type. If empty string is supplied then the type returned is TypeContainer. +// 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 == api.InstanceTypeContainer || name == "" { + return TypeContainer, nil + } + + return -1, fmt.Errorf("Invalid instance type") +} + +// String converts the internal representation of instance type to a string used in API requests. +// Returns empty string if value is not a valid instance type. +func (instanceType Type) String() string { + if instanceType == TypeContainer { + return api.InstanceTypeContainer + } + + return "" +} From e8a8d793f1480b63165c99f53096c96a2f4f2840 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <t...@tomp.uk> Date: Tue, 10 Sep 2019 13:39:19 +0100 Subject: [PATCH 02/16] api: Adds instances extension Signed-off-by: Thomas Parrott <t...@tomp.uk> --- doc/api-extensions.md | 3 +++ shared/version/api.go | 1 + 2 files changed, 4 insertions(+) diff --git a/doc/api-extensions.md b/doc/api-extensions.md index dc4ef31970..e562f4a32e 100644 --- a/doc/api-extensions.md +++ b/doc/api-extensions.md @@ -823,3 +823,6 @@ Export infiniband character device information (issm, umad, uverb) as part of th This introduces two new configuration keys `storage.images\_volume` and `storage.backups\_volume` to allow for a storage volume on an existing pool be used for storing the daemon-wide images and backups artifacts. + +## instances +This introduces the concept of instances, of which currently the only type is "container". diff --git a/shared/version/api.go b/shared/version/api.go index 201e834828..dd9979868f 100644 --- a/shared/version/api.go +++ b/shared/version/api.go @@ -164,6 +164,7 @@ var APIExtensions = []string{ "storage_shifted", "resources_infiniband", "daemon_storage", + "instances", } // APIExtensionsCount returns the number of available API extensions. From 98965fcecc04b76a67c673bc9be50e1b4d78831d Mon Sep 17 00:00:00 2001 From: Thomas Parrott <t...@tomp.uk> Date: Tue, 10 Sep 2019 14:23:16 +0100 Subject: [PATCH 03/16] shared/api/container: Adds Type to Container and ContainersPost - Defines instance type value for containers. Signed-off-by: Thomas Parrott <t...@tomp.uk> --- shared/api/container.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/shared/api/container.go b/shared/api/container.go index ed41a6e61e..88ce8341ab 100644 --- a/shared/api/container.go +++ b/shared/api/container.go @@ -4,6 +4,9 @@ import ( "time" ) +// InstanceTypeContainer defines the instance type value for a container. +const InstanceTypeContainer = "container" + // ContainersPost represents the fields available for a new LXD container type ContainersPost struct { ContainerPut `yaml:",inline"` @@ -12,6 +15,9 @@ type ContainersPost struct { Source ContainerSource `json:"source" yaml:"source"` InstanceType string `json:"instance_type" yaml:"instance_type"` + + // API extension: instances + Type string `json:"type" yaml:"type"` } // ContainerPost represents the fields required to rename/move a LXD container @@ -73,6 +79,9 @@ type Container struct { // API extension: clustering Location string `json:"location" yaml:"location"` + + // API extension: instances + Type string `json:"type" yaml:"type"` } // ContainerFull is a combination of Container, ContainerState and CotnainerSnapshot From 15a3c55747ae24de3eec16dfcf8514fb8f1928f5 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <t...@tomp.uk> Date: Tue, 10 Sep 2019 14:24:01 +0100 Subject: [PATCH 04/16] lxd/containers/post: Converts from string instance type and instance.Type - Defaults to container instance type if not supplied during POST. Signed-off-by: Thomas Parrott <t...@tomp.uk> --- lxd/containers_post.go | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/lxd/containers_post.go b/lxd/containers_post.go index 106c1e3feb..b2c6fc6382 100644 --- a/lxd/containers_post.go +++ b/lxd/containers_post.go @@ -93,11 +93,16 @@ func createFromImage(d *Daemon, project string, req *api.ContainersPost) Respons return BadRequest(fmt.Errorf("Must specify one of alias, fingerprint or properties for init from image")) } + dbType, err := instance.New(req.Type) + if err != nil { + return BadRequest(fmt.Errorf("Invalid instance type")) + } + run := func(op *operation) error { args := db.ContainerArgs{ Project: project, Config: req.Config, - Type: instance.TypeContainer, + Type: dbType, Description: req.Description, Devices: config.NewDevices(req.Devices), Ephemeral: req.Ephemeral, @@ -150,10 +155,15 @@ func createFromImage(d *Daemon, project string, req *api.ContainersPost) Respons } func createFromNone(d *Daemon, project string, req *api.ContainersPost) Response { + dbType, err := instance.New(req.Type) + if err != nil { + return BadRequest(fmt.Errorf("Invalid instance type")) + } + args := db.ContainerArgs{ Project: project, Config: req.Config, - Type: instance.TypeContainer, + Type: dbType, Description: req.Description, Devices: config.NewDevices(req.Devices), Ephemeral: req.Ephemeral, @@ -204,13 +214,18 @@ func createFromMigration(d *Daemon, project string, req *api.ContainersPost) Res req.Profiles = []string{"default"} } + dbType, err := instance.New(req.Type) + if err != nil { + return BadRequest(fmt.Errorf("Invalid instance type")) + } + // Prepare the container creation request args := db.ContainerArgs{ Project: project, Architecture: architecture, BaseImage: req.Source.BaseImage, Config: req.Config, - Type: instance.TypeContainer, + Type: dbType, Devices: config.NewDevices(req.Devices), Description: req.Description, Ephemeral: req.Ephemeral, @@ -551,12 +566,17 @@ func createFromCopy(d *Daemon, project string, req *api.ContainersPost) Response } } + dbType, err := instance.New(req.Type) + if err != nil { + return BadRequest(fmt.Errorf("Invalid instance type")) + } + args := db.ContainerArgs{ Project: targetProject, Architecture: source.Architecture(), BaseImage: req.Source.BaseImage, Config: req.Config, - Type: instance.TypeContainer, + Type: dbType, Description: req.Description, Devices: config.NewDevices(req.Devices), Ephemeral: req.Ephemeral, From ac2acfa788a7d5385045b53e01d2c63235d2585d Mon Sep 17 00:00:00 2001 From: Thomas Parrott <t...@tomp.uk> Date: Tue, 10 Sep 2019 15:31:42 +0100 Subject: [PATCH 05/16] lxd/api/internal: Set instance type from request data Signed-off-by: Thomas Parrott <t...@tomp.uk> --- lxd/api_internal.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lxd/api_internal.go b/lxd/api_internal.go index 588f75e05a..29d18c662b 100644 --- a/lxd/api_internal.go +++ b/lxd/api_internal.go @@ -900,13 +900,19 @@ func internalImport(d *Daemon, r *http.Request) Response { if err != nil { return SmartError(err) } + + dbType, err := instance.New(backup.Container.Type) + if err != nil { + return SmartError(fmt.Errorf("Invalid instance type")) + } + _, err = containerCreateInternal(d.State(), db.ContainerArgs{ Project: projectName, Architecture: arch, BaseImage: baseImage, Config: backup.Container.Config, CreationDate: backup.Container.CreatedAt, - Type: instance.TypeContainer, + Type: dbType, Description: backup.Container.Description, Devices: deviceConfig.NewDevices(backup.Container.Devices), Ephemeral: backup.Container.Ephemeral, @@ -1012,7 +1018,7 @@ func internalImport(d *Daemon, r *http.Request) Response { BaseImage: baseImage, Config: snap.Config, CreationDate: snap.CreatedAt, - Type: instance.TypeContainer, + Type: dbType, Snapshot: true, Devices: deviceConfig.NewDevices(snap.Devices), Ephemeral: snap.Ephemeral, From 28a32d75b3bc3a0e20ae2d8c964e44cced07d6ad Mon Sep 17 00:00:00 2001 From: Thomas Parrott <t...@tomp.uk> Date: Tue, 10 Sep 2019 17:15:54 +0100 Subject: [PATCH 06/16] lxd/db/instances/mapper: Updates InstanceList to use instance.TypeAny Signed-off-by: Thomas Parrott <t...@tomp.uk> --- lxd/db/instances.mapper.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lxd/db/instances.mapper.go b/lxd/db/instances.mapper.go index f5b9e5c9cf..74389ef9e7 100644 --- a/lxd/db/instances.mapper.go +++ b/lxd/db/instances.mapper.go @@ -5,10 +5,13 @@ package db import ( "database/sql" "fmt" + + "github.com/pkg/errors" + "github.com/lxc/lxd/lxd/db/cluster" "github.com/lxc/lxd/lxd/db/query" + "github.com/lxc/lxd/lxd/instance" "github.com/lxc/lxd/shared/api" - "github.com/pkg/errors" ) var _ = api.ServerEnvironment{} @@ -163,7 +166,7 @@ func (c *ClusterTx) InstanceList(filter InstanceFilter) ([]Instance, error) { if filter.Node != "" { criteria["Node"] = filter.Node } - if filter.Type != -1 { + if filter.Type != instance.TypeAny { criteria["Type"] = filter.Type } From 43c03601dca2f100782ddeab9b037a09db1c3c50 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <t...@tomp.uk> Date: Tue, 10 Sep 2019 17:16:51 +0100 Subject: [PATCH 07/16] lxd/db/containers: Updates container filtering functions to support instance.Type Signed-off-by: Thomas Parrott <t...@tomp.uk> --- lxd/db/containers.go | 54 ++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 14 deletions(-) diff --git a/lxd/db/containers.go b/lxd/db/containers.go index 78822a0f7c..068da2532a 100644 --- a/lxd/db/containers.go +++ b/lxd/db/containers.go @@ -241,22 +241,35 @@ SELECT nodes.id, nodes.address // string, to distinguish it from remote nodes. // // Containers whose node is down are addeded to the special address "0.0.0.0". -func (c *ClusterTx) ContainersListByNodeAddress(project string) (map[string][]string, error) { +func (c *ClusterTx) ContainersListByNodeAddress(project string, instanceType instance.Type) (map[string][]string, error) { offlineThreshold, err := c.NodeOfflineThreshold() if err != nil { return nil, err } - stmt := ` + args := make([]interface{}, 0, 2) // Expect up to 2 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) + } + + stmt := fmt.Sprintf(` SELECT instances.name, nodes.id, nodes.address, nodes.heartbeat FROM instances JOIN nodes ON nodes.id = instances.node_id JOIN projects ON projects.id = instances.project_id - WHERE instances.type=? - AND projects.name = ? + WHERE %s ORDER BY instances.id -` - rows, err := c.tx.Query(stmt, instance.TypeContainer, project) +`, filters.String()) + + rows, err := c.tx.Query(stmt, args...) if err != nil { return nil, err } @@ -328,16 +341,29 @@ func (c *ClusterTx) ContainerListExpanded() ([]Instance, error) { // ContainersByNodeName returns a map associating each container to the name of // its node. -func (c *ClusterTx) ContainersByNodeName(project string) (map[string]string, error) { - stmt := ` +func (c *ClusterTx) ContainersByNodeName(project string, instanceType instance.Type) (map[string]string, error) { + args := make([]interface{}, 0, 2) // Expect up to 2 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) + } + + stmt := fmt.Sprintf(` SELECT instances.name, nodes.name FROM instances JOIN nodes ON nodes.id = instances.node_id JOIN projects ON projects.id = instances.project_id - WHERE instances.type=? - AND projects.name = ? -` - rows, err := c.tx.Query(stmt, instance.TypeContainer, project) + WHERE %s +`, filters.String()) + + rows, err := c.tx.Query(stmt, args...) if err != nil { return nil, err } @@ -490,7 +516,7 @@ func (c *ClusterTx) ContainerNodeList() ([]Instance, error) { } // ContainerNodeProjectList returns all container objects on the local node within the given project. -func (c *ClusterTx) ContainerNodeProjectList(project string) ([]Instance, error) { +func (c *ClusterTx) ContainerNodeProjectList(project string, instanceType instance.Type) ([]Instance, error) { node, err := c.NodeName() if err != nil { return nil, errors.Wrap(err, "Local node name") @@ -498,7 +524,7 @@ func (c *ClusterTx) ContainerNodeProjectList(project string) ([]Instance, error) filter := InstanceFilter{ Project: project, Node: node, - Type: instance.TypeContainer, + Type: instanceType, } return c.InstanceList(filter) From cdce67eae625b3b6a3c5dff193a6a6f6ac4f37af Mon Sep 17 00:00:00 2001 From: Thomas Parrott <t...@tomp.uk> Date: Tue, 10 Sep 2019 17:17:22 +0100 Subject: [PATCH 08/16] lxd/container: Updates containerLoadNodeProjectAll to support instance.Type filtering Signed-off-by: Thomas Parrott <t...@tomp.uk> --- lxd/container.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/container.go b/lxd/container.go index a6a04c3837..cd0edf6a2e 100644 --- a/lxd/container.go +++ b/lxd/container.go @@ -1152,12 +1152,12 @@ func containerLoadNodeAll(s *state.State) ([]container, error) { } // Load all containers of this nodes under the given project. -func containerLoadNodeProjectAll(s *state.State, project string) ([]container, error) { +func containerLoadNodeProjectAll(s *state.State, project string, instanceType instance.Type) ([]container, error) { // Get all the container arguments var cts []db.Instance err := s.Cluster.Transaction(func(tx *db.ClusterTx) error { var err error - cts, err = tx.ContainerNodeProjectList(project) + cts, err = tx.ContainerNodeProjectList(project, instanceType) if err != nil { return err } From 09d2a2dc84d89141724de1aa5401d4f0f81936ea Mon Sep 17 00:00:00 2001 From: Thomas Parrott <t...@tomp.uk> Date: Tue, 10 Sep 2019 17:17:52 +0100 Subject: [PATCH 09/16] lxd/container/lxc: Adds instance Type string field population in Render() Signed-off-by: Thomas Parrott <t...@tomp.uk> --- lxd/container_lxc.go | 1 + 1 file changed, 1 insertion(+) diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go index f0fdd9c791..b1e67d781a 100644 --- a/lxd/container_lxc.go +++ b/lxd/container_lxc.go @@ -3246,6 +3246,7 @@ func (c *containerLXC) Render() (interface{}, interface{}, error) { Status: statusCode.String(), StatusCode: statusCode, Location: c.node, + Type: c.Type().String(), } ct.Description = c.description From fb28d9f01c4ef2d815a08bbc6d442e8ee72610f6 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <t...@tomp.uk> Date: Tue, 10 Sep 2019 17:18:47 +0100 Subject: [PATCH 10/16] lxd/containers/get: Makes /1.0/containers filter by instance type container - However there is still work to do in doContainersGetFromNode and doContainersFullGetFromNode to actually filter, however this requires changing the client package. Signed-off-by: Thomas Parrott <t...@tomp.uk> --- lxd/containers_get.go | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/lxd/containers_get.go b/lxd/containers_get.go index e45c458aa5..60193da982 100644 --- a/lxd/containers_get.go +++ b/lxd/containers_get.go @@ -5,17 +5,21 @@ import ( "net/http" "sort" "strconv" + "strings" "sync" "time" + "github.com/gorilla/mux" + "github.com/pkg/errors" + "github.com/lxc/lxd/lxd/cluster" "github.com/lxc/lxd/lxd/db" "github.com/lxc/lxd/lxd/db/query" + "github.com/lxc/lxd/lxd/instance" "github.com/lxc/lxd/shared" "github.com/lxc/lxd/shared/api" "github.com/lxc/lxd/shared/logger" "github.com/lxc/lxd/shared/version" - "github.com/pkg/errors" ) func containersGet(d *Daemon, r *http.Request) Response { @@ -44,6 +48,12 @@ func doContainersGet(d *Daemon, r *http.Request) (interface{}, error) { resultFullList := []*api.ContainerFull{} resultMu := sync.Mutex{} + // Instance type. + instanceType := instance.TypeAny + if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") { + instanceType = instance.TypeContainer + } + // Parse the recursion field recursionStr := r.FormValue("recursion") @@ -61,12 +71,12 @@ func doContainersGet(d *Daemon, r *http.Request) (interface{}, error) { err = d.cluster.Transaction(func(tx *db.ClusterTx) error { var err error - result, err = tx.ContainersListByNodeAddress(project) + result, err = tx.ContainersListByNodeAddress(project, instanceType) if err != nil { return err } - nodes, err = tx.ContainersByNodeName(project) + nodes, err = tx.ContainersByNodeName(project, instanceType) if err != nil { return err } @@ -80,7 +90,7 @@ func doContainersGet(d *Daemon, r *http.Request) (interface{}, error) { // Get the local containers nodeCts := map[string]container{} if recursion > 0 { - cts, err := containerLoadNodeProjectAll(d.State(), project) + cts, err := containerLoadNodeProjectAll(d.State(), project, instanceType) if err != nil { return nil, err } @@ -151,7 +161,7 @@ func doContainersGet(d *Daemon, r *http.Request) (interface{}, error) { cert := d.endpoints.NetworkCert() if recursion == 1 { - cs, err := doContainersGetFromNode(project, address, cert) + cs, err := doContainersGetFromNode(project, address, cert, instanceType) if err != nil { for _, name := range containers { resultListAppend(name, api.Container{}, err) @@ -167,7 +177,7 @@ func doContainersGet(d *Daemon, r *http.Request) (interface{}, error) { return } - cs, err := doContainersFullGetFromNode(project, address, cert) + cs, err := doContainersFullGetFromNode(project, address, cert, instanceType) if err != nil { for _, name := range containers { resultFullListAppend(name, api.ContainerFull{}, err) @@ -186,7 +196,11 @@ func doContainersGet(d *Daemon, r *http.Request) (interface{}, error) { if recursion == 0 { for _, container := range containers { - url := fmt.Sprintf("/%s/containers/%s", version.APIVersion, container) + instancePath := "instances" + if instanceType == instance.TypeContainer { + instancePath = "containers" + } + url := fmt.Sprintf("/%s/%s/%s", version.APIVersion, instancePath, container) resultString = append(resultString, url) } } else { @@ -262,7 +276,7 @@ func doContainersGet(d *Daemon, r *http.Request) (interface{}, error) { // Fetch information about the containers on the given remote node, using the // rest API and with a timeout of 30 seconds. -func doContainersGetFromNode(project, node string, cert *shared.CertInfo) ([]api.Container, error) { +func doContainersGetFromNode(project, node string, cert *shared.CertInfo, instanceType instance.Type) ([]api.Container, error) { f := func() ([]api.Container, error) { client, err := cluster.Connect(node, cert, true) if err != nil { @@ -299,7 +313,7 @@ func doContainersGetFromNode(project, node string, cert *shared.CertInfo) ([]api return containers, err } -func doContainersFullGetFromNode(project, node string, cert *shared.CertInfo) ([]api.ContainerFull, error) { +func doContainersFullGetFromNode(project, node string, cert *shared.CertInfo, instanceType instance.Type) ([]api.ContainerFull, error) { f := func() ([]api.ContainerFull, error) { client, err := cluster.Connect(node, cert, true) if err != nil { From 3d773c4da37c325297ec509279dfc8e23045d99f Mon Sep 17 00:00:00 2001 From: Thomas Parrott <t...@tomp.uk> Date: Tue, 10 Sep 2019 17:40:52 +0100 Subject: [PATCH 11/16] lxd/db/containers/test: Fixes tests Signed-off-by: Thomas Parrott <t...@tomp.uk> --- lxd/db/containers_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lxd/db/containers_test.go b/lxd/db/containers_test.go index bc2adf4fa8..ef829b9e37 100644 --- a/lxd/db/containers_test.go +++ b/lxd/db/containers_test.go @@ -313,7 +313,7 @@ func TestContainersListByNodeAddress(t *testing.T) { addContainer(t, tx, nodeID3, "c3") addContainer(t, tx, nodeID2, "c4") - result, err := tx.ContainersListByNodeAddress("default") + result, err := tx.ContainersListByNodeAddress("default", instance.TypeContainer) require.NoError(t, err) assert.Equal( t, @@ -337,7 +337,7 @@ func TestContainersByNodeName(t *testing.T) { addContainer(t, tx, nodeID2, "c1") addContainer(t, tx, nodeID1, "c2") - result, err := tx.ContainersByNodeName("default") + result, err := tx.ContainersByNodeName("default", instance.TypeContainer) require.NoError(t, err) assert.Equal( t, From 3ea17480785f7e324840d75f0d315ccd6abbf028 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 11 Sep 2019 11:11:22 +0100 Subject: [PATCH 12/16] lxd/db/containers: Adds instanceType filter to ContainerNodeAddress - Supports instance.TypeAny if no filter is needed. Signed-off-by: Thomas Parrott <thomas.parr...@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 92ae4f361700b45d696c60b45b35aab628e46f2c Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 11 Sep 2019 11:12:49 +0100 Subject: [PATCH 13/16] lxd/cluster/connect: Adds instanceType filter to ConnectIfContainerIsRemote Signed-off-by: Thomas Parrott <thomas.parr...@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 c59207c727a19c3c0ffc8449d8fd5ddb1511055e Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 11 Sep 2019 11:13:47 +0100 Subject: [PATCH 14/16] lxd/response: Adds instanceType filter to ForwardedResponseIfContainerIsRemote Signed-off-by: Thomas Parrott <thomas.parr...@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 e09015ba1146087a8f47d9d0ba6fed5db79b186f Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 11 Sep 2019 11:14:10 +0100 Subject: [PATCH 15/16] 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.parr...@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 b2c6fc6382..b032d60d1d 100644 --- a/lxd/containers_post.go +++ b/lxd/containers_post.go @@ -884,7 +884,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 ac9162a552b7c02841d2c0098f9146a48f2aff61 Mon Sep 17 00:00:00 2001 From: Thomas Parrott <thomas.parr...@canonical.com> Date: Wed, 11 Sep 2019 13:50:25 +0100 Subject: [PATCH 16/16] shared/api: Renames container files and types to instance - Aliases old Container types to maintain backwards compatibility. - Where old types contain fields that are of old types too, the new type has been duplicated. - Where old types have functions that return old types, the aliased old type has a function that calls the equivalent function on the new type and casts the result back to the old type. Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com> --- shared/api/container_console.go | 17 --- shared/api/container_state.go | 70 --------- shared/api/{container.go => instance.go} | 132 +++++++++++++---- ...container_backup.go => instance_backup.go} | 32 +++- shared/api/instance_console.go | 27 ++++ .../{container_exec.go => instance_exec.go} | 18 ++- ...ainer_snapshot.go => instance_snapshot.go} | 56 +++++-- shared/api/instance_state.go | 139 ++++++++++++++++++ 8 files changed, 353 insertions(+), 138 deletions(-) delete mode 100644 shared/api/container_console.go delete mode 100644 shared/api/container_state.go rename shared/api/{container.go => instance.go} (66%) rename shared/api/{container_backup.go => instance_backup.go} (58%) create mode 100644 shared/api/instance_console.go rename shared/api/{container_exec.go => instance_exec.go} (67%) rename shared/api/{container_snapshot.go => instance_snapshot.go} (56%) create mode 100644 shared/api/instance_state.go diff --git a/shared/api/container_console.go b/shared/api/container_console.go deleted file mode 100644 index 56aff07aa4..0000000000 --- a/shared/api/container_console.go +++ /dev/null @@ -1,17 +0,0 @@ -package api - -// ContainerConsoleControl represents a message on the container console "control" socket -// -// API extension: console -type ContainerConsoleControl struct { - Command string `json:"command" yaml:"command"` - Args map[string]string `json:"args" yaml:"args"` -} - -// ContainerConsolePost represents a LXD container console request -// -// API extension: console -type ContainerConsolePost struct { - Width int `json:"width" yaml:"width"` - Height int `json:"height" yaml:"height"` -} diff --git a/shared/api/container_state.go b/shared/api/container_state.go deleted file mode 100644 index f9e1cb9b53..0000000000 --- a/shared/api/container_state.go +++ /dev/null @@ -1,70 +0,0 @@ -package api - -// ContainerStatePut represents the modifiable fields of a LXD container's state -type ContainerStatePut 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"` -} - -// ContainerState represents a LXD container's state -type ContainerState struct { - Status string `json:"status" yaml:"status"` - StatusCode StatusCode `json:"status_code" yaml:"status_code"` - Disk map[string]ContainerStateDisk `json:"disk" yaml:"disk"` - Memory ContainerStateMemory `json:"memory" yaml:"memory"` - Network map[string]ContainerStateNetwork `json:"network" yaml:"network"` - Pid int64 `json:"pid" yaml:"pid"` - Processes int64 `json:"processes" yaml:"processes"` - - // API extension: container_cpu_time - CPU ContainerStateCPU `json:"cpu" yaml:"cpu"` -} - -// ContainerStateDisk represents the disk information section of a LXD container's state -type ContainerStateDisk struct { - Usage int64 `json:"usage" yaml:"usage"` -} - -// ContainerStateCPU represents the cpu information section of a LXD container's state -// -// API extension: container_cpu_time -type ContainerStateCPU struct { - Usage int64 `json:"usage" yaml:"usage"` -} - -// ContainerStateMemory represents the memory information section of a LXD container's state -type ContainerStateMemory 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"` -} - -// ContainerStateNetwork represents the network information section of a LXD container's state -type ContainerStateNetwork struct { - Addresses []ContainerStateNetworkAddress `json:"addresses" yaml:"addresses"` - Counters ContainerStateNetworkCounters `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"` -} - -// ContainerStateNetworkAddress represents a network address as part of the network section of a LXD container's state -type ContainerStateNetworkAddress 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"` -} - -// ContainerStateNetworkCounters represents packet counters as part of the network section of a LXD container's state -type ContainerStateNetworkCounters 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"` -} diff --git a/shared/api/container.go b/shared/api/instance.go similarity index 66% rename from shared/api/container.go rename to shared/api/instance.go index 88ce8341ab..2e5f54f9c4 100644 --- a/shared/api/container.go +++ b/shared/api/instance.go @@ -7,7 +7,22 @@ import ( // InstanceTypeContainer defines the instance type value for a container. const InstanceTypeContainer = "container" -// ContainersPost represents the fields available for a new LXD 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"` + + // API extension: instances + Type string `json:"type" yaml:"type"` +} + +// ContainersPost represents the fields available for a new LXD container, type ContainersPost struct { ContainerPut `yaml:",inline"` @@ -20,7 +35,27 @@ type ContainersPost struct { Type string `json:"type" yaml:"type"` } -// ContainerPost represents the fields required to rename/move a LXD container +// InstancePost represents the fields required to rename/move a LXD instance. +// +// API extension: instances +type InstancePost struct { + // Used for renames + Name string `json:"name" yaml:"name"` + + // Used for migration + Migration bool `json:"migration" yaml:"migration"` + + // API extension: container_stateless_copy + Live bool `json:"live" yaml:"live"` + + // API extension: container_only_migration + ContainerOnly bool `json:"container_only" yaml:"container_only"` + + // API extension: container_push_target + Target *InstancePostTarget `json:"target" yaml:"target"` +} + +// ContainerPost represents the fields required to rename/move a LXD container. type ContainerPost struct { // Used for renames Name string `json:"name" yaml:"name"` @@ -38,17 +73,24 @@ type ContainerPost struct { Target *ContainerPostTarget `json:"target" yaml:"target"` } -// ContainerPostTarget represents the migration target host and operation +// InstancePostTarget represents the migration target host and operation. // -// API extension: container_push_target -type ContainerPostTarget struct { +// 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"` } -// ContainerPut represents the modifiable fields of a LXD container -type ContainerPut struct { +// ContainerPostTarget represents the migration target host and operation. +// +// API extension: container_push_target +type ContainerPostTarget InstancePostTarget + +// 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"` @@ -63,9 +105,14 @@ type ContainerPut struct { Description string `json:"description" yaml:"description"` } -// Container represents a LXD container -type Container struct { - ContainerPut `yaml:",inline"` +// ContainerPut represents the modifiable fields of a LXD container. +type ContainerPut InstancePut + +// 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"` @@ -84,24 +131,13 @@ type Container struct { Type string `json:"type" yaml:"type"` } -// ContainerFull is a combination of Container, ContainerState and CotnainerSnapshot -// -// API extension: container_full -type ContainerFull struct { - Container `yaml:",inline"` - - Backups []ContainerBackup `json:"backups" yaml:"backups"` - State *ContainerState `json:"state" yaml:"state"` - Snapshots []ContainerSnapshot `json:"snapshots" yaml:"snapshots"` -} - -// Writable converts a full Container struct into a ContainerPut struct (filters read-only fields) -func (c *Container) Writable() ContainerPut { - return c.ContainerPut +// Writable converts a full Instance struct into a InstancePut struct (filters read-only fields). +func (c *Instance) Writable() InstancePut { + return c.InstancePut } -// IsActive checks whether the container state indicates the container is active -func (c Container) IsActive() bool { +// IsActive checks whether the instance state indicates the instance is active. +func (c Instance) IsActive() bool { switch c.StatusCode { case Stopped: return false @@ -112,8 +148,45 @@ func (c Container) IsActive() bool { } } -// ContainerSource represents the creation source for a new container -type ContainerSource struct { +// Container represents a LXD container. +type Container Instance + +// Writable converts a full Container struct into a ContainerPut struct (filters read-only fields). +func (c *Container) Writable() ContainerPut { + return ContainerPut(c.InstancePut) +} + +// IsActive checks whether the container state indicates the container is active. +func (c Container) IsActive() bool { + return Instance(c).IsActive() +} + +// 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"` +} + +// ContainerFull is a combination of Container, ContainerBackup, ContainerState and ContainerSnapshot. +// +// API extension: container_full +type ContainerFull struct { + Container `yaml:",inline"` + + Backups []ContainerBackup `json:"backups" yaml:"backups"` + State *ContainerState `json:"state" yaml:"state"` + Snapshots []ContainerSnapshot `json:"snapshots" yaml:"snapshots"` +} + +// 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"` @@ -148,3 +221,6 @@ type ContainerSource struct { // API extension: container_copy_project Project string `json:"project,omitempty" yaml:"project,omitempty"` } + +// ContainerSource represents the creation source for a new container. +type ContainerSource InstanceSource diff --git a/shared/api/container_backup.go b/shared/api/instance_backup.go similarity index 58% rename from shared/api/container_backup.go rename to shared/api/instance_backup.go index 8fe35e9964..d44622ba8c 100644 --- a/shared/api/container_backup.go +++ b/shared/api/instance_backup.go @@ -2,18 +2,25 @@ package api import "time" -// ContainerBackupsPost represents the fields available for a new LXD container backup -// API extension: container_backup -type ContainerBackupsPost struct { +// 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"` } -// ContainerBackup represents a LXD container backup +// ContainerBackupsPost represents the fields available for a new LXD container backup. +// // API extension: container_backup -type ContainerBackup struct { +type ContainerBackupsPost InstanceBackupsPost + +// 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"` @@ -21,9 +28,18 @@ type ContainerBackup struct { OptimizedStorage bool `json:"optimized_storage" yaml:"optimized_storage"` } -// ContainerBackupPost represents the fields available for the renaming of a -// container backup +// ContainerBackup represents a LXD container backup. +// // API extension: container_backup -type ContainerBackupPost struct { +type ContainerBackup InstanceBackup + +// InstanceBackupPost represents the fields available for the renaming of a instance backup. +// API extension: instances +type InstanceBackupPost struct { Name string `json:"name" yaml:"name"` } + +// ContainerBackupPost represents the fields available for the renaming of a container backup. +// +// API extension: container_backup +type ContainerBackupPost InstanceBackupPost diff --git a/shared/api/instance_console.go b/shared/api/instance_console.go new file mode 100644 index 0000000000..180b9ed40e --- /dev/null +++ b/shared/api/instance_console.go @@ -0,0 +1,27 @@ +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"` +} + +// ContainerConsoleControl represents a message on the container console "control" socket. +// +// API extension: console +type ContainerConsoleControl InstanceConsoleControl + +// 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"` +} + +// ContainerConsolePost represents a LXD container console request. +// +// API extension: console +type ContainerConsolePost InstanceConsolePost diff --git a/shared/api/container_exec.go b/shared/api/instance_exec.go similarity index 67% rename from shared/api/container_exec.go rename to shared/api/instance_exec.go index 7e724dc49f..2522e12f66 100644 --- a/shared/api/container_exec.go +++ b/shared/api/instance_exec.go @@ -1,14 +1,21 @@ package api -// ContainerExecControl represents a message on the container exec "control" socket -type ContainerExecControl struct { +// 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"` } -// ContainerExecPost represents a LXD container exec request -type ContainerExecPost struct { +// ContainerExecControl represents a message on the container exec "control" socket. +type ContainerExecControl InstanceExecControl + +// 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"` @@ -24,3 +31,6 @@ type ContainerExecPost struct { Group uint32 `json:"group" yaml:"group"` Cwd string `json:"cwd" yaml:"cwd"` } + +// ContainerExecPost represents a LXD container exec request. +type ContainerExecPost InstanceExecPost diff --git a/shared/api/container_snapshot.go b/shared/api/instance_snapshot.go similarity index 56% rename from shared/api/container_snapshot.go rename to shared/api/instance_snapshot.go index e68a0fb8cc..cc9045fbf7 100644 --- a/shared/api/container_snapshot.go +++ b/shared/api/instance_snapshot.go @@ -4,8 +4,10 @@ import ( "time" ) -// ContainerSnapshotsPost represents the fields available for a new LXD container snapshot -type ContainerSnapshotsPost struct { +// 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"` @@ -13,7 +15,22 @@ type ContainerSnapshotsPost struct { ExpiresAt *time.Time `json:"expires_at" yaml:"expires_at"` } -// ContainerSnapshotPost represents the fields required to rename/move a LXD container snapshot +// ContainerSnapshotsPost represents the fields available for a new LXD container snapshot. +type ContainerSnapshotsPost InstanceSnapshotsPost + +// 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"` + + // API extension: container_snapshot_stateful_migration + Live bool `json:"live,omitempty" yaml:"live,omitempty"` +} + +// ContainerSnapshotPost represents the fields required to rename/move a LXD container snapshot. type ContainerSnapshotPost struct { Name string `json:"name" yaml:"name"` Migration bool `json:"migration" yaml:"migration"` @@ -23,9 +40,10 @@ type ContainerSnapshotPost struct { Live bool `json:"live,omitempty" yaml:"live,omitempty"` } -// ContainerSnapshotPut represents the modifiable fields of a LXD container snapshot -// API extension: snapshot_expiry -type ContainerSnapshotPut struct { +// 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"` @@ -34,9 +52,16 @@ type ContainerSnapshotPut struct { ExpiresAt time.Time `json:"expires_at" yaml:"expires_at"` } -// ContainerSnapshot represents a LXD conainer snapshot -type ContainerSnapshot struct { - ContainerSnapshotPut `yaml:",inline"` +// ContainerSnapshotPut represents the modifiable fields of a LXD container snapshot. +// +// API extension: snapshot_expiry +type ContainerSnapshotPut InstanceSnapshotPut + +// 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"` @@ -46,8 +71,17 @@ type ContainerSnapshot struct { Stateful bool `json:"stateful" yaml:"stateful"` } -// Writable converts a full ContainerSnapshot struct into a ContainerSnapshotPut struct +// ContainerSnapshot represents a LXD container snapshot. +type ContainerSnapshot InstanceSnapshot + +// Writable converts a full InstanceSnapshot struct into a InstanceSnapshotPut struct. +// (filters read-only fields) +func (c *InstanceSnapshot) Writable() InstanceSnapshotPut { + return c.InstanceSnapshotPut +} + +// Writable converts a full ContainerSnapshot struct into a ContainerSnapshotPut struct. // (filters read-only fields) func (c *ContainerSnapshot) Writable() ContainerSnapshotPut { - return c.ContainerSnapshotPut + return ContainerSnapshotPut(c.InstanceSnapshotPut) } diff --git a/shared/api/instance_state.go b/shared/api/instance_state.go new file mode 100644 index 0000000000..9ddf3985c7 --- /dev/null +++ b/shared/api/instance_state.go @@ -0,0 +1,139 @@ +package api + +// InstanceStatePut represents the modifiable fields of a LXD instance's state +// +// API extension: container_cpu_time +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"` +} + +// ContainerStatePut represents the modifiable fields of a LXD container's state. +type ContainerStatePut InstanceStatePut + +// 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"` + + // API extension: container_cpu_time + CPU ContainerStateCPU `json:"cpu" yaml:"cpu"` +} + +// ContainerState represents a LXD container's state. +type ContainerState struct { + Status string `json:"status" yaml:"status"` + StatusCode StatusCode `json:"status_code" yaml:"status_code"` + Disk map[string]ContainerStateDisk `json:"disk" yaml:"disk"` + Memory ContainerStateMemory `json:"memory" yaml:"memory"` + Network map[string]ContainerStateNetwork `json:"network" yaml:"network"` + Pid int64 `json:"pid" yaml:"pid"` + Processes int64 `json:"processes" yaml:"processes"` + + // API extension: container_cpu_time + CPU ContainerStateCPU `json:"cpu" yaml:"cpu"` +} + +// InstanceStateDisk represents the disk information section of a LXD instance's state. +type InstanceStateDisk struct { + Usage int64 `json:"usage" yaml:"usage"` +} + +// ContainerStateDisk represents the disk information section of a LXD container's state. +// +// API extension: instances +type ContainerStateDisk InstanceStateDisk + +// InstanceStateCPU represents the cpu information section of a LXD instance's state. +// +// API extension: instances +type InstanceStateCPU struct { + Usage int64 `json:"usage" yaml:"usage"` +} + +// ContainerStateCPU represents the cpu information section of a LXD container's state +// +// API extension: container_cpu_time +type ContainerStateCPU InstanceStateCPU + +// 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"` +} + +// ContainerStateMemory represents the memory information section of a LXD container's state. +type ContainerStateMemory InstanceStateMemory + +// 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"` +} + +// ContainerStateNetwork represents the network information section of a LXD container's state. +type ContainerStateNetwork struct { + Addresses []ContainerStateNetworkAddress `json:"addresses" yaml:"addresses"` + Counters ContainerStateNetworkCounters `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"` +} + +// ContainerStateNetworkAddress represents a network address as part of the network section of a LXD container's state. +type ContainerStateNetworkAddress 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"` +} + +// ContainerStateNetworkCounters represents packet counters as part of the network section of a LXD container's state +type ContainerStateNetworkCounters 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"` +}
_______________________________________________ lxc-devel mailing list lxc-devel@lists.linuxcontainers.org http://lists.linuxcontainers.org/listinfo/lxc-devel