[lxc-devel] [lxd/master] Move all our API structs to shared/api
stgraber on Github
lxc-bot at linuxcontainers.org
Thu Dec 22 22:37:35 UTC 2016
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20161222/4a51cac6/attachment.bin>
-------------- next part --------------
From 271be2f43553bd664d7df3abe90365e8a8c92dfa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Thu, 15 Dec 2016 19:24:41 -0500
Subject: [PATCH 1/9] shared: Move REST API to new package: image
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
client.go | 17 ++++----
lxc/image.go | 43 ++++++++++---------
lxd/containers_post.go | 5 ++-
lxd/daemon_images.go | 11 ++---
lxd/db_images.go | 57 +++++++++++++-----------
lxd/db_test.go | 15 ++++---
lxd/images.go | 77 +++++++++++----------------------
lxd/remote.go | 4 +-
lxd/storage.go | 3 +-
shared/api/image.go | 81 +++++++++++++++++++++++++++++++++++
shared/image.go | 64 ---------------------------
shared/simplestreams/simplestreams.go | 55 ++++++++++++------------
12 files changed, 218 insertions(+), 214 deletions(-)
create mode 100644 shared/api/image.go
delete mode 100644 shared/image.go
diff --git a/client.go b/client.go
index b9ca129..1a61dd4 100644
--- a/client.go
+++ b/client.go
@@ -24,6 +24,7 @@ import (
"github.com/gorilla/websocket"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/ioprogress"
"github.com/lxc/lxd/shared/simplestreams"
"github.com/lxc/lxd/shared/version"
@@ -1136,7 +1137,7 @@ func (c *Client) PostImage(imageFile string, rootfsFile string, properties []str
return fingerprint, nil
}
-func (c *Client) GetImageInfo(image string) (*shared.ImageInfo, error) {
+func (c *Client) GetImageInfo(image string) (*api.Image, error) {
if c.Remote.Protocol == "simplestreams" && c.simplestreams != nil {
return c.simplestreams.GetImageInfo(image)
}
@@ -1146,7 +1147,7 @@ func (c *Client) GetImageInfo(image string) (*shared.ImageInfo, error) {
return nil, err
}
- info := shared.ImageInfo{}
+ info := api.Image{}
if err := json.Unmarshal(resp.Metadata, &info); err != nil {
return nil, err
}
@@ -1154,7 +1155,7 @@ func (c *Client) GetImageInfo(image string) (*shared.ImageInfo, error) {
return &info, nil
}
-func (c *Client) PutImageInfo(name string, p shared.BriefImageInfo) error {
+func (c *Client) PutImageInfo(name string, p api.ImagePut) error {
if c.Remote.Public {
return fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -1163,7 +1164,7 @@ func (c *Client) PutImageInfo(name string, p shared.BriefImageInfo) error {
return err
}
-func (c *Client) ListImages() ([]shared.ImageInfo, error) {
+func (c *Client) ListImages() ([]api.Image, error) {
if c.Remote.Protocol == "simplestreams" && c.simplestreams != nil {
return c.simplestreams.ListImages()
}
@@ -1173,7 +1174,7 @@ func (c *Client) ListImages() ([]shared.ImageInfo, error) {
return nil, err
}
- var result []shared.ImageInfo
+ var result []api.Image
if err := json.Unmarshal(resp.Metadata, &result); err != nil {
return nil, err
}
@@ -1215,7 +1216,7 @@ func (c *Client) DeleteAlias(alias string) error {
return err
}
-func (c *Client) ListAliases() (shared.ImageAliases, error) {
+func (c *Client) ListAliases() ([]api.ImageAliasesEntry, error) {
if c.Remote.Protocol == "simplestreams" && c.simplestreams != nil {
return c.simplestreams.ListAliases()
}
@@ -1225,7 +1226,7 @@ func (c *Client) ListAliases() (shared.ImageAliases, error) {
return nil, err
}
- var result shared.ImageAliases
+ var result []api.ImageAliasesEntry
if err := json.Unmarshal(resp.Metadata, &result); err != nil {
return nil, err
@@ -1308,7 +1309,7 @@ func (c *Client) GetAlias(alias string) string {
return ""
}
- var result shared.ImageAliasesEntry
+ var result api.ImageAliasesEntry
if err := json.Unmarshal(resp.Metadata, &result); err != nil {
return ""
}
diff --git a/lxc/image.go b/lxc/image.go
index 9e23892..b81191d 100644
--- a/lxc/image.go
+++ b/lxc/image.go
@@ -15,6 +15,7 @@ import (
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
"github.com/lxc/lxd/shared/termios"
@@ -346,12 +347,12 @@ func (c *imageCmd) run(config *lxd.Config, args []string) error {
fmt.Printf(i18n.G("Public: %s")+"\n", public)
fmt.Printf(i18n.G("Timestamps:") + "\n")
const layout = "2006/01/02 15:04 UTC"
- if info.CreationDate.UTC().Unix() != 0 {
- fmt.Printf(" "+i18n.G("Created: %s")+"\n", info.CreationDate.UTC().Format(layout))
+ if info.CreatedAt.UTC().Unix() != 0 {
+ fmt.Printf(" "+i18n.G("Created: %s")+"\n", info.CreatedAt.UTC().Format(layout))
}
- fmt.Printf(" "+i18n.G("Uploaded: %s")+"\n", info.UploadDate.UTC().Format(layout))
- if info.ExpiryDate.UTC().Unix() != 0 {
- fmt.Printf(" "+i18n.G("Expires: %s")+"\n", info.ExpiryDate.UTC().Format(layout))
+ fmt.Printf(" "+i18n.G("Uploaded: %s")+"\n", info.UploadedAt.UTC().Format(layout))
+ if info.ExpiresAt.UTC().Unix() != 0 {
+ fmt.Printf(" "+i18n.G("Expires: %s")+"\n", info.ExpiresAt.UTC().Format(layout))
} else {
fmt.Printf(" " + i18n.G("Expires: never") + "\n")
}
@@ -364,11 +365,11 @@ func (c *imageCmd) run(config *lxd.Config, args []string) error {
fmt.Printf(" - %s\n", alias.Name)
}
fmt.Printf(i18n.G("Auto update: %s")+"\n", autoUpdate)
- if info.Source != nil {
+ if info.UpdateSource != nil {
fmt.Println(i18n.G("Source:"))
- fmt.Printf(" Server: %s\n", info.Source.Server)
- fmt.Printf(" Protocol: %s\n", info.Source.Protocol)
- fmt.Printf(" Alias: %s\n", info.Source.Alias)
+ fmt.Printf(" Server: %s\n", info.UpdateSource.Server)
+ fmt.Printf(" Protocol: %s\n", info.UpdateSource.Protocol)
+ fmt.Printf(" Alias: %s\n", info.UpdateSource.Alias)
}
return nil
@@ -466,7 +467,7 @@ func (c *imageCmd) run(config *lxd.Config, args []string) error {
return err
}
- var images []shared.ImageInfo
+ var images []api.Image
allImages, err := d.ListImages()
if err != nil {
return err
@@ -557,7 +558,7 @@ func (c *imageCmd) run(config *lxd.Config, args []string) error {
return err
}
- properties := info.Brief()
+ properties := info.Writable()
data, err := yaml.Marshal(&properties)
fmt.Printf("%s", data)
@@ -576,7 +577,7 @@ func (c *imageCmd) dereferenceAlias(d *lxd.Client, inName string) string {
return result
}
-func (c *imageCmd) shortestAlias(list []shared.ImageAlias) string {
+func (c *imageCmd) shortestAlias(list []api.ImageAlias) string {
shortest := ""
for _, l := range list {
if shortest == "" {
@@ -600,7 +601,7 @@ func (c *imageCmd) findDescription(props map[string]string) string {
return ""
}
-func (c *imageCmd) showImages(images []shared.ImageInfo, filters []string) error {
+func (c *imageCmd) showImages(images []api.Image, filters []string) error {
switch c.format {
case listFormatTable:
data := [][]string{}
@@ -622,7 +623,7 @@ func (c *imageCmd) showImages(images []shared.ImageInfo, filters []string) error
}
const layout = "Jan 2, 2006 at 3:04pm (MST)"
- uploaded := image.UploadDate.UTC().Format(layout)
+ uploaded := image.UploadedAt.UTC().Format(layout)
size := fmt.Sprintf("%.2fMB", float64(image.Size)/1024.0/1024.0)
data = append(data, []string{shortest, fp, public, description, image.Architecture, size, uploaded})
}
@@ -643,7 +644,7 @@ func (c *imageCmd) showImages(images []shared.ImageInfo, filters []string) error
table.AppendBulk(data)
table.Render()
case listFormatJSON:
- data := make([]*shared.ImageInfo, len(images))
+ data := make([]*api.Image, len(images))
for i := range images {
data[i] = &images[i]
}
@@ -659,7 +660,7 @@ func (c *imageCmd) showImages(images []shared.ImageInfo, filters []string) error
return nil
}
-func (c *imageCmd) showAliases(aliases shared.ImageAliases, filters []string) error {
+func (c *imageCmd) showAliases(aliases []api.ImageAliasesEntry, filters []string) error {
data := [][]string{}
for _, alias := range aliases {
if !c.aliasShouldShow(filters, &alias) {
@@ -692,7 +693,7 @@ func (c *imageCmd) doImageEdit(client *lxd.Client, image string) error {
return err
}
- newdata := shared.BriefImageInfo{}
+ newdata := api.ImagePut{}
err = yaml.Unmarshal(contents, &newdata)
if err != nil {
return err
@@ -706,7 +707,7 @@ func (c *imageCmd) doImageEdit(client *lxd.Client, image string) error {
return err
}
- brief := config.Brief()
+ brief := config.Writable()
data, err := yaml.Marshal(&brief)
if err != nil {
return err
@@ -720,7 +721,7 @@ func (c *imageCmd) doImageEdit(client *lxd.Client, image string) error {
for {
// Parse the text received from the editor
- newdata := shared.BriefImageInfo{}
+ newdata := api.ImagePut{}
err = yaml.Unmarshal(content, &newdata)
if err == nil {
err = client.PutImageInfo(image, newdata)
@@ -747,7 +748,7 @@ func (c *imageCmd) doImageEdit(client *lxd.Client, image string) error {
return nil
}
-func (c *imageCmd) imageShouldShow(filters []string, state *shared.ImageInfo) bool {
+func (c *imageCmd) imageShouldShow(filters []string, state *api.Image) bool {
if len(filters) == 0 {
return true
}
@@ -806,7 +807,7 @@ func (c *imageCmd) imageShouldShow(filters []string, state *shared.ImageInfo) bo
return true
}
-func (c *imageCmd) aliasShouldShow(filters []string, state *shared.ImageAliasesEntry) bool {
+func (c *imageCmd) aliasShouldShow(filters []string, state *api.ImageAliasesEntry) bool {
if len(filters) == 0 {
return true
}
diff --git a/lxd/containers_post.go b/lxd/containers_post.go
index b7754ea..306c590 100644
--- a/lxd/containers_post.go
+++ b/lxd/containers_post.go
@@ -13,6 +13,7 @@ import (
"github.com/lxc/lxd/lxd/types"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/osarch"
log "gopkg.in/inconshreveable/log15.v2"
@@ -89,7 +90,7 @@ func createFromImage(d *Daemon, req *containerPostReq) Response {
return InternalError(err)
}
- var image *shared.ImageInfo
+ var image *api.Image
for _, hash := range hashes {
_, img, err := dbImageGet(d.db, hash, false, true)
@@ -97,7 +98,7 @@ func createFromImage(d *Daemon, req *containerPostReq) Response {
continue
}
- if image != nil && img.CreationDate.Before(image.CreationDate) {
+ if image != nil && img.CreatedAt.Before(image.CreatedAt) {
continue
}
diff --git a/lxd/daemon_images.go b/lxd/daemon_images.go
index 7324eff..6a8c7ee 100644
--- a/lxd/daemon_images.go
+++ b/lxd/daemon_images.go
@@ -16,6 +16,7 @@ import (
"gopkg.in/yaml.v2"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/ioprogress"
"github.com/lxc/lxd/shared/simplestreams"
"github.com/lxc/lxd/shared/version"
@@ -25,8 +26,8 @@ import (
// Simplestream cache
type imageStreamCacheEntry struct {
- Aliases shared.ImageAliases `yaml:"aliases"`
- Fingerprints []string `yaml:"fingerprints"`
+ Aliases []api.ImageAliasesEntry `yaml:"aliases"`
+ Fingerprints []string `yaml:"fingerprints"`
expiry time.Time
ss *simplestreams.SimpleStreams
}
@@ -244,7 +245,7 @@ func (d *Daemon) ImageDownload(op *operation, server string, protocol string, ce
exporturl := server
- var info shared.ImageInfo
+ var info api.Image
info.Fingerprint = fp
destDir := shared.VarPath("images")
@@ -481,8 +482,8 @@ func (d *Daemon) ImageDownload(op *operation, server string, protocol string, ce
}
info.Architecture = imageMeta.Architecture
- info.CreationDate = time.Unix(imageMeta.CreationDate, 0)
- info.ExpiryDate = time.Unix(imageMeta.ExpiryDate, 0)
+ info.CreatedAt = time.Unix(imageMeta.CreationDate, 0)
+ info.ExpiresAt = time.Unix(imageMeta.ExpiryDate, 0)
info.Properties = imageMeta.Properties
}
diff --git a/lxd/db_images.go b/lxd/db_images.go
index ac515a3..c3aef67 100644
--- a/lxd/db_images.go
+++ b/lxd/db_images.go
@@ -7,7 +7,7 @@ import (
_ "github.com/mattn/go-sqlite3"
- "github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/osarch"
)
@@ -76,27 +76,27 @@ func dbImageSourceInsert(db *sql.DB, imageId int, server string, protocol string
return err
}
-func dbImageSourceGet(db *sql.DB, imageId int) (int, shared.ImageSource, error) {
+func dbImageSourceGet(db *sql.DB, imageId int) (int, api.ImageSource, error) {
q := `SELECT id, server, protocol, certificate, alias FROM images_source WHERE image_id=?`
id := 0
protocolInt := -1
- result := shared.ImageSource{}
+ result := api.ImageSource{}
arg1 := []interface{}{imageId}
arg2 := []interface{}{&id, &result.Server, &protocolInt, &result.Certificate, &result.Alias}
err := dbQueryRowScan(db, q, arg1, arg2)
if err != nil {
if err == sql.ErrNoRows {
- return -1, shared.ImageSource{}, NoSuchObjectError
+ return -1, api.ImageSource{}, NoSuchObjectError
}
- return -1, shared.ImageSource{}, err
+ return -1, api.ImageSource{}, err
}
protocol, found := dbImageSourceProtocol[protocolInt]
if !found {
- return -1, shared.ImageSource{}, fmt.Errorf("Invalid protocol: %d", protocolInt)
+ return -1, api.ImageSource{}, fmt.Errorf("Invalid protocol: %d", protocolInt)
}
result.Protocol = protocol
@@ -110,12 +110,12 @@ func dbImageSourceGet(db *sql.DB, imageId int) (int, shared.ImageSource, error)
// pass a shortform and will get the full fingerprint.
// There can never be more than one image with a given fingerprint, as it is
// enforced by a UNIQUE constraint in the schema.
-func dbImageGet(db *sql.DB, fingerprint string, public bool, strictMatching bool) (int, *shared.ImageInfo, error) {
+func dbImageGet(db *sql.DB, fingerprint string, public bool, strictMatching bool) (int, *api.Image, error) {
var err error
var create, expire, used, upload *time.Time // These hold the db-returned times
// The object we'll actually return
- image := shared.ImageInfo{}
+ image := api.Image{}
id := -1
arch := -1
@@ -159,27 +159,27 @@ func dbImageGet(db *sql.DB, fingerprint string, public bool, strictMatching bool
// Some of the dates can be nil in the DB, let's process them.
if create != nil {
- image.CreationDate = *create
+ image.CreatedAt = *create
} else {
- image.CreationDate = time.Time{}
+ image.CreatedAt = time.Time{}
}
if expire != nil {
- image.ExpiryDate = *expire
+ image.ExpiresAt = *expire
} else {
- image.ExpiryDate = time.Time{}
+ image.ExpiresAt = time.Time{}
}
if used != nil {
- image.LastUsedDate = *used
+ image.LastUsedAt = *used
} else {
- image.LastUsedDate = time.Time{}
+ image.LastUsedAt = time.Time{}
}
image.Architecture, _ = osarch.ArchitectureName(arch)
// The upload date is enforced by NOT NULL in the schema, so it can never be nil.
- image.UploadDate = *upload
+ image.UploadedAt = *upload
// Get the properties
q := "SELECT key, value FROM images_properties where image_id=?"
@@ -209,11 +209,11 @@ func dbImageGet(db *sql.DB, fingerprint string, public bool, strictMatching bool
return -1, nil, err
}
- aliases := []shared.ImageAlias{}
+ aliases := []api.ImageAlias{}
for _, r := range results {
name = r[0].(string)
desc = r[0].(string)
- a := shared.ImageAlias{Name: name, Description: desc}
+ a := api.ImageAlias{Name: name, Description: desc}
aliases = append(aliases, a)
}
@@ -221,7 +221,7 @@ func dbImageGet(db *sql.DB, fingerprint string, public bool, strictMatching bool
_, source, err := dbImageSourceGet(db, id)
if err == nil {
- image.Source = &source
+ image.UpdateSource = &source
}
return id, &image, nil
@@ -245,7 +245,7 @@ func dbImageDelete(db *sql.DB, id int) error {
return nil
}
-func dbImageAliasGet(db *sql.DB, name string, isTrustedClient bool) (int, shared.ImageAliasesEntry, error) {
+func dbImageAliasGet(db *sql.DB, name string, isTrustedClient bool) (int, api.ImageAliasesEntry, error) {
q := `SELECT images_aliases.id, images.fingerprint, images_aliases.description
FROM images_aliases
INNER JOIN images
@@ -257,19 +257,24 @@ func dbImageAliasGet(db *sql.DB, name string, isTrustedClient bool) (int, shared
var fingerprint, description string
id := -1
+ entry := api.ImageAliasesEntry{}
arg1 := []interface{}{name}
arg2 := []interface{}{&id, &fingerprint, &description}
err := dbQueryRowScan(db, q, arg1, arg2)
if err != nil {
if err == sql.ErrNoRows {
- return -1, shared.ImageAliasesEntry{}, NoSuchObjectError
+ return -1, entry, NoSuchObjectError
}
- return -1, shared.ImageAliasesEntry{}, err
+ return -1, entry, err
}
- return id, shared.ImageAliasesEntry{Name: name, Target: fingerprint, Description: description}, nil
+ entry.Name = name
+ entry.Target = fingerprint
+ entry.Description = description
+
+ return id, entry, nil
}
func dbImageAliasRename(db *sql.DB, id int, name string) error {
@@ -312,7 +317,7 @@ func dbImageLastAccessInit(db *sql.DB, fingerprint string) error {
return err
}
-func dbImageUpdate(db *sql.DB, id int, fname string, sz int64, public bool, autoUpdate bool, architecture string, creationDate time.Time, expiryDate time.Time, properties map[string]string) error {
+func dbImageUpdate(db *sql.DB, id int, fname string, sz int64, public bool, autoUpdate bool, architecture string, createdAt time.Time, expiresAt time.Time, properties map[string]string) error {
arch, err := osarch.ArchitectureId(architecture)
if err != nil {
arch = 0
@@ -340,7 +345,7 @@ func dbImageUpdate(db *sql.DB, id int, fname string, sz int64, public bool, auto
}
defer stmt.Close()
- _, err = stmt.Exec(fname, sz, publicInt, autoUpdateInt, arch, creationDate, expiryDate, id)
+ _, err = stmt.Exec(fname, sz, publicInt, autoUpdateInt, arch, createdAt, expiresAt, id)
if err != nil {
tx.Rollback()
return err
@@ -369,7 +374,7 @@ func dbImageUpdate(db *sql.DB, id int, fname string, sz int64, public bool, auto
return nil
}
-func dbImageInsert(db *sql.DB, fp string, fname string, sz int64, public bool, autoUpdate bool, architecture string, creationDate time.Time, expiryDate time.Time, properties map[string]string) error {
+func dbImageInsert(db *sql.DB, fp string, fname string, sz int64, public bool, autoUpdate bool, architecture string, createdAt time.Time, expiresAt time.Time, properties map[string]string) error {
arch, err := osarch.ArchitectureId(architecture)
if err != nil {
arch = 0
@@ -397,7 +402,7 @@ func dbImageInsert(db *sql.DB, fp string, fname string, sz int64, public bool, a
}
defer stmt.Close()
- result, err := stmt.Exec(fp, fname, sz, publicInt, autoUpdateInt, arch, creationDate, expiryDate)
+ result, err := stmt.Exec(fp, fname, sz, publicInt, autoUpdateInt, arch, createdAt, expiresAt)
if err != nil {
tx.Rollback()
return err
diff --git a/lxd/db_test.go b/lxd/db_test.go
index 9e9aa38..ae0e504 100644
--- a/lxd/db_test.go
+++ b/lxd/db_test.go
@@ -8,6 +8,7 @@ import (
"github.com/lxc/lxd/lxd/types"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/logging"
)
@@ -401,7 +402,7 @@ INSERT INTO containers_config (container_id, key, value) VALUES (1, 'thekey', 't
func Test_dbImageGet_finds_image_for_fingerprint(t *testing.T) {
var db *sql.DB
var err error
- var result *shared.ImageInfo
+ var result *api.Image
db = createTestDb(t)
defer db.Close()
@@ -420,16 +421,16 @@ func Test_dbImageGet_finds_image_for_fingerprint(t *testing.T) {
t.Fatal("Filename should be set.")
}
- if result.CreationDate.UTC() != time.Unix(1431547174, 0).UTC() {
- t.Fatal(fmt.Sprintf("%s != %s", result.CreationDate, time.Unix(1431547174, 0)))
+ if result.CreatedAt.UTC() != time.Unix(1431547174, 0).UTC() {
+ t.Fatal(fmt.Sprintf("%s != %s", result.CreatedAt, time.Unix(1431547174, 0)))
}
- if result.ExpiryDate.UTC() != time.Unix(1431547175, 0).UTC() { // It was short lived
- t.Fatal(fmt.Sprintf("%s != %s", result.ExpiryDate, time.Unix(1431547175, 0)))
+ if result.ExpiresAt.UTC() != time.Unix(1431547175, 0).UTC() { // It was short lived
+ t.Fatal(fmt.Sprintf("%s != %s", result.ExpiresAt, time.Unix(1431547175, 0)))
}
- if result.UploadDate.UTC() != time.Unix(1431547176, 0).UTC() {
- t.Fatal(fmt.Sprintf("%s != %s", result.UploadDate, time.Unix(1431547176, 0)))
+ if result.UploadedAt.UTC() != time.Unix(1431547176, 0).UTC() {
+ t.Fatal(fmt.Sprintf("%s != %s", result.UploadedAt, time.Unix(1431547176, 0)))
}
}
diff --git a/lxd/images.go b/lxd/images.go
index 12e49a1..1d34299 100644
--- a/lxd/images.go
+++ b/lxd/images.go
@@ -23,6 +23,7 @@ import (
"gopkg.in/yaml.v2"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/logging"
"github.com/lxc/lxd/shared/osarch"
"github.com/lxc/lxd/shared/version"
@@ -208,15 +209,6 @@ type templateEntry struct {
Properties map[string]string `yaml:"properties"`
}
-type imagePostReq struct {
- Filename string `json:"filename"`
- Public bool `json:"public"`
- Source map[string]string `json:"source"`
- Properties map[string]string `json:"properties"`
- AutoUpdate bool `json:"auto_update"`
- CompressionAlgorithm string `json:"compression_algorithm"`
-}
-
type imageMetadata struct {
Architecture string `yaml:"architecture"`
CreationDate int64 `yaml:"creation_date"`
@@ -229,8 +221,8 @@ type imageMetadata struct {
* This function takes a container or snapshot from the local image server and
* exports it as an image.
*/
-func imgPostContInfo(d *Daemon, r *http.Request, req imagePostReq,
- builddir string) (info shared.ImageInfo, err error) {
+func imgPostContInfo(d *Daemon, r *http.Request, req api.ImagesPost,
+ builddir string) (info api.Image, err error) {
info.Properties = map[string]string{}
name := req.Source["name"]
@@ -327,7 +319,7 @@ func imgPostContInfo(d *Daemon, r *http.Request, req imagePostReq,
return info, nil
}
-func imgPostRemoteInfo(d *Daemon, req imagePostReq, op *operation) error {
+func imgPostRemoteInfo(d *Daemon, req api.ImagesPost, op *operation) error {
var err error
var hash string
@@ -356,7 +348,7 @@ func imgPostRemoteInfo(d *Daemon, req imagePostReq, op *operation) error {
// Update the DB record if needed
if req.Public || req.AutoUpdate || req.Filename != "" || len(req.Properties) > 0 {
- err = dbImageUpdate(d.db, id, req.Filename, info.Size, req.Public, req.AutoUpdate, info.Architecture, info.CreationDate, info.ExpiryDate, info.Properties)
+ err = dbImageUpdate(d.db, id, req.Filename, info.Size, req.Public, req.AutoUpdate, info.Architecture, info.CreatedAt, info.ExpiresAt, info.Properties)
if err != nil {
return err
}
@@ -370,7 +362,7 @@ func imgPostRemoteInfo(d *Daemon, req imagePostReq, op *operation) error {
return nil
}
-func imgPostURLInfo(d *Daemon, req imagePostReq, op *operation) error {
+func imgPostURLInfo(d *Daemon, req api.ImagesPost, op *operation) error {
var err error
if req.Source["url"] == "" {
@@ -429,7 +421,7 @@ func imgPostURLInfo(d *Daemon, req imagePostReq, op *operation) error {
}
if req.Public || req.AutoUpdate || req.Filename != "" || len(req.Properties) > 0 {
- err = dbImageUpdate(d.db, id, req.Filename, info.Size, req.Public, req.AutoUpdate, info.Architecture, info.CreationDate, info.ExpiryDate, info.Properties)
+ err = dbImageUpdate(d.db, id, req.Filename, info.Size, req.Public, req.AutoUpdate, info.Architecture, info.CreatedAt, info.ExpiresAt, info.Properties)
if err != nil {
return err
}
@@ -444,7 +436,7 @@ func imgPostURLInfo(d *Daemon, req imagePostReq, op *operation) error {
}
func getImgPostInfo(d *Daemon, r *http.Request,
- builddir string, post *os.File) (info shared.ImageInfo, err error) {
+ builddir string, post *os.File) (info api.Image, err error) {
var imageMeta *imageMetadata
logger := logging.AddContext(shared.Log, log.Ctx{"function": "getImgPostInfo"})
@@ -618,8 +610,8 @@ func getImgPostInfo(d *Daemon, r *http.Request,
}
info.Architecture = imageMeta.Architecture
- info.CreationDate = time.Unix(imageMeta.CreationDate, 0)
- info.ExpiryDate = time.Unix(imageMeta.ExpiryDate, 0)
+ info.CreatedAt = time.Unix(imageMeta.CreationDate, 0)
+ info.ExpiresAt = time.Unix(imageMeta.ExpiryDate, 0)
info.Properties = imageMeta.Properties
if len(propHeaders) > 0 {
@@ -634,7 +626,7 @@ func getImgPostInfo(d *Daemon, r *http.Request,
return info, nil
}
-func imageBuildFromInfo(d *Daemon, info shared.ImageInfo) (metadata map[string]string, err error) {
+func imageBuildFromInfo(d *Daemon, info api.Image) (metadata map[string]string, err error) {
err = d.Storage.ImageCreate(info.Fingerprint)
if err != nil {
os.Remove(shared.VarPath("images", info.Fingerprint))
@@ -651,8 +643,8 @@ func imageBuildFromInfo(d *Daemon, info shared.ImageInfo) (metadata map[string]s
info.Public,
info.AutoUpdate,
info.Architecture,
- info.CreationDate,
- info.ExpiryDate,
+ info.CreatedAt,
+ info.ExpiresAt,
info.Properties)
if err != nil {
return metadata, err
@@ -702,7 +694,7 @@ func imagesPost(d *Daemon, r *http.Request) Response {
decoder := json.NewDecoder(post)
imageUpload := false
- req := imagePostReq{}
+ req := api.ImagesPost{}
err = decoder.Decode(&req)
if err != nil {
if r.Header.Get("Content-Type") == "application/json" {
@@ -718,7 +710,7 @@ func imagesPost(d *Daemon, r *http.Request) Response {
// Begin background operation
run := func(op *operation) error {
- var info shared.ImageInfo
+ var info api.Image
// Setup the cleanup function
defer cleanup(builddir, post)
@@ -825,7 +817,7 @@ func doImagesGet(d *Daemon, recursion bool, public bool) (interface{}, error) {
}
resultString := make([]string, len(results))
- resultMap := make([]*shared.ImageInfo, len(results))
+ resultMap := make([]*api.Image, len(results))
i := 0
for _, name := range results {
if !recursion {
@@ -903,7 +895,7 @@ func autoUpdateImages(d *Daemon) {
continue
}
- err = dbImageLastAccessUpdate(d.db, hash, info.LastUsedDate)
+ err = dbImageLastAccessUpdate(d.db, hash, info.LastUsedAt)
if err != nil {
shared.LogError("Error setting last use date", log.Ctx{"err": err, "fp": hash})
continue
@@ -1007,7 +999,7 @@ func imageDelete(d *Daemon, r *http.Request) Response {
return OperationResponse(op)
}
-func doImageGet(d *Daemon, fingerprint string, public bool) (*shared.ImageInfo, Response) {
+func doImageGet(d *Daemon, fingerprint string, public bool) (*api.Image, Response) {
_, imgInfo, err := dbImageGet(d.db, fingerprint, public, false)
if err != nil {
return nil, SmartError(err)
@@ -1064,12 +1056,6 @@ func imageGet(d *Daemon, r *http.Request) Response {
return SyncResponseETag(true, info, etag)
}
-type imagePutReq struct {
- Properties map[string]string `json:"properties"`
- Public bool `json:"public"`
- AutoUpdate bool `json:"auto_update"`
-}
-
func imagePut(d *Daemon, r *http.Request) Response {
// Get current value
fingerprint := mux.Vars(r)["fingerprint"]
@@ -1085,12 +1071,12 @@ func imagePut(d *Daemon, r *http.Request) Response {
return PreconditionFailed(err)
}
- req := imagePutReq{}
+ req := api.ImagePut{}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return BadRequest(err)
}
- err = dbImageUpdate(d.db, id, info.Filename, info.Size, req.Public, req.AutoUpdate, info.Architecture, info.CreationDate, info.ExpiryDate, req.Properties)
+ err = dbImageUpdate(d.db, id, info.Filename, info.Size, req.Public, req.AutoUpdate, info.Architecture, info.CreatedAt, info.ExpiresAt, req.Properties)
if err != nil {
return SmartError(err)
}
@@ -1126,7 +1112,7 @@ func imagePatch(d *Daemon, r *http.Request) Response {
return BadRequest(err)
}
- req := imagePutReq{}
+ req := api.ImagePut{}
if err := json.NewDecoder(rdr2).Decode(&req); err != nil {
return BadRequest(err)
}
@@ -1156,7 +1142,7 @@ func imagePatch(d *Daemon, r *http.Request) Response {
info.Properties = properties
}
- err = dbImageUpdate(d.db, id, info.Filename, info.Size, info.Public, info.AutoUpdate, info.Architecture, info.CreationDate, info.ExpiryDate, info.Properties)
+ err = dbImageUpdate(d.db, id, info.Filename, info.Size, info.Public, info.AutoUpdate, info.Architecture, info.CreatedAt, info.ExpiresAt, info.Properties)
if err != nil {
return SmartError(err)
}
@@ -1166,19 +1152,8 @@ func imagePatch(d *Daemon, r *http.Request) Response {
var imageCmd = Command{name: "images/{fingerprint}", untrustedGet: true, get: imageGet, put: imagePut, delete: imageDelete, patch: imagePatch}
-type aliasPostReq struct {
- Name string `json:"name"`
- Description string `json:"description"`
- Target string `json:"target"`
-}
-
-type aliasPutReq struct {
- Description string `json:"description"`
- Target string `json:"target"`
-}
-
func aliasesPost(d *Daemon, r *http.Request) Response {
- req := aliasPostReq{}
+ req := api.ImageAliasesPost{}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return BadRequest(err)
}
@@ -1218,7 +1193,7 @@ func aliasesGet(d *Daemon, r *http.Request) Response {
return BadRequest(err)
}
responseStr := []string{}
- responseMap := shared.ImageAliases{}
+ responseMap := []api.ImageAliasesEntry{}
for _, res := range results {
name = res[0].(string)
if !recursion {
@@ -1281,7 +1256,7 @@ func aliasPut(d *Daemon, r *http.Request) Response {
return PreconditionFailed(err)
}
- req := aliasPutReq{}
+ req := api.ImageAliasesEntryPut{}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return BadRequest(err)
}
@@ -1358,7 +1333,7 @@ func aliasPatch(d *Daemon, r *http.Request) Response {
func aliasPost(d *Daemon, r *http.Request) Response {
name := mux.Vars(r)["name"]
- req := aliasPostReq{}
+ req := api.ImageAliasesEntryPost{}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return BadRequest(err)
}
diff --git a/lxd/remote.go b/lxd/remote.go
index ca7158a..0a42483 100644
--- a/lxd/remote.go
+++ b/lxd/remote.go
@@ -4,7 +4,7 @@ import (
"encoding/json"
"fmt"
- "github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/version"
)
@@ -18,7 +18,7 @@ func remoteGetImageFingerprint(d *Daemon, server string, certificate string, ali
return "", err
}
- var result shared.ImageAliasesEntry
+ var result api.ImageAliasesEntry
if err = json.Unmarshal(resp.Metadata, &result); err != nil {
return "", fmt.Errorf("Error reading alias")
}
diff --git a/lxd/storage.go b/lxd/storage.go
index 44c131f..774366c 100644
--- a/lxd/storage.go
+++ b/lxd/storage.go
@@ -15,6 +15,7 @@ import (
"github.com/lxc/lxd/lxd/types"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/ioprogress"
"github.com/lxc/lxd/shared/logging"
@@ -275,7 +276,7 @@ func storageForFilename(d *Daemon, filename string) (storage, error) {
return newStorageWithConfig(d, storageType, config)
}
-func storageForImage(d *Daemon, imgInfo *shared.ImageInfo) (storage, error) {
+func storageForImage(d *Daemon, imgInfo *api.Image) (storage, error) {
imageFilename := shared.VarPath("images", imgInfo.Fingerprint)
return storageForFilename(d, imageFilename)
}
diff --git a/shared/api/image.go b/shared/api/image.go
new file mode 100644
index 0000000..396aecc
--- /dev/null
+++ b/shared/api/image.go
@@ -0,0 +1,81 @@
+package api
+
+import (
+ "time"
+)
+
+// ImagesPost represents the fields available for a new LXD image
+type ImagesPost struct {
+ ImagePut
+
+ CompressionAlgorithm string `json:"compression_algorithm"`
+ Filename string `json:"filename"`
+ Source map[string]string `json:"source"`
+}
+
+// ImagePut represents the modifiable fields of a LXD image
+type ImagePut struct {
+ AutoUpdate bool `json:"auto_update"`
+ Properties map[string]string `json:"properties"`
+ Public bool `json:"public"`
+}
+
+// Image represents a LXD image
+type Image struct {
+ ImagePut
+
+ Aliases []ImageAlias `json:"aliases"`
+ Architecture string `json:"architecture"`
+ Cached bool `json:"cached"`
+ Filename string `json:"filename"`
+ Fingerprint string `json:"fingerprint"`
+ Size int64 `json:"size"`
+ UpdateSource *ImageSource `json:"update_source,omitempty"`
+
+ CreatedAt time.Time `json:"created_at"`
+ ExpiresAt time.Time `json:"expires_at"`
+ LastUsedAt time.Time `json:"last_used_at"`
+ UploadedAt time.Time `json:"uploaded_at"`
+}
+
+// Convert a full Image struct into a ImagePut struct (filters read-only fields)
+func (img *Image) Writable() ImagePut {
+ return img.ImagePut
+}
+
+// ImageAlias represents an alias from the alias list of a LXD image
+type ImageAlias struct {
+ Name string `json:"name"`
+ Description string `json:"description"`
+}
+
+// ImageSource represents the source of a LXD image
+type ImageSource struct {
+ Alias string `json:"alias"`
+ Certificate string `json:"certificate"`
+ Protocol string `json:"protocol"`
+ Server string `json:"server"`
+}
+
+// ImageAliasesPost represents a new LXD image alias
+type ImageAliasesPost struct {
+ ImageAliasesEntry
+}
+
+// ImageAliasesEntryPost represents the required fields to rename a LXD image alias
+type ImageAliasesEntryPost struct {
+ Name string `json:"name"`
+}
+
+// ImageAliasesEntryPut represents the modifiable fields of a LXD image alias
+type ImageAliasesEntryPut struct {
+ Description string `json:"description"`
+ Target string `json:"target"`
+}
+
+// ImageAliasesEntry represents a LXD image alias
+type ImageAliasesEntry struct {
+ ImageAliasesEntryPut
+
+ Name string `json:"name"`
+}
diff --git a/shared/image.go b/shared/image.go
deleted file mode 100644
index e2e39d4..0000000
--- a/shared/image.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package shared
-
-import (
- "time"
-)
-
-type ImageProperties map[string]string
-
-type ImageAliasesEntry struct {
- Name string `json:"name"`
- Description string `json:"description"`
- Target string `json:"target"`
-}
-
-type ImageAliases []ImageAliasesEntry
-
-type ImageAlias struct {
- Name string `json:"name"`
- Description string `json:"description"`
-}
-
-type ImageSource struct {
- Server string `json:"server"`
- Protocol string `json:"protocol"`
- Certificate string `json:"certificate"`
- Alias string `json:"alias"`
-}
-
-type ImageInfo struct {
- Aliases []ImageAlias `json:"aliases"`
- Architecture string `json:"architecture"`
- Cached bool `json:"cached"`
- Filename string `json:"filename"`
- Fingerprint string `json:"fingerprint"`
- Properties map[string]string `json:"properties"`
- Public bool `json:"public"`
- Size int64 `json:"size"`
-
- AutoUpdate bool `json:"auto_update"`
- Source *ImageSource `json:"update_source,omitempty"`
-
- CreationDate time.Time `json:"created_at"`
- ExpiryDate time.Time `json:"expires_at"`
- LastUsedDate time.Time `json:"last_used_at"`
- UploadDate time.Time `json:"uploaded_at"`
-}
-
-/*
- * BriefImageInfo contains a subset of the fields in
- * ImageInfo, namely those which a user may update
- */
-type BriefImageInfo struct {
- AutoUpdate bool `json:"auto_update"`
- Properties map[string]string `json:"properties"`
- Public bool `json:"public"`
-}
-
-func (i *ImageInfo) Brief() BriefImageInfo {
- retstate := BriefImageInfo{
- AutoUpdate: i.AutoUpdate,
- Properties: i.Properties,
- Public: i.Public}
- return retstate
-}
diff --git a/shared/simplestreams/simplestreams.go b/shared/simplestreams/simplestreams.go
index 9be22d7..dd5d206 100644
--- a/shared/simplestreams/simplestreams.go
+++ b/shared/simplestreams/simplestreams.go
@@ -14,11 +14,12 @@ import (
"time"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/ioprogress"
"github.com/lxc/lxd/shared/osarch"
)
-type ssSortImage []shared.ImageInfo
+type ssSortImage []api.Image
func (a ssSortImage) Len() int {
return len(a)
@@ -31,15 +32,15 @@ func (a ssSortImage) Swap(i, j int) {
func (a ssSortImage) Less(i, j int) bool {
if a[i].Properties["os"] == a[j].Properties["os"] {
if a[i].Properties["release"] == a[j].Properties["release"] {
- if a[i].CreationDate.UTC().Unix() == 0 {
+ if a[i].CreatedAt.UTC().Unix() == 0 {
return true
}
- if a[j].CreationDate.UTC().Unix() == 0 {
+ if a[j].CreatedAt.UTC().Unix() == 0 {
return false
}
- return a[i].CreationDate.UTC().Unix() > a[j].CreationDate.UTC().Unix()
+ return a[i].CreatedAt.UTC().Unix() > a[j].CreatedAt.UTC().Unix()
}
if a[i].Properties["release"] == "" {
@@ -76,10 +77,10 @@ type SimpleStreamsManifest struct {
Products map[string]SimpleStreamsManifestProduct `json:"products"`
}
-func (s *SimpleStreamsManifest) ToLXD() ([]shared.ImageInfo, map[string][][]string) {
+func (s *SimpleStreamsManifest) ToLXD() ([]api.Image, map[string][][]string) {
downloads := map[string][][]string{}
- images := []shared.ImageInfo{}
+ images := []api.Image{}
nameLayout := "20060102"
eolLayout := "2006-01-02"
@@ -169,12 +170,12 @@ func (s *SimpleStreamsManifest) ToLXD() ([]shared.ImageInfo, map[string][][]stri
}
description = fmt.Sprintf("%s (%s)", description, name)
- image := shared.ImageInfo{}
+ image := api.Image{}
image.Architecture = architectureName
image.Public = true
image.Size = size
- image.CreationDate = creationDate
- image.UploadDate = creationDate
+ image.CreatedAt = creationDate
+ image.UploadedAt = creationDate
image.Filename = filename
image.Fingerprint = fingerprint
image.Properties = map[string]string{
@@ -189,9 +190,9 @@ func (s *SimpleStreamsManifest) ToLXD() ([]shared.ImageInfo, map[string][][]stri
// Add the provided aliases
if product.Aliases != "" {
- image.Aliases = []shared.ImageAlias{}
+ image.Aliases = []api.ImageAlias{}
for _, entry := range strings.Split(product.Aliases, ",") {
- image.Aliases = append(image.Aliases, shared.ImageAlias{Name: entry})
+ image.Aliases = append(image.Aliases, api.ImageAlias{Name: entry})
}
}
@@ -203,11 +204,11 @@ func (s *SimpleStreamsManifest) ToLXD() ([]shared.ImageInfo, map[string][][]stri
}
// Attempt to parse the EOL
- image.ExpiryDate = time.Unix(0, 0).UTC()
+ image.ExpiresAt = time.Unix(0, 0).UTC()
if product.SupportedEOL != "" {
eolDate, err := time.Parse(eolLayout, product.SupportedEOL)
if err == nil {
- image.ExpiryDate = eolDate
+ image.ExpiresAt = eolDate
}
}
@@ -278,8 +279,8 @@ type SimpleStreams struct {
cachedIndex *SimpleStreamsIndex
cachedManifest map[string]*SimpleStreamsManifest
- cachedImages []shared.ImageInfo
- cachedAliases map[string]*shared.ImageAliasesEntry
+ cachedImages []api.Image
+ cachedAliases map[string]*api.ImageAliasesEntry
}
func (s *SimpleStreams) parseIndex() (*SimpleStreamsIndex, error) {
@@ -356,8 +357,8 @@ func (s *SimpleStreams) parseManifest(path string) (*SimpleStreamsManifest, erro
return &ssManifest, nil
}
-func (s *SimpleStreams) applyAliases(images []shared.ImageInfo) ([]shared.ImageInfo, map[string]*shared.ImageAliasesEntry, error) {
- aliases := map[string]*shared.ImageAliasesEntry{}
+func (s *SimpleStreams) applyAliases(images []api.Image) ([]api.Image, map[string]*api.ImageAliasesEntry, error) {
+ aliases := map[string]*api.ImageAliasesEntry{}
sort.Sort(ssSortImage(images))
@@ -369,7 +370,7 @@ func (s *SimpleStreams) applyAliases(images []shared.ImageInfo) ([]shared.ImageI
}
}
- addAlias := func(name string, fingerprint string) *shared.ImageAlias {
+ addAlias := func(name string, fingerprint string) *api.ImageAlias {
if defaultOS != "" {
name = strings.TrimPrefix(name, fmt.Sprintf("%s/", defaultOS))
}
@@ -378,17 +379,17 @@ func (s *SimpleStreams) applyAliases(images []shared.ImageInfo) ([]shared.ImageI
return nil
}
- alias := shared.ImageAliasesEntry{}
+ alias := api.ImageAliasesEntry{}
alias.Name = name
alias.Target = fingerprint
aliases[name] = &alias
- return &shared.ImageAlias{Name: name}
+ return &api.ImageAlias{Name: name}
}
architectureName, _ := osarch.ArchitectureGetLocal()
- newImages := []shared.ImageInfo{}
+ newImages := []api.Image{}
for _, image := range images {
if image.Aliases != nil {
// Build a new list of aliases from the provided ones
@@ -418,12 +419,12 @@ func (s *SimpleStreams) applyAliases(images []shared.ImageInfo) ([]shared.ImageI
return newImages, aliases, nil
}
-func (s *SimpleStreams) getImages() ([]shared.ImageInfo, map[string]*shared.ImageAliasesEntry, error) {
+func (s *SimpleStreams) getImages() ([]api.Image, map[string]*api.ImageAliasesEntry, error) {
if s.cachedImages != nil && s.cachedAliases != nil {
return s.cachedImages, s.cachedAliases, nil
}
- images := []shared.ImageInfo{}
+ images := []api.Image{}
// Load the main index
ssIndex, err := s.parseIndex()
@@ -573,13 +574,13 @@ func (s *SimpleStreams) downloadFile(path string, hash string, target string, pr
return nil
}
-func (s *SimpleStreams) ListAliases() (shared.ImageAliases, error) {
+func (s *SimpleStreams) ListAliases() ([]api.ImageAliasesEntry, error) {
_, aliasesMap, err := s.getImages()
if err != nil {
return nil, err
}
- aliases := shared.ImageAliases{}
+ aliases := []api.ImageAliasesEntry{}
for _, alias := range aliasesMap {
aliases = append(aliases, *alias)
@@ -588,7 +589,7 @@ func (s *SimpleStreams) ListAliases() (shared.ImageAliases, error) {
return aliases, nil
}
-func (s *SimpleStreams) ListImages() ([]shared.ImageInfo, error) {
+func (s *SimpleStreams) ListImages() ([]api.Image, error) {
images, _, err := s.getImages()
return images, err
}
@@ -607,7 +608,7 @@ func (s *SimpleStreams) GetAlias(name string) string {
return alias.Target
}
-func (s *SimpleStreams) GetImageInfo(fingerprint string) (*shared.ImageInfo, error) {
+func (s *SimpleStreams) GetImageInfo(fingerprint string) (*api.Image, error) {
images, _, err := s.getImages()
if err != nil {
return nil, err
From a660f0a782e112c6043450978e484c06f48fb347 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Mon, 19 Dec 2016 14:23:38 -0500
Subject: [PATCH 2/9] shared: Move REST API to new package: certificate
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
client.go | 4 ++--
lxd/certificates.go | 24 +++++++++---------------
shared/api/certificate.go | 28 ++++++++++++++++++++++++++++
shared/cert.go | 8 --------
4 files changed, 39 insertions(+), 25 deletions(-)
create mode 100644 shared/api/certificate.go
diff --git a/client.go b/client.go
index 1a61dd4..4c9757d 100644
--- a/client.go
+++ b/client.go
@@ -1235,7 +1235,7 @@ func (c *Client) ListAliases() ([]api.ImageAliasesEntry, error) {
return result, nil
}
-func (c *Client) CertificateList() ([]shared.CertInfo, error) {
+func (c *Client) CertificateList() ([]api.Certificate, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -1245,7 +1245,7 @@ func (c *Client) CertificateList() ([]shared.CertInfo, error) {
return nil, err
}
- var result []shared.CertInfo
+ var result []api.Certificate
if err := json.Unmarshal(resp.Metadata, &result); err != nil {
return nil, err
}
diff --git a/lxd/certificates.go b/lxd/certificates.go
index e31a0d2..0f3c42e 100644
--- a/lxd/certificates.go
+++ b/lxd/certificates.go
@@ -12,6 +12,7 @@ import (
"github.com/gorilla/mux"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/version"
)
@@ -19,14 +20,14 @@ func certificatesGet(d *Daemon, r *http.Request) Response {
recursion := d.isRecursionRequest(r)
if recursion {
- certResponses := []shared.CertInfo{}
+ certResponses := []api.Certificate{}
baseCerts, err := dbCertsGet(d.db)
if err != nil {
return SmartError(err)
}
for _, baseCert := range baseCerts {
- resp := shared.CertInfo{}
+ resp := api.Certificate{}
resp.Fingerprint = baseCert.Fingerprint
resp.Certificate = baseCert.Certificate
if baseCert.Type == 1 {
@@ -48,13 +49,6 @@ func certificatesGet(d *Daemon, r *http.Request) Response {
return SyncResponse(true, body)
}
-type certificatesPostBody struct {
- Type string `json:"type"`
- Certificate string `json:"certificate"`
- Name string `json:"name"`
- Password string `json:"password"`
-}
-
func readSavedClientCAList(d *Daemon) {
d.clientCerts = []x509.Certificate{}
@@ -94,7 +88,7 @@ func saveCert(d *Daemon, host string, cert *x509.Certificate) error {
func certificatesPost(d *Daemon, r *http.Request) Response {
// Parse the request
- req := certificatesPostBody{}
+ req := api.CertificatesPost{}
if err := shared.ReadToJSON(r.Body, &req); err != nil {
return BadRequest(err)
}
@@ -168,8 +162,8 @@ func certificateFingerprintGet(d *Daemon, r *http.Request) Response {
return SyncResponseETag(true, cert, cert)
}
-func doCertificateGet(d *Daemon, fingerprint string) (shared.CertInfo, error) {
- resp := shared.CertInfo{}
+func doCertificateGet(d *Daemon, fingerprint string) (api.Certificate, error) {
+ resp := api.Certificate{}
dbCertInfo, err := dbCertGet(d.db, fingerprint)
if err != nil {
@@ -202,7 +196,7 @@ func certificateFingerprintPut(d *Daemon, r *http.Request) Response {
return PreconditionFailed(err)
}
- req := shared.CertInfo{}
+ req := api.CertificatePut{}
if err := shared.ReadToJSON(r.Body, &req); err != nil {
return BadRequest(err)
}
@@ -242,10 +236,10 @@ func certificateFingerprintPatch(d *Daemon, r *http.Request) Response {
req.Type = value
}
- return doCertificateUpdate(d, fingerprint, req)
+ return doCertificateUpdate(d, fingerprint, req.Writable())
}
-func doCertificateUpdate(d *Daemon, fingerprint string, req shared.CertInfo) Response {
+func doCertificateUpdate(d *Daemon, fingerprint string, req api.CertificatePut) Response {
if req.Type != "client" {
return BadRequest(fmt.Errorf("Unknown request type %s", req.Type))
}
diff --git a/shared/api/certificate.go b/shared/api/certificate.go
new file mode 100644
index 0000000..6719d69
--- /dev/null
+++ b/shared/api/certificate.go
@@ -0,0 +1,28 @@
+package api
+
+// CertificatesPost represents the fields of a new LXD certificate
+type CertificatesPost struct {
+ CertificatePut
+
+ Certificate string `json:"certificate"`
+ Password string `json:"password"`
+}
+
+// CertificatePut represents the modifiable fields of a LXD certificate
+type CertificatePut struct {
+ Name string `json:"name"`
+ Type string `json:"type"`
+}
+
+// Certificate represents a LXD certificate
+type Certificate struct {
+ CertificatePut
+
+ Certificate string `json:"certificate"`
+ Fingerprint string `json:"fingerprint"`
+}
+
+// Convert a full Certificate struct into a CertificatePut struct (filters read-only fields)
+func (cert *Certificate) Writable() CertificatePut {
+ return cert.CertificatePut
+}
diff --git a/shared/cert.go b/shared/cert.go
index d1225e5..b264ee1 100644
--- a/shared/cert.go
+++ b/shared/cert.go
@@ -23,14 +23,6 @@ import (
"time"
)
-// CertInfo is the representation of a Certificate in the API.
-type CertInfo struct {
- Certificate string `json:"certificate"`
- Fingerprint string `json:"fingerprint"`
- Name string `json:"name"`
- Type string `json:"type"`
-}
-
/*
* Generate a list of names for which the certificate will be valid.
* This will include the hostname and ip address
From 9980f97220b91f8795a6d13daf0910b8b4461847 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Mon, 19 Dec 2016 14:40:07 -0500
Subject: [PATCH 3/9] shared: Move REST API to new package: network
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
client.go | 20 ++++++++------------
lxc/network.go | 7 ++++---
lxd/db_networks.go | 26 +++++++++++++++-----------
lxd/networks.go | 25 +++++++++++++------------
shared/api/network.go | 35 +++++++++++++++++++++++++++++++++++
shared/container.go | 8 --------
6 files changed, 75 insertions(+), 46 deletions(-)
create mode 100644 shared/api/network.go
diff --git a/client.go b/client.go
index 4c9757d..f60c8f6 100644
--- a/client.go
+++ b/client.go
@@ -2788,33 +2788,29 @@ func (c *Client) NetworkCreate(name string, config map[string]string) error {
return err
}
-func (c *Client) NetworkGet(name string) (shared.NetworkConfig, error) {
+func (c *Client) NetworkGet(name string) (api.Network, error) {
if c.Remote.Public {
- return shared.NetworkConfig{}, fmt.Errorf("This function isn't supported by public remotes.")
+ return api.Network{}, fmt.Errorf("This function isn't supported by public remotes.")
}
resp, err := c.get(fmt.Sprintf("networks/%s", name))
if err != nil {
- return shared.NetworkConfig{}, err
+ return api.Network{}, err
}
- network := shared.NetworkConfig{}
+ network := api.Network{}
if err := json.Unmarshal(resp.Metadata, &network); err != nil {
- return shared.NetworkConfig{}, err
+ return api.Network{}, err
}
return network, nil
}
-func (c *Client) NetworkPut(name string, network shared.NetworkConfig) error {
+func (c *Client) NetworkPut(name string, network api.NetworkPut) error {
if c.Remote.Public {
return fmt.Errorf("This function isn't supported by public remotes.")
}
- if network.Name != name {
- return fmt.Errorf("Cannot change network name")
- }
-
_, err := c.put(fmt.Sprintf("networks/%s", name), network, Sync)
return err
}
@@ -2828,7 +2824,7 @@ func (c *Client) NetworkDelete(name string) error {
return err
}
-func (c *Client) ListNetworks() ([]shared.NetworkConfig, error) {
+func (c *Client) ListNetworks() ([]api.Network, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -2838,7 +2834,7 @@ func (c *Client) ListNetworks() ([]shared.NetworkConfig, error) {
return nil, err
}
- networks := []shared.NetworkConfig{}
+ networks := []api.Network{}
if err := json.Unmarshal(resp.Metadata, &networks); err != nil {
return nil, err
}
diff --git a/lxc/network.go b/lxc/network.go
index 2be6398..777615b 100644
--- a/lxc/network.go
+++ b/lxc/network.go
@@ -13,6 +13,7 @@ import (
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/i18n"
"github.com/lxc/lxd/shared/termios"
)
@@ -303,7 +304,7 @@ func (c *networkCmd) doNetworkEdit(client *lxd.Client, name string) error {
return err
}
- newdata := shared.NetworkConfig{}
+ newdata := api.NetworkPut{}
err = yaml.Unmarshal(contents, &newdata)
if err != nil {
return err
@@ -330,7 +331,7 @@ func (c *networkCmd) doNetworkEdit(client *lxd.Client, name string) error {
for {
// Parse the text received from the editor
- newdata := shared.NetworkConfig{}
+ newdata := api.NetworkPut{}
err = yaml.Unmarshal(content, &newdata)
if err == nil {
err = client.NetworkPut(name, newdata)
@@ -458,7 +459,7 @@ func (c *networkCmd) doNetworkSet(client *lxd.Client, name string, args []string
network.Config[key] = value
- return client.NetworkPut(name, network)
+ return client.NetworkPut(name, network.Writable())
}
func (c *networkCmd) doNetworkShow(client *lxd.Client, name string) error {
diff --git a/lxd/db_networks.go b/lxd/db_networks.go
index 6ee23a3..6ddaa6d 100644
--- a/lxd/db_networks.go
+++ b/lxd/db_networks.go
@@ -7,7 +7,7 @@ import (
_ "github.com/mattn/go-sqlite3"
- "github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
)
func dbNetworks(db *sql.DB) ([]string, error) {
@@ -28,11 +28,11 @@ func dbNetworks(db *sql.DB) ([]string, error) {
return response, nil
}
-func dbNetworkGet(db *sql.DB, network string) (int64, *shared.NetworkConfig, error) {
+func dbNetworkGet(db *sql.DB, name string) (int64, *api.Network, error) {
id := int64(-1)
q := "SELECT id FROM networks WHERE name=?"
- arg1 := []interface{}{network}
+ arg1 := []interface{}{name}
arg2 := []interface{}{&id}
err := dbQueryRowScan(db, q, arg1, arg2)
if err != nil {
@@ -44,15 +44,17 @@ func dbNetworkGet(db *sql.DB, network string) (int64, *shared.NetworkConfig, err
return -1, nil, err
}
- return id, &shared.NetworkConfig{
- Name: network,
+ network := api.Network{
+ Name: name,
Managed: true,
Type: "bridge",
- Config: config,
- }, nil
+ }
+ network.Config = config
+
+ return id, &network, nil
}
-func dbNetworkGetInterface(db *sql.DB, devName string) (int64, *shared.NetworkConfig, error) {
+func dbNetworkGetInterface(db *sql.DB, devName string) (int64, *api.Network, error) {
id := int64(-1)
name := ""
value := ""
@@ -85,12 +87,14 @@ func dbNetworkGetInterface(db *sql.DB, devName string) (int64, *shared.NetworkCo
return -1, nil, err
}
- return id, &shared.NetworkConfig{
+ network := api.Network{
Name: name,
Managed: true,
Type: "bridge",
- Config: config,
- }, nil
+ }
+ network.Config = config
+
+ return id, &network, nil
}
func dbNetworkConfigGet(db *sql.DB, id int64) (map[string]string, error) {
diff --git a/lxd/networks.go b/lxd/networks.go
index bb5d3c6..8ad97ec 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -16,6 +16,7 @@ import (
log "gopkg.in/inconshreveable/log15.v2"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/version"
)
@@ -33,7 +34,7 @@ func networksGet(d *Daemon, r *http.Request) Response {
}
resultString := []string{}
- resultMap := []shared.NetworkConfig{}
+ resultMap := []api.Network{}
for _, iface := range ifs {
if recursion == 0 {
resultString = append(resultString, fmt.Sprintf("/%s/networks/%s", version.APIVersion, iface))
@@ -54,7 +55,7 @@ func networksGet(d *Daemon, r *http.Request) Response {
}
func networksPost(d *Daemon, r *http.Request) Response {
- req := shared.NetworkConfig{}
+ req := api.NetworksPost{}
// Parse the request
err := json.NewDecoder(r.Body).Decode(&req)
@@ -161,18 +162,18 @@ func networkGet(d *Daemon, r *http.Request) Response {
return SyncResponseETag(true, &n, etag)
}
-func doNetworkGet(d *Daemon, name string) (shared.NetworkConfig, error) {
+func doNetworkGet(d *Daemon, name string) (api.Network, error) {
// Get some information
osInfo, _ := net.InterfaceByName(name)
_, dbInfo, _ := dbNetworkGet(d.db, name)
// Sanity check
if osInfo == nil && dbInfo == nil {
- return shared.NetworkConfig{}, os.ErrNotExist
+ return api.Network{}, os.ErrNotExist
}
// Prepare the response
- n := shared.NetworkConfig{}
+ n := api.Network{}
n.Name = name
n.UsedBy = []string{}
n.Config = map[string]string{}
@@ -180,13 +181,13 @@ func doNetworkGet(d *Daemon, name string) (shared.NetworkConfig, error) {
// Look for containers using the interface
cts, err := dbContainersList(d.db, cTypeRegular)
if err != nil {
- return shared.NetworkConfig{}, err
+ return api.Network{}, err
}
for _, ct := range cts {
c, err := containerLoadByName(d, ct)
if err != nil {
- return shared.NetworkConfig{}, err
+ return api.Network{}, err
}
if networkIsInUse(c, n.Name) {
@@ -245,7 +246,7 @@ func networkDelete(d *Daemon, r *http.Request) Response {
func networkPost(d *Daemon, r *http.Request) Response {
name := mux.Vars(r)["name"]
- req := shared.NetworkConfig{}
+ req := api.NetworkPost{}
// Parse the request
err := json.NewDecoder(r.Body).Decode(&req)
@@ -305,7 +306,7 @@ func networkPut(d *Daemon, r *http.Request) Response {
return PreconditionFailed(err)
}
- req := shared.NetworkConfig{}
+ req := api.NetworkPut{}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return BadRequest(err)
}
@@ -330,7 +331,7 @@ func networkPatch(d *Daemon, r *http.Request) Response {
return PreconditionFailed(err)
}
- req := shared.NetworkConfig{}
+ req := api.NetworkPut{}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return BadRequest(err)
}
@@ -370,7 +371,7 @@ func doNetworkUpdate(d *Daemon, name string, oldConfig map[string]string, newCon
return NotFound
}
- err = n.Update(shared.NetworkConfig{Config: newConfig})
+ err = n.Update(api.NetworkPut{Config: newConfig})
if err != nil {
return SmartError(err)
}
@@ -1234,7 +1235,7 @@ func (n *network) Stop() error {
return nil
}
-func (n *network) Update(newNetwork shared.NetworkConfig) error {
+func (n *network) Update(newNetwork api.NetworkPut) error {
err := networkFillAuto(newNetwork.Config)
if err != nil {
return err
diff --git a/shared/api/network.go b/shared/api/network.go
new file mode 100644
index 0000000..0ce71dd
--- /dev/null
+++ b/shared/api/network.go
@@ -0,0 +1,35 @@
+package api
+
+// NetworksPost represents the fields of a new LXD network
+type NetworksPost struct {
+ NetworkPut
+
+ Managed bool `json:"managed"`
+ Name string `json:"name"`
+ Type string `json:"type"`
+}
+
+// NetworkPost represents the fields required to rename a LXD network
+type NetworkPost struct {
+ Name string `json:"name"`
+}
+
+// NetworkPut represents the modifiable fields of a LXD network
+type NetworkPut struct {
+ Config map[string]string `json:"config"`
+}
+
+// Network represents a LXD network
+type Network struct {
+ NetworkPut
+
+ Managed bool `json:"managed"`
+ Name string `json:"name"`
+ Type string `json:"type"`
+ UsedBy []string `json:"used_by"`
+}
+
+// Convert a full Network struct into a NetworkPut struct (filters read-only fields)
+func (network *Network) Writable() NetworkPut {
+ return network.NetworkPut
+}
diff --git a/shared/container.go b/shared/container.go
index 0af7f44..6fb85aa 100644
--- a/shared/container.go
+++ b/shared/container.go
@@ -153,14 +153,6 @@ type ProfileConfig struct {
UsedBy []string `json:"used_by"`
}
-type NetworkConfig struct {
- Name string `json:"name"`
- Config map[string]string `json:"config"`
- Managed bool `json:"managed"`
- Type string `json:"type"`
- UsedBy []string `json:"used_by"`
-}
-
func IsInt64(value string) error {
if value == "" {
return nil
From 1797f7ac7cb85f659ea35f3a1e589dfec6d750bd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Mon, 19 Dec 2016 20:57:36 -0500
Subject: [PATCH 4/9] shared: Move REST API to new package: server
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
client.go | 6 +-
lxc/config.go | 9 +--
lxd/api_1.0.go | 160 +++++++++++++++++++++++++--------------------------
shared/api/server.go | 46 +++++++++++++++
shared/server.go | 37 ------------
5 files changed, 133 insertions(+), 125 deletions(-)
create mode 100644 shared/api/server.go
delete mode 100644 shared/server.go
diff --git a/client.go b/client.go
index f60c8f6..80c9c23 100644
--- a/client.go
+++ b/client.go
@@ -1691,8 +1691,8 @@ func (c *Client) Delete(name string) (*Response, error) {
return c.delete(url, nil, Async)
}
-func (c *Client) ServerStatus() (*shared.ServerState, error) {
- ss := shared.ServerState{}
+func (c *Client) ServerStatus() (*api.Server, error) {
+ ss := api.Server{}
resp, err := c.GetServerConfig()
if err != nil {
@@ -2362,7 +2362,7 @@ func (c *Client) SetServerConfig(key string, value string) (*Response, error) {
return c.put("", ss, Sync)
}
-func (c *Client) UpdateServerConfig(ss shared.BriefServerState) (*Response, error) {
+func (c *Client) UpdateServerConfig(ss api.ServerPut) (*Response, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
diff --git a/lxc/config.go b/lxc/config.go
index 59c67cd..dc7c558 100644
--- a/lxc/config.go
+++ b/lxc/config.go
@@ -15,6 +15,7 @@ import (
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
"github.com/lxc/lxd/shared/termios"
@@ -341,7 +342,7 @@ func (c *configCmd) run(config *lxd.Config, args []string) error {
return err
}
- brief := config.Brief()
+ brief := config.Writable()
data, err = yaml.Marshal(&brief)
} else {
var brief shared.BriefContainerInfo
@@ -554,7 +555,7 @@ func (c *configCmd) doDaemonConfigEdit(client *lxd.Client) error {
return err
}
- newdata := shared.BriefServerState{}
+ newdata := api.ServerPut{}
err = yaml.Unmarshal(contents, &newdata)
if err != nil {
return err
@@ -570,7 +571,7 @@ func (c *configCmd) doDaemonConfigEdit(client *lxd.Client) error {
return err
}
- brief := config.Brief()
+ brief := config.Writable()
data, err := yaml.Marshal(&brief)
if err != nil {
return err
@@ -584,7 +585,7 @@ func (c *configCmd) doDaemonConfigEdit(client *lxd.Client) error {
for {
// Parse the text received from the editor
- newdata := shared.BriefServerState{}
+ newdata := api.ServerPut{}
err = yaml.Unmarshal(content, &newdata)
if err == nil {
_, err = client.UpdateServerConfig(newdata)
diff --git a/lxd/api_1.0.go b/lxd/api_1.0.go
index 43f692a..d4de314 100644
--- a/lxd/api_1.0.go
+++ b/lxd/api_1.0.go
@@ -11,6 +11,7 @@ import (
"gopkg.in/lxc/go-lxc.v2"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/osarch"
"github.com/lxc/lxd/shared/version"
)
@@ -46,7 +47,7 @@ var api10 = []Command{
}
func api10Get(d *Daemon, r *http.Request) Response {
- body := shared.Jmap{
+ srv := api.ServerUntrusted{
/* List of API extensions in the order they were added.
*
* The following kind of changes require an addition to api_extensions:
@@ -56,7 +57,7 @@ func api10Get(d *Daemon, r *http.Request) Response {
* - New argument inside an existing REST API call
* - New HTTPs authentication mechanisms or protocols
*/
- "api_extensions": []string{
+ APIExtensions: []string{
"storage_zfs_remove_snapshots",
"container_host_shutdown_timeout",
"container_syscall_filtering",
@@ -84,98 +85,95 @@ func api10Get(d *Daemon, r *http.Request) Response {
"network_firewall_filtering",
"network_routes",
},
-
- "api_status": "stable",
- "api_version": version.APIVersion,
+ APIStatus: "stable",
+ APIVersion: version.APIVersion,
+ Public: false,
+ Auth: "untrusted",
}
- if d.isTrustedClient(r) {
- body["auth"] = "trusted"
+ // If untrusted, return now
+ if !d.isTrustedClient(r) {
+ return SyncResponseETag(true, srv, nil)
+ }
- /*
- * Based on: https://groups.google.com/forum/#!topic/golang-nuts/Jel8Bb-YwX8
- * there is really no better way to do this, which is
- * unfortunate. Also, we ditch the more accepted CharsToString
- * version in that thread, since it doesn't seem as portable,
- * viz. github issue #206.
- */
- uname := syscall.Utsname{}
- if err := syscall.Uname(&uname); err != nil {
- return InternalError(err)
- }
+ srv.Auth = "trusted"
+
+ /*
+ * Based on: https://groups.google.com/forum/#!topic/golang-nuts/Jel8Bb-YwX8
+ * there is really no better way to do this, which is
+ * unfortunate. Also, we ditch the more accepted CharsToString
+ * version in that thread, since it doesn't seem as portable,
+ * viz. github issue #206.
+ */
+ uname := syscall.Utsname{}
+ if err := syscall.Uname(&uname); err != nil {
+ return InternalError(err)
+ }
- kernel := ""
- for _, c := range uname.Sysname {
- if c == 0 {
- break
- }
- kernel += string(byte(c))
+ kernel := ""
+ for _, c := range uname.Sysname {
+ if c == 0 {
+ break
}
+ kernel += string(byte(c))
+ }
- kernelVersion := ""
- for _, c := range uname.Release {
- if c == 0 {
- break
- }
- kernelVersion += string(byte(c))
+ kernelVersion := ""
+ for _, c := range uname.Release {
+ if c == 0 {
+ break
}
+ kernelVersion += string(byte(c))
+ }
- kernelArchitecture := ""
- for _, c := range uname.Machine {
- if c == 0 {
- break
- }
- kernelArchitecture += string(byte(c))
+ kernelArchitecture := ""
+ for _, c := range uname.Machine {
+ if c == 0 {
+ break
}
+ kernelArchitecture += string(byte(c))
+ }
- addresses, err := d.ListenAddresses()
- if err != nil {
- return InternalError(err)
- }
+ addresses, err := d.ListenAddresses()
+ if err != nil {
+ return InternalError(err)
+ }
- var certificate string
- if len(d.tlsConfig.Certificates) != 0 {
- certificate = string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: d.tlsConfig.Certificates[0].Certificate[0]}))
- }
+ var certificate string
+ if len(d.tlsConfig.Certificates) != 0 {
+ certificate = string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: d.tlsConfig.Certificates[0].Certificate[0]}))
+ }
- architectures := []string{}
+ architectures := []string{}
- for _, architecture := range d.architectures {
- architectureName, err := osarch.ArchitectureName(architecture)
- if err != nil {
- return InternalError(err)
- }
- architectures = append(architectures, architectureName)
+ for _, architecture := range d.architectures {
+ architectureName, err := osarch.ArchitectureName(architecture)
+ if err != nil {
+ return InternalError(err)
}
-
- env := shared.Jmap{
- "addresses": addresses,
- "architectures": architectures,
- "certificate": certificate,
- "driver": "lxc",
- "driver_version": lxc.Version(),
- "kernel": kernel,
- "kernel_architecture": kernelArchitecture,
- "kernel_version": kernelVersion,
- "storage": d.Storage.GetStorageTypeName(),
- "storage_version": d.Storage.GetStorageTypeVersion(),
- "server": "lxd",
- "server_pid": os.Getpid(),
- "server_version": version.Version}
-
- body["environment"] = env
- body["public"] = false
- body["config"] = daemonConfigRender()
- } else {
- body["auth"] = "untrusted"
- body["public"] = false
+ architectures = append(architectures, architectureName)
}
- return SyncResponseETag(true, body, body["config"])
-}
-
-type apiPut struct {
- Config shared.Jmap `json:"config"`
+ env := api.ServerEnvironment{
+ Addresses: addresses,
+ Architectures: architectures,
+ Certificate: certificate,
+ Driver: "lxc",
+ DriverVersion: lxc.Version(),
+ Kernel: kernel,
+ KernelArchitecture: kernelArchitecture,
+ KernelVersion: kernelVersion,
+ Storage: d.Storage.GetStorageTypeName(),
+ StorageVersion: d.Storage.GetStorageTypeVersion(),
+ Server: "lxd",
+ ServerPid: os.Getpid(),
+ ServerVersion: version.Version}
+
+ fullSrv := api.Server{ServerUntrusted: srv}
+ fullSrv.Environment = env
+ fullSrv.Config = daemonConfigRender()
+
+ return SyncResponseETag(true, fullSrv, fullSrv.Config)
}
func api10Put(d *Daemon, r *http.Request) Response {
@@ -189,7 +187,7 @@ func api10Put(d *Daemon, r *http.Request) Response {
return PreconditionFailed(err)
}
- req := apiPut{}
+ req := api.ServerPut{}
if err := shared.ReadToJSON(r.Body, &req); err != nil {
return BadRequest(err)
}
@@ -208,7 +206,7 @@ func api10Patch(d *Daemon, r *http.Request) Response {
return PreconditionFailed(err)
}
- req := apiPut{}
+ req := api.ServerPut{}
if err := shared.ReadToJSON(r.Body, &req); err != nil {
return BadRequest(err)
}
@@ -227,7 +225,7 @@ func api10Patch(d *Daemon, r *http.Request) Response {
return doApi10Update(d, oldConfig, req)
}
-func doApi10Update(d *Daemon, oldConfig map[string]string, req apiPut) Response {
+func doApi10Update(d *Daemon, oldConfig map[string]string, req api.ServerPut) Response {
// Deal with special keys
for k, v := range req.Config {
config := daemonConfig[k]
diff --git a/shared/api/server.go b/shared/api/server.go
new file mode 100644
index 0000000..f453cf1
--- /dev/null
+++ b/shared/api/server.go
@@ -0,0 +1,46 @@
+package api
+
+// ServerEnvironment represents the read-only environment fields of a LXD server
+type ServerEnvironment struct {
+ Addresses []string `json:"addresses"`
+ Architectures []string `json:"architectures"`
+ Certificate string `json:"certificate"`
+ CertificateFingerprint string `json:"certificate_fingerprint"`
+ Driver string `json:"driver"`
+ DriverVersion string `json:"driver_version"`
+ Kernel string `json:"kernel"`
+ KernelArchitecture string `json:"kernel_architecture"`
+ KernelVersion string `json:"kernel_version"`
+ Server string `json:"server"`
+ ServerPid int `json:"server_pid"`
+ ServerVersion string `json:"server_version"`
+ Storage string `json:"storage"`
+ StorageVersion string `json:"storage_version"`
+}
+
+// ServerPut represents the modifiable fields of a LXD server configuration
+type ServerPut struct {
+ Config map[string]interface{} `json:"config"`
+}
+
+// ServerUntrusted represents a LXD server for an untrusted client
+type ServerUntrusted struct {
+ APIExtensions []string `json:"api_extensions"`
+ APIStatus string `json:"api_status"`
+ APIVersion string `json:"api_version"`
+ Auth string `json:"auth"`
+ Public bool `json:"public"`
+}
+
+// Server represents a LXD server
+type Server struct {
+ ServerPut
+ ServerUntrusted
+
+ Environment ServerEnvironment `json:"environment"`
+}
+
+// Convert a full Server struct into a ServerPut struct (filters read-only fields)
+func (srv *Server) Writable() ServerPut {
+ return srv.ServerPut
+}
diff --git a/shared/server.go b/shared/server.go
deleted file mode 100644
index 96044da..0000000
--- a/shared/server.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package shared
-
-type ServerStateEnvironment struct {
- Addresses []string `json:"addresses"`
- Architectures []string `json:"architectures"`
- Certificate string `json:"certificate"`
- CertificateFingerprint string `json:"certificate_fingerprint"`
- Driver string `json:"driver"`
- DriverVersion string `json:"driver_version"`
- Kernel string `json:"kernel"`
- KernelArchitecture string `json:"kernel_architecture"`
- KernelVersion string `json:"kernel_version"`
- Server string `json:"server"`
- ServerPid int `json:"server_pid"`
- ServerVersion string `json:"server_version"`
- Storage string `json:"storage"`
- StorageVersion string `json:"storage_version"`
-}
-
-type ServerState struct {
- APIExtensions []string `json:"api_extensions"`
- APIStatus string `json:"api_status"`
- APIVersion string `json:"api_version"`
- Auth string `json:"auth"`
- Environment ServerStateEnvironment `json:"environment"`
- Config map[string]interface{} `json:"config"`
- Public bool `json:"public"`
-}
-
-type BriefServerState struct {
- Config map[string]interface{} `json:"config"`
-}
-
-func (c *ServerState) Brief() BriefServerState {
- retstate := BriefServerState{Config: c.Config}
- return retstate
-}
From aa8cecd978ad0a0c3ceef479419e04ebab80652b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Mon, 19 Dec 2016 22:41:47 -0500
Subject: [PATCH 5/9] shared: Move REST API to new package: status
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
client.go | 12 ++++++------
lxc/action.go | 3 ++-
lxc/delete.go | 5 +++--
lxc/init.go | 4 ++--
lxc/publish.go | 5 +++--
lxd/container_lxc.go | 27 +++++++++++++-------------
lxd/containers_get.go | 5 +++--
lxd/operations.go | 29 ++++++++++++++--------------
lxd/response.go | 27 +++++++++++++-------------
shared/api/status_code.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++
shared/container.go | 10 ++++++----
shared/operation.go | 4 +++-
shared/status.go | 49 -----------------------------------------------
13 files changed, 120 insertions(+), 109 deletions(-)
create mode 100644 shared/api/status_code.go
delete mode 100644 shared/status.go
diff --git a/client.go b/client.go
index 80c9c23..fd6c177 100644
--- a/client.go
+++ b/client.go
@@ -1643,11 +1643,11 @@ func (c *Client) Exec(name string, cmd []string, env map[string]string,
return -1, err
}
- if op.StatusCode == shared.Failure {
+ if op.StatusCode == api.Failure {
return -1, fmt.Errorf(op.Err)
}
- if op.StatusCode != shared.Success {
+ if op.StatusCode != api.Success {
return -1, fmt.Errorf("got bad op status %s", op.Status)
}
@@ -2241,7 +2241,7 @@ func (c *Client) WaitForSuccess(waitURL string) error {
return err
}
- if op.StatusCode == shared.Success {
+ if op.StatusCode == api.Success {
return nil
}
@@ -2254,7 +2254,7 @@ func (c *Client) WaitForSuccessOp(waitURL string) (*shared.Operation, error) {
return nil, err
}
- if op.StatusCode == shared.Success {
+ if op.StatusCode == api.Success {
return op, nil
}
@@ -2723,11 +2723,11 @@ func (c *Client) AsyncWaitMeta(resp *Response) (*shared.Jmap, error) {
return nil, err
}
- if op.StatusCode == shared.Failure {
+ if op.StatusCode == api.Failure {
return nil, fmt.Errorf(op.Err)
}
- if op.StatusCode != shared.Success {
+ if op.StatusCode != api.Success {
return nil, fmt.Errorf("got bad op status %s", op.Status)
}
diff --git a/lxc/action.go b/lxc/action.go
index 15b3569..ab66cfe 100644
--- a/lxc/action.go
+++ b/lxc/action.go
@@ -5,6 +5,7 @@ import (
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
)
@@ -76,7 +77,7 @@ func (c *actionCmd) run(config *lxd.Config, args []string) error {
}
// "start" for a frozen container means "unfreeze"
- if current.StatusCode == shared.Frozen {
+ if current.StatusCode == api.Frozen {
c.action = shared.Unfreeze
}
diff --git a/lxc/delete.go b/lxc/delete.go
index 866e5a3..6900fb3 100644
--- a/lxc/delete.go
+++ b/lxc/delete.go
@@ -8,6 +8,7 @@ import (
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
)
@@ -87,7 +88,7 @@ func (c *deleteCmd) run(config *lxd.Config, args []string) error {
return err
}
- if ct.StatusCode != 0 && ct.StatusCode != shared.Stopped {
+ if ct.StatusCode != 0 && ct.StatusCode != api.Stopped {
if !c.force {
return fmt.Errorf(i18n.G("The container is currently running, stop it first or pass --force."))
}
@@ -102,7 +103,7 @@ func (c *deleteCmd) run(config *lxd.Config, args []string) error {
return err
}
- if op.StatusCode == shared.Failure {
+ if op.StatusCode == api.Failure {
return fmt.Errorf(i18n.G("Stopping container failed!"))
}
diff --git a/lxc/init.go b/lxc/init.go
index 20d062b..c94c225 100644
--- a/lxc/init.go
+++ b/lxc/init.go
@@ -6,7 +6,7 @@ import (
"strings"
"github.com/lxc/lxd"
- "github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
)
@@ -260,7 +260,7 @@ func (c *initCmd) initProgressTracker(d *lxd.Client, progress *ProgressRenderer,
return
}
- if shared.StatusCode(md["status_code"].(float64)).IsFinal() {
+ if api.StatusCode(md["status_code"].(float64)).IsFinal() {
return
}
diff --git a/lxc/publish.go b/lxc/publish.go
index d45ed14..38c6bd6 100644
--- a/lxc/publish.go
+++ b/lxc/publish.go
@@ -5,6 +5,7 @@ import (
"strings"
"github.com/lxc/lxd"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
@@ -83,7 +84,7 @@ func (c *publishCmd) run(config *lxd.Config, args []string) error {
return err
}
- wasRunning := ct.StatusCode != 0 && ct.StatusCode != shared.Stopped
+ wasRunning := ct.StatusCode != 0 && ct.StatusCode != api.Stopped
wasEphemeral := ct.Ephemeral
if wasRunning {
@@ -109,7 +110,7 @@ func (c *publishCmd) run(config *lxd.Config, args []string) error {
return err
}
- if op.StatusCode == shared.Failure {
+ if op.StatusCode == api.Failure {
return fmt.Errorf(i18n.G("Stopping container failed!"))
}
defer s.Action(cName, shared.Start, -1, true, false)
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index a660370..f4b1221 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -26,6 +26,7 @@ import (
"github.com/lxc/lxd/lxd/types"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/osarch"
log "gopkg.in/inconshreveable/log15.v2"
@@ -164,17 +165,17 @@ func lxcValidConfig(rawLxc string) error {
return nil
}
-func lxcStatusCode(state lxc.State) shared.StatusCode {
- return map[int]shared.StatusCode{
- 1: shared.Stopped,
- 2: shared.Starting,
- 3: shared.Running,
- 4: shared.Stopping,
- 5: shared.Aborting,
- 6: shared.Freezing,
- 7: shared.Frozen,
- 8: shared.Thawed,
- 9: shared.Error,
+func lxcStatusCode(state lxc.State) api.StatusCode {
+ return map[int]api.StatusCode{
+ 1: api.Stopped,
+ 2: api.Starting,
+ 3: api.Running,
+ 4: api.Stopping,
+ 5: api.Aborting,
+ 6: api.Freezing,
+ 7: api.Frozen,
+ 8: api.Thawed,
+ 9: api.Error,
}[int(state)]
}
@@ -6278,12 +6279,12 @@ func (c *containerLXC) State() string {
// Load the go-lxc struct
err := c.initLXC()
if err != nil {
- return "BROKEN"
+ return "Broken"
}
state, err := c.getLxcState()
if err != nil {
- return shared.Error.String()
+ return api.Error.String()
}
return state.String()
}
diff --git a/lxd/containers_get.go b/lxd/containers_get.go
index 5c54107..d3719ec 100644
--- a/lxd/containers_get.go
+++ b/lxd/containers_get.go
@@ -6,6 +6,7 @@ import (
"time"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/version"
)
@@ -50,8 +51,8 @@ func doContainersGet(d *Daemon, recursion bool) (interface{}, error) {
if err != nil {
c = &shared.ContainerInfo{
Name: container,
- Status: shared.Error.String(),
- StatusCode: shared.Error}
+ Status: api.Error.String(),
+ StatusCode: api.Error}
}
resultList = append(resultList, c)
}
diff --git a/lxd/operations.go b/lxd/operations.go
index 52df1ec..dec968d 100644
--- a/lxd/operations.go
+++ b/lxd/operations.go
@@ -12,6 +12,7 @@ import (
"github.com/pborman/uuid"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/version"
)
@@ -39,7 +40,7 @@ type operation struct {
class operationClass
createdAt time.Time
updatedAt time.Time
- status shared.StatusCode
+ status api.StatusCode
url string
resources map[string][]string
metadata map[string]interface{}
@@ -97,21 +98,21 @@ func (op *operation) done() {
}
func (op *operation) Run() (chan error, error) {
- if op.status != shared.Pending {
+ if op.status != api.Pending {
return nil, fmt.Errorf("Only pending operations can be started")
}
chanRun := make(chan error, 1)
op.lock.Lock()
- op.status = shared.Running
+ op.status = api.Running
if op.onRun != nil {
go func(op *operation, chanRun chan error) {
err := op.onRun(op)
if err != nil {
op.lock.Lock()
- op.status = shared.Failure
+ op.status = api.Failure
op.err = SmartError(err).String()
op.lock.Unlock()
op.done()
@@ -125,7 +126,7 @@ func (op *operation) Run() (chan error, error) {
}
op.lock.Lock()
- op.status = shared.Success
+ op.status = api.Success
op.lock.Unlock()
op.done()
chanRun <- nil
@@ -147,7 +148,7 @@ func (op *operation) Run() (chan error, error) {
}
func (op *operation) Cancel() (chan error, error) {
- if op.status != shared.Running {
+ if op.status != api.Running {
return nil, fmt.Errorf("Only running operations can be cancelled")
}
@@ -159,11 +160,11 @@ func (op *operation) Cancel() (chan error, error) {
op.lock.Lock()
oldStatus := op.status
- op.status = shared.Cancelling
+ op.status = api.Cancelling
op.lock.Unlock()
if op.onCancel != nil {
- go func(op *operation, oldStatus shared.StatusCode, chanCancel chan error) {
+ go func(op *operation, oldStatus api.StatusCode, chanCancel chan error) {
err := op.onCancel(op)
if err != nil {
op.lock.Lock()
@@ -178,7 +179,7 @@ func (op *operation) Cancel() (chan error, error) {
}
op.lock.Lock()
- op.status = shared.Cancelled
+ op.status = api.Cancelled
op.lock.Unlock()
op.done()
chanCancel <- nil
@@ -195,7 +196,7 @@ func (op *operation) Cancel() (chan error, error) {
if op.onCancel == nil {
op.lock.Lock()
- op.status = shared.Cancelled
+ op.status = api.Cancelled
op.lock.Unlock()
op.done()
chanCancel <- nil
@@ -213,7 +214,7 @@ func (op *operation) Connect(r *http.Request, w http.ResponseWriter) (chan error
return nil, fmt.Errorf("Only websocket operations can be connected")
}
- if op.status != shared.Running {
+ if op.status != api.Running {
return nil, fmt.Errorf("Only running operations can be connected")
}
@@ -308,7 +309,7 @@ func (op *operation) WaitFinal(timeout int) (bool, error) {
}
func (op *operation) UpdateResources(opResources map[string][]string) error {
- if op.status != shared.Pending && op.status != shared.Running {
+ if op.status != api.Pending && op.status != api.Running {
return fmt.Errorf("Only pending or running operations can be updated")
}
@@ -329,7 +330,7 @@ func (op *operation) UpdateResources(opResources map[string][]string) error {
}
func (op *operation) UpdateMetadata(opMetadata interface{}) error {
- if op.status != shared.Pending && op.status != shared.Running {
+ if op.status != api.Pending && op.status != api.Running {
return fmt.Errorf("Only pending or running operations can be updated")
}
@@ -365,7 +366,7 @@ func operationCreate(opClass operationClass, opResources map[string][]string, op
op.class = opClass
op.createdAt = time.Now()
op.updatedAt = op.createdAt
- op.status = shared.Pending
+ op.status = api.Pending
op.url = fmt.Sprintf("/%s/operations/%s", version.APIVersion, op.id)
op.resources = opResources
op.chanDone = make(chan error)
diff --git a/lxd/response.go b/lxd/response.go
index ec3ea59..062e8e1 100644
--- a/lxd/response.go
+++ b/lxd/response.go
@@ -15,21 +15,22 @@ import (
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
)
type syncResp struct {
- Type lxd.ResponseType `json:"type"`
- Status string `json:"status"`
- StatusCode shared.StatusCode `json:"status_code"`
- Metadata interface{} `json:"metadata"`
+ Type lxd.ResponseType `json:"type"`
+ Status string `json:"status"`
+ StatusCode api.StatusCode `json:"status_code"`
+ Metadata interface{} `json:"metadata"`
}
type asyncResp struct {
- Type lxd.ResponseType `json:"type"`
- Status string `json:"status"`
- StatusCode shared.StatusCode `json:"status_code"`
- Metadata interface{} `json:"metadata"`
- Operation string `json:"operation"`
+ Type lxd.ResponseType `json:"type"`
+ Status string `json:"status"`
+ StatusCode api.StatusCode `json:"status_code"`
+ Metadata interface{} `json:"metadata"`
+ Operation string `json:"operation"`
}
type Response interface {
@@ -56,9 +57,9 @@ func (r *syncResponse) Render(w http.ResponseWriter) error {
}
// Prepare the JSON response
- status := shared.Success
+ status := api.Success
if !r.success {
- status = shared.Failure
+ status = api.Failure
}
if r.headers != nil {
@@ -233,8 +234,8 @@ func (r *operationResponse) Render(w http.ResponseWriter) error {
body := asyncResp{
Type: lxd.Async,
- Status: shared.OperationCreated.String(),
- StatusCode: shared.OperationCreated,
+ Status: api.OperationCreated.String(),
+ StatusCode: api.OperationCreated,
Operation: url,
Metadata: md}
diff --git a/shared/api/status_code.go b/shared/api/status_code.go
new file mode 100644
index 0000000..74ec4eb
--- /dev/null
+++ b/shared/api/status_code.go
@@ -0,0 +1,49 @@
+package api
+
+type StatusCode int
+
+const (
+ OperationCreated StatusCode = 100
+ Started StatusCode = 101
+ Stopped StatusCode = 102
+ Running StatusCode = 103
+ Cancelling StatusCode = 104
+ Pending StatusCode = 105
+ Starting StatusCode = 106
+ Stopping StatusCode = 107
+ Aborting StatusCode = 108
+ Freezing StatusCode = 109
+ Frozen StatusCode = 110
+ Thawed StatusCode = 111
+ Error StatusCode = 112
+
+ Success StatusCode = 200
+
+ Failure StatusCode = 400
+ Cancelled StatusCode = 401
+)
+
+func (o StatusCode) String() string {
+ return map[StatusCode]string{
+ OperationCreated: "Operation created",
+ Started: "Started",
+ Stopped: "Stopped",
+ Running: "Running",
+ Cancelling: "Cancelling",
+ Pending: "Pending",
+ Success: "Success",
+ Failure: "Failure",
+ Cancelled: "Cancelled",
+ Starting: "Starting",
+ Stopping: "Stopping",
+ Aborting: "Aborting",
+ Freezing: "Freezing",
+ Frozen: "Frozen",
+ Thawed: "Thawed",
+ Error: "Error",
+ }[o]
+}
+
+func (o StatusCode) IsFinal() bool {
+ return int(o) >= 200
+}
diff --git a/shared/container.go b/shared/container.go
index 6fb85aa..5d0052b 100644
--- a/shared/container.go
+++ b/shared/container.go
@@ -6,11 +6,13 @@ import (
"strings"
"syscall"
"time"
+
+ "github.com/lxc/lxd/shared/api"
)
type ContainerState struct {
Status string `json:"status"`
- StatusCode StatusCode `json:"status_code"`
+ StatusCode api.StatusCode `json:"status_code"`
CPU ContainerStateCPU `json:"cpu"`
Disk map[string]ContainerStateDisk `json:"disk"`
Memory ContainerStateMemory `json:"memory"`
@@ -91,14 +93,14 @@ type ContainerInfo struct {
Profiles []string `json:"profiles"`
Stateful bool `json:"stateful"`
Status string `json:"status"`
- StatusCode StatusCode `json:"status_code"`
+ StatusCode api.StatusCode `json:"status_code"`
}
func (c ContainerInfo) IsActive() bool {
switch c.StatusCode {
- case Stopped:
+ case api.Stopped:
return false
- case Error:
+ case api.Error:
return false
default:
return true
diff --git a/shared/operation.go b/shared/operation.go
index 121be2e..7abe917 100644
--- a/shared/operation.go
+++ b/shared/operation.go
@@ -2,6 +2,8 @@ package shared
import (
"time"
+
+ "github.com/lxc/lxd/shared/api"
)
type Operation struct {
@@ -10,7 +12,7 @@ type Operation struct {
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Status string `json:"status"`
- StatusCode StatusCode `json:"status_code"`
+ StatusCode api.StatusCode `json:"status_code"`
Resources map[string][]string `json:"resources"`
Metadata *Jmap `json:"metadata"`
MayCancel bool `json:"may_cancel"`
diff --git a/shared/status.go b/shared/status.go
deleted file mode 100644
index 7651f5c..0000000
--- a/shared/status.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package shared
-
-type StatusCode int
-
-const (
- OperationCreated StatusCode = 100
- Started StatusCode = 101
- Stopped StatusCode = 102
- Running StatusCode = 103
- Cancelling StatusCode = 104
- Pending StatusCode = 105
- Starting StatusCode = 106
- Stopping StatusCode = 107
- Aborting StatusCode = 108
- Freezing StatusCode = 109
- Frozen StatusCode = 110
- Thawed StatusCode = 111
- Error StatusCode = 112
-
- Success StatusCode = 200
-
- Failure StatusCode = 400
- Cancelled StatusCode = 401
-)
-
-func (o StatusCode) String() string {
- return map[StatusCode]string{
- OperationCreated: "Operation created",
- Started: "Started",
- Stopped: "Stopped",
- Running: "Running",
- Cancelling: "Cancelling",
- Pending: "Pending",
- Success: "Success",
- Failure: "Failure",
- Cancelled: "Cancelled",
- Starting: "Starting",
- Stopping: "Stopping",
- Aborting: "Aborting",
- Freezing: "Freezing",
- Frozen: "Frozen",
- Thawed: "Thawed",
- Error: "Error",
- }[o]
-}
-
-func (o StatusCode) IsFinal() bool {
- return int(o) >= 200
-}
From 2b9e2be79f42b09012d29ce79af972e404362add Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 20 Dec 2016 14:47:50 -0500
Subject: [PATCH 6/9] shared: Move REST API to new package: operation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
client.go | 33 +++++++++++++++++----------------
lxc/copy.go | 2 +-
lxd/operations.go | 12 +++++-------
shared/api/operation.go | 19 +++++++++++++++++++
shared/operation.go | 20 --------------------
5 files changed, 42 insertions(+), 44 deletions(-)
create mode 100644 shared/api/operation.go
delete mode 100644 shared/operation.go
diff --git a/client.go b/client.go
index fd6c177..3ff0c19 100644
--- a/client.go
+++ b/client.go
@@ -90,8 +90,8 @@ func (r *Response) MetadataAsMap() (*shared.Jmap, error) {
return &ret, nil
}
-func (r *Response) MetadataAsOperation() (*shared.Operation, error) {
- op := shared.Operation{}
+func (r *Response) MetadataAsOperation() (*api.Operation, error) {
+ op := api.Operation{}
if err := json.Unmarshal(r.Metadata, &op); err != nil {
return nil, err
}
@@ -660,7 +660,7 @@ func (c *Client) CopyImage(image string, dest *Client, copy_aliases bool, aliase
return err
}
- secret, err = op.Metadata.GetString("secret")
+ secret, err = shared.Jmap(op.Metadata).GetString("secret")
if err != nil {
return err
}
@@ -730,7 +730,7 @@ func (c *Client) CopyImage(image string, dest *Client, copy_aliases bool, aliase
}
if op.Metadata != nil {
- value, err := op.Metadata.GetString("fingerprint")
+ value, err := shared.Jmap(op.Metadata).GetString("fingerprint")
if err == nil {
fingerprint = value
}
@@ -957,7 +957,7 @@ func (c *Client) PostImageURL(imageFile string, properties []string, public bool
return "", fmt.Errorf("Missing operation metadata")
}
- fingerprint, err := op.Metadata.GetString("fingerprint")
+ fingerprint, err := shared.Jmap(op.Metadata).GetString("fingerprint")
if err != nil {
return "", err
}
@@ -1115,12 +1115,12 @@ func (c *Client) PostImage(imageFile string, rootfsFile string, properties []str
return "", err
}
- jmap, err := c.AsyncWaitMeta(resp)
+ meta, err := c.AsyncWaitMeta(resp)
if err != nil {
return "", err
}
- fingerprint, err := jmap.GetString("fingerprint")
+ fingerprint, err := shared.Jmap(meta).GetString("fingerprint")
if err != nil {
return "", err
}
@@ -1376,7 +1376,7 @@ func (c *Client) Init(name string, imgremote string, image string, profiles *[]s
return nil, err
}
- secret, err = op.Metadata.GetString("secret")
+ secret, err = shared.Jmap(op.Metadata).GetString("secret")
if err != nil {
return nil, err
}
@@ -1568,7 +1568,7 @@ func (c *Client) Exec(name string, cmd []string, env map[string]string,
return -1, err
}
- fds, err = op.Metadata.GetMap("fds")
+ fds, err = shared.Jmap(op.Metadata).GetMap("fds")
if err != nil {
return -1, err
}
@@ -1655,7 +1655,7 @@ func (c *Client) Exec(name string, cmd []string, env map[string]string,
return -1, fmt.Errorf("no metadata received")
}
- return op.Metadata.GetInt("return")
+ return shared.Jmap(op.Metadata).GetInt("return")
}
func (c *Client) Action(name string, action shared.ContainerAction, timeout int, force bool, stateful bool) (*Response, error) {
@@ -2104,7 +2104,8 @@ func (c *Client) MigrateFrom(name string, operation string, certificate string,
if err != nil {
return nil, err
}
- for k, v := range *op.Metadata {
+
+ for k, v := range op.Metadata {
destSecrets[k] = v.(string)
}
@@ -2216,7 +2217,7 @@ func (c *Client) Rename(name string, newName string) (*Response, error) {
}
/* Wait for an operation */
-func (c *Client) WaitFor(waitURL string) (*shared.Operation, error) {
+func (c *Client) WaitFor(waitURL string) (*api.Operation, error) {
if len(waitURL) < 1 {
return nil, fmt.Errorf("invalid wait url %s", waitURL)
}
@@ -2248,7 +2249,7 @@ func (c *Client) WaitForSuccess(waitURL string) error {
return fmt.Errorf(op.Err)
}
-func (c *Client) WaitForSuccessOp(waitURL string) (*shared.Operation, error) {
+func (c *Client) WaitForSuccessOp(waitURL string) (*api.Operation, error) {
op, err := c.WaitFor(waitURL)
if err != nil {
return nil, err
@@ -2717,7 +2718,7 @@ func (c *Client) ProfileCopy(name, newname string, dest *Client) error {
return err
}
-func (c *Client) AsyncWaitMeta(resp *Response) (*shared.Jmap, error) {
+func (c *Client) AsyncWaitMeta(resp *Response) (map[string]interface{}, error) {
op, err := c.WaitFor(resp.Operation)
if err != nil {
return nil, err
@@ -2754,12 +2755,12 @@ func (c *Client) ImageFromContainer(cname string, public bool, aliases []string,
return "", err
}
- jmap, err := c.AsyncWaitMeta(resp)
+ meta, err := c.AsyncWaitMeta(resp)
if err != nil {
return "", err
}
- fingerprint, err := jmap.GetString("fingerprint")
+ fingerprint, err := shared.Jmap(meta).GetString("fingerprint")
if err != nil {
return "", err
}
diff --git a/lxc/copy.go b/lxc/copy.go
index 9f8437a..4c98735 100644
--- a/lxc/copy.go
+++ b/lxc/copy.go
@@ -189,7 +189,7 @@ func (c *copyCmd) copyContainer(config *lxd.Config, sourceResource string, destR
return err
}
- for k, v := range *op.Metadata {
+ for k, v := range op.Metadata {
secrets[k] = v.(string)
}
diff --git a/lxd/operations.go b/lxd/operations.go
index dec968d..4ff6e0b 100644
--- a/lxd/operations.go
+++ b/lxd/operations.go
@@ -246,7 +246,7 @@ func (op *operation) mayCancel() bool {
return op.onCancel != nil || op.class == operationClassToken
}
-func (op *operation) Render() (string, *shared.Operation, error) {
+func (op *operation) Render() (string, *api.Operation, error) {
// Setup the resource URLs
resources := op.resources
if resources != nil {
@@ -261,9 +261,7 @@ func (op *operation) Render() (string, *shared.Operation, error) {
resources = tmpResources
}
- md := shared.Jmap(op.metadata)
-
- return op.url, &shared.Operation{
+ return op.url, &api.Operation{
Id: op.id,
Class: op.class.String(),
CreatedAt: op.createdAt,
@@ -271,7 +269,7 @@ func (op *operation) Render() (string, *shared.Operation, error) {
Status: op.status.String(),
StatusCode: op.status,
Resources: resources,
- Metadata: &md,
+ Metadata: op.metadata,
MayCancel: op.mayCancel(),
Err: op.err,
}, nil
@@ -473,7 +471,7 @@ func operationsAPIGet(d *Daemon, r *http.Request) Response {
_, ok := md[status]
if !ok {
if recursion {
- md[status] = make([]*shared.Operation, 0)
+ md[status] = make([]*api.Operation, 0)
} else {
md[status] = make([]string, 0)
}
@@ -489,7 +487,7 @@ func operationsAPIGet(d *Daemon, r *http.Request) Response {
continue
}
- md[status] = append(md[status].([]*shared.Operation), body)
+ md[status] = append(md[status].([]*api.Operation), body)
}
return SyncResponse(true, md)
diff --git a/shared/api/operation.go b/shared/api/operation.go
new file mode 100644
index 0000000..5529f4d
--- /dev/null
+++ b/shared/api/operation.go
@@ -0,0 +1,19 @@
+package api
+
+import (
+ "time"
+)
+
+// Operation represents a LXD background operation
+type Operation struct {
+ Id string `json:"id"`
+ Class string `json:"class"`
+ CreatedAt time.Time `json:"created_at"`
+ UpdatedAt time.Time `json:"updated_at"`
+ Status string `json:"status"`
+ StatusCode StatusCode `json:"status_code"`
+ Resources map[string][]string `json:"resources"`
+ Metadata map[string]interface{} `json:"metadata"`
+ MayCancel bool `json:"may_cancel"`
+ Err string `json:"err"`
+}
diff --git a/shared/operation.go b/shared/operation.go
deleted file mode 100644
index 7abe917..0000000
--- a/shared/operation.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package shared
-
-import (
- "time"
-
- "github.com/lxc/lxd/shared/api"
-)
-
-type Operation struct {
- Id string `json:"id"`
- Class string `json:"class"`
- CreatedAt time.Time `json:"created_at"`
- UpdatedAt time.Time `json:"updated_at"`
- Status string `json:"status"`
- StatusCode api.StatusCode `json:"status_code"`
- Resources map[string][]string `json:"resources"`
- Metadata *Jmap `json:"metadata"`
- MayCancel bool `json:"may_cancel"`
- Err string `json:"err"`
-}
From b8821ce800aa7b6c253e6c910735491848a5b3be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 21 Dec 2016 18:40:50 -0500
Subject: [PATCH 7/9] shared: Move REST API to new package: profile
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
client.go | 14 +++++---------
lxc/config.go | 4 ++--
lxc/profile.go | 5 +++--
lxd/db_profiles.go | 25 ++++++++++++++-----------
lxd/profiles.go | 23 ++++++++---------------
shared/api/profile.go | 33 +++++++++++++++++++++++++++++++++
shared/container.go | 8 --------
7 files changed, 65 insertions(+), 47 deletions(-)
create mode 100644 shared/api/profile.go
diff --git a/client.go b/client.go
index 3ff0c19..e935ed0 100644
--- a/client.go
+++ b/client.go
@@ -1767,12 +1767,12 @@ func (c *Client) GetLog(container string, log string) (io.Reader, error) {
return resp.Body, nil
}
-func (c *Client) ProfileConfig(name string) (*shared.ProfileConfig, error) {
+func (c *Client) ProfileConfig(name string) (*api.Profile, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
- ct := shared.ProfileConfig{}
+ ct := api.Profile{}
resp, err := c.get(fmt.Sprintf("profiles/%s", name))
if err != nil {
@@ -2494,20 +2494,16 @@ func (c *Client) SetProfileConfigItem(profile, key, value string) error {
return err
}
-func (c *Client) PutProfile(name string, profile shared.ProfileConfig) error {
+func (c *Client) PutProfile(name string, profile api.ProfilePut) error {
if c.Remote.Public {
return fmt.Errorf("This function isn't supported by public remotes.")
}
- if profile.Name != name {
- return fmt.Errorf("Cannot change profile name")
- }
-
_, err := c.put(fmt.Sprintf("profiles/%s", name), profile, Sync)
return err
}
-func (c *Client) ListProfiles() ([]shared.ProfileConfig, error) {
+func (c *Client) ListProfiles() ([]api.Profile, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -2517,7 +2513,7 @@ func (c *Client) ListProfiles() ([]shared.ProfileConfig, error) {
return nil, err
}
- profiles := []shared.ProfileConfig{}
+ profiles := []api.Profile{}
if err := json.Unmarshal(resp.Metadata, &profiles); err != nil {
return nil, err
}
diff --git a/lxc/config.go b/lxc/config.go
index dc7c558..1a91fa7 100644
--- a/lxc/config.go
+++ b/lxc/config.go
@@ -724,7 +724,7 @@ func (c *configCmd) deviceSet(config *lxd.Config, which string, args []string) e
dev[key] = value
st.Devices[devname] = dev
- err = client.PutProfile(name, *st)
+ err = client.PutProfile(name, st.Writable())
if err != nil {
return err
}
@@ -780,7 +780,7 @@ func (c *configCmd) deviceUnset(config *lxd.Config, which string, args []string)
delete(dev, key)
st.Devices[devname] = dev
- err = client.PutProfile(name, *st)
+ err = client.PutProfile(name, st.Writable())
if err != nil {
return err
}
diff --git a/lxc/profile.go b/lxc/profile.go
index 5d8a238..41ce1da 100644
--- a/lxc/profile.go
+++ b/lxc/profile.go
@@ -12,6 +12,7 @@ import (
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/i18n"
"github.com/lxc/lxd/shared/termios"
)
@@ -179,7 +180,7 @@ func (c *profileCmd) doProfileEdit(client *lxd.Client, p string) error {
return err
}
- newdata := shared.ProfileConfig{}
+ newdata := api.ProfilePut{}
err = yaml.Unmarshal(contents, &newdata)
if err != nil {
return err
@@ -206,7 +207,7 @@ func (c *profileCmd) doProfileEdit(client *lxd.Client, p string) error {
for {
// Parse the text received from the editor
- newdata := shared.ProfileConfig{}
+ newdata := api.ProfilePut{}
err = yaml.Unmarshal(content, &newdata)
if err == nil {
err = client.PutProfile(p, newdata)
diff --git a/lxd/db_profiles.go b/lxd/db_profiles.go
index d935848..abbb3ab 100644
--- a/lxd/db_profiles.go
+++ b/lxd/db_profiles.go
@@ -7,7 +7,7 @@ import (
_ "github.com/mattn/go-sqlite3"
"github.com/lxc/lxd/lxd/types"
- "github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
)
// dbProfiles returns a string list of profiles.
@@ -29,34 +29,37 @@ func dbProfiles(db *sql.DB) ([]string, error) {
return response, nil
}
-func dbProfileGet(db *sql.DB, profile string) (int64, *shared.ProfileConfig, error) {
+func dbProfileGet(db *sql.DB, name string) (int64, *api.Profile, error) {
id := int64(-1)
description := sql.NullString{}
q := "SELECT id, description FROM profiles WHERE name=?"
- arg1 := []interface{}{profile}
+ arg1 := []interface{}{name}
arg2 := []interface{}{&id, &description}
err := dbQueryRowScan(db, q, arg1, arg2)
if err != nil {
return -1, nil, err
}
- config, err := dbProfileConfig(db, profile)
+ config, err := dbProfileConfig(db, name)
if err != nil {
return -1, nil, err
}
- devices, err := dbDevices(db, profile, true)
+ devices, err := dbDevices(db, name, true)
if err != nil {
return -1, nil, err
}
- return id, &shared.ProfileConfig{
- Name: profile,
- Config: config,
- Description: description.String,
- Devices: devices,
- }, nil
+ profile := api.Profile{
+ Name: name,
+ }
+
+ profile.Config = config
+ profile.Description = description.String
+ profile.Devices = devices
+
+ return id, &profile, nil
}
func dbProfileCreate(db *sql.DB, profile string, description string, config map[string]string,
diff --git a/lxd/profiles.go b/lxd/profiles.go
index 98e852c..181d0ca 100644
--- a/lxd/profiles.go
+++ b/lxd/profiles.go
@@ -12,21 +12,14 @@ import (
"github.com/gorilla/mux"
_ "github.com/mattn/go-sqlite3"
- "github.com/lxc/lxd/lxd/types"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/version"
log "gopkg.in/inconshreveable/log15.v2"
)
/* This is used for both profiles post and profile put */
-type profilesPostReq struct {
- Name string `json:"name"`
- Config map[string]string `json:"config"`
- Description string `json:"description"`
- Devices types.Devices `json:"devices"`
-}
-
func profilesGet(d *Daemon, r *http.Request) Response {
results, err := dbProfiles(d.db)
if err != nil {
@@ -36,7 +29,7 @@ func profilesGet(d *Daemon, r *http.Request) Response {
recursion := d.isRecursionRequest(r)
resultString := make([]string, len(results))
- resultMap := make([]*shared.ProfileConfig, len(results))
+ resultMap := make([]*api.Profile, len(results))
i := 0
for _, name := range results {
if !recursion {
@@ -61,7 +54,7 @@ func profilesGet(d *Daemon, r *http.Request) Response {
}
func profilesPost(d *Daemon, r *http.Request) Response {
- req := profilesPostReq{}
+ req := api.ProfilesPost{}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return BadRequest(err)
}
@@ -109,7 +102,7 @@ var profilesCmd = Command{
get: profilesGet,
post: profilesPost}
-func doProfileGet(d *Daemon, name string) (*shared.ProfileConfig, error) {
+func doProfileGet(d *Daemon, name string) (*api.Profile, error) {
_, profile, err := dbProfileGet(d.db, name)
if err != nil {
return nil, err
@@ -174,7 +167,7 @@ func profilePut(d *Daemon, r *http.Request) Response {
return PreconditionFailed(err)
}
- req := profilesPostReq{}
+ req := api.ProfilePut{}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return BadRequest(err)
}
@@ -209,7 +202,7 @@ func profilePatch(d *Daemon, r *http.Request) Response {
return BadRequest(err)
}
- req := profilesPostReq{}
+ req := api.ProfilePut{}
if err := json.NewDecoder(rdr2).Decode(&req); err != nil {
return BadRequest(err)
}
@@ -247,7 +240,7 @@ func profilePatch(d *Daemon, r *http.Request) Response {
return doProfileUpdate(d, name, id, profile, req)
}
-func doProfileUpdate(d *Daemon, name string, id int64, profile *shared.ProfileConfig, req profilesPostReq) Response {
+func doProfileUpdate(d *Daemon, name string, id int64, profile *api.Profile, req api.ProfilePut) Response {
// Sanity checks
err := containerValidConfig(d, req.Config, true, false)
if err != nil {
@@ -339,7 +332,7 @@ func doProfileUpdate(d *Daemon, name string, id int64, profile *shared.ProfileCo
func profilePost(d *Daemon, r *http.Request) Response {
name := mux.Vars(r)["name"]
- req := profilesPostReq{}
+ req := api.ProfilePost{}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return BadRequest(err)
}
diff --git a/shared/api/profile.go b/shared/api/profile.go
new file mode 100644
index 0000000..861d08f
--- /dev/null
+++ b/shared/api/profile.go
@@ -0,0 +1,33 @@
+package api
+
+// ProfilesPost represents the fields of a new LXD profile
+type ProfilesPost struct {
+ ProfilePut
+
+ Name string `json:"name"`
+}
+
+// ProfilePost represents the fields required to rename a LXD profile
+type ProfilePost struct {
+ Name string `json:"name"`
+}
+
+// ProfilePut represents the modifiable fields of a LXD profile
+type ProfilePut struct {
+ Config map[string]string `json:"config"`
+ Description string `json:"description"`
+ Devices map[string]map[string]string `json:"devices"`
+}
+
+// Profile represents a LXD profile
+type Profile struct {
+ ProfilePut
+
+ Name string `json:"name"`
+ UsedBy []string `json:"used_by"`
+}
+
+// Convert a full Profile struct into a ProfilePut struct (filters read-only fields)
+func (profile *Profile) Writable() ProfilePut {
+ return profile.ProfilePut
+}
diff --git a/shared/container.go b/shared/container.go
index 5d0052b..ded95c4 100644
--- a/shared/container.go
+++ b/shared/container.go
@@ -147,14 +147,6 @@ const (
Unfreeze ContainerAction = "unfreeze"
)
-type ProfileConfig struct {
- Name string `json:"name"`
- Config map[string]string `json:"config"`
- Description string `json:"description"`
- Devices map[string]map[string]string `json:"devices"`
- UsedBy []string `json:"used_by"`
-}
-
func IsInt64(value string) error {
if value == "" {
return nil
From 599453f4efddd533d6667a683789839a8f6d2479 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Thu, 22 Dec 2016 15:16:30 -0500
Subject: [PATCH 8/9] shared: Move REST API to new package: container
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
client.go | 22 +++----
lxc/config.go | 21 ++++---
lxc/exec.go | 7 ++-
lxc/info.go | 4 +-
lxc/list.go | 54 ++++++++--------
lxc/list_test.go | 3 +-
lxc/profile.go | 4 +-
lxc/publish.go | 4 +-
lxd/api_internal.go | 4 +-
lxd/container.go | 3 +-
lxd/container_exec.go | 17 ++---
lxd/container_lxc.go | 57 +++++++++--------
lxd/container_patch.go | 3 +-
lxd/container_post.go | 9 +--
lxd/container_put.go | 13 +---
lxd/container_snapshot.go | 14 ++---
lxd/container_state.go | 10 +--
lxd/container_test.go | 3 +-
lxd/containers_get.go | 8 +--
lxd/containers_post.go | 52 ++--------------
lxd/main_forkgetnet.go | 12 ++--
shared/api/container.go | 86 +++++++++++++++++++++++++
shared/api/container_exec.go | 19 ++++++
shared/api/container_snapshot.go | 32 ++++++++++
shared/api/container_state.go | 66 ++++++++++++++++++++
shared/container.go | 131 ---------------------------------------
test/lxd-benchmark/main.go | 5 +-
27 files changed, 337 insertions(+), 326 deletions(-)
create mode 100644 shared/api/container.go
create mode 100644 shared/api/container_exec.go
create mode 100644 shared/api/container_snapshot.go
create mode 100644 shared/api/container_state.go
diff --git a/client.go b/client.go
index e935ed0..1635e50 100644
--- a/client.go
+++ b/client.go
@@ -609,7 +609,7 @@ func (c *Client) IsPublic() bool {
return public
}
-func (c *Client) ListContainers() ([]shared.ContainerInfo, error) {
+func (c *Client) ListContainers() ([]api.Container, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -619,7 +619,7 @@ func (c *Client) ListContainers() ([]shared.ContainerInfo, error) {
return nil, err
}
- var result []shared.ContainerInfo
+ var result []api.Container
if err := json.Unmarshal(resp.Metadata, &result); err != nil {
return nil, err
@@ -1715,12 +1715,12 @@ func (c *Client) ServerStatus() (*api.Server, error) {
return &ss, nil
}
-func (c *Client) ContainerInfo(name string) (*shared.ContainerInfo, error) {
+func (c *Client) ContainerInfo(name string) (*api.Container, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
- ct := shared.ContainerInfo{}
+ ct := api.Container{}
resp, err := c.get(fmt.Sprintf("containers/%s", name))
if err != nil {
@@ -1734,12 +1734,12 @@ func (c *Client) ContainerInfo(name string) (*shared.ContainerInfo, error) {
return &ct, nil
}
-func (c *Client) ContainerState(name string) (*shared.ContainerState, error) {
+func (c *Client) ContainerState(name string) (*api.ContainerState, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
- ct := shared.ContainerState{}
+ ct := api.ContainerState{}
resp, err := c.get(fmt.Sprintf("containers/%s/state", name))
if err != nil {
@@ -2280,7 +2280,7 @@ func (c *Client) Snapshot(container string, snapshotName string, stateful bool)
return c.post(fmt.Sprintf("containers/%s/snapshots", container), body, Async)
}
-func (c *Client) ListSnapshots(container string) ([]shared.SnapshotInfo, error) {
+func (c *Client) ListSnapshots(container string) ([]api.ContainerSnapshot, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -2291,7 +2291,7 @@ func (c *Client) ListSnapshots(container string) ([]shared.SnapshotInfo, error)
return nil, err
}
- var result []shared.SnapshotInfo
+ var result []api.ContainerSnapshot
if err := json.Unmarshal(resp.Metadata, &result); err != nil {
return nil, err
@@ -2300,7 +2300,7 @@ func (c *Client) ListSnapshots(container string) ([]shared.SnapshotInfo, error)
return result, nil
}
-func (c *Client) SnapshotInfo(snapName string) (*shared.SnapshotInfo, error) {
+func (c *Client) SnapshotInfo(snapName string) (*api.ContainerSnapshot, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -2316,7 +2316,7 @@ func (c *Client) SnapshotInfo(snapName string) (*shared.SnapshotInfo, error) {
return nil, err
}
- var result shared.SnapshotInfo
+ var result api.ContainerSnapshot
if err := json.Unmarshal(resp.Metadata, &result); err != nil {
return nil, err
@@ -2427,7 +2427,7 @@ func (c *Client) SetContainerConfig(container, key, value string) error {
return c.WaitForSuccess(resp.Operation)
}
-func (c *Client) UpdateContainerConfig(container string, st shared.BriefContainerInfo) error {
+func (c *Client) UpdateContainerConfig(container string, st api.ContainerPut) error {
if c.Remote.Public {
return fmt.Errorf("This function isn't supported by public remotes.")
}
diff --git a/lxc/config.go b/lxc/config.go
index 1a91fa7..0f2ad48 100644
--- a/lxc/config.go
+++ b/lxc/config.go
@@ -345,21 +345,21 @@ func (c *configCmd) run(config *lxd.Config, args []string) error {
brief := config.Writable()
data, err = yaml.Marshal(&brief)
} else {
- var brief shared.BriefContainerInfo
+ var brief api.ContainerPut
if shared.IsSnapshot(container) {
config, err := d.SnapshotInfo(container)
if err != nil {
return err
}
- brief = shared.BriefContainerInfo{
+ brief = api.ContainerPut{
Profiles: config.Profiles,
Config: config.Config,
Devices: config.Devices,
Ephemeral: config.Ephemeral,
}
if c.expanded {
- brief = shared.BriefContainerInfo{
+ brief = api.ContainerPut{
Profiles: config.Profiles,
Config: config.ExpandedConfig,
Devices: config.ExpandedDevices,
@@ -372,9 +372,10 @@ func (c *configCmd) run(config *lxd.Config, args []string) error {
return err
}
- brief = config.Brief()
+ brief = config.Writable()
if c.expanded {
- brief = config.BriefExpanded()
+ brief.Config = config.ExpandedConfig
+ brief.Devices = config.ExpandedDevices
}
}
@@ -492,7 +493,7 @@ func (c *configCmd) doContainerConfigEdit(client *lxd.Client, cont string) error
return err
}
- newdata := shared.BriefContainerInfo{}
+ newdata := api.ContainerPut{}
err = yaml.Unmarshal(contents, &newdata)
if err != nil {
return err
@@ -506,7 +507,7 @@ func (c *configCmd) doContainerConfigEdit(client *lxd.Client, cont string) error
return err
}
- brief := config.Brief()
+ brief := config.Writable()
data, err := yaml.Marshal(&brief)
if err != nil {
return err
@@ -520,7 +521,7 @@ func (c *configCmd) doContainerConfigEdit(client *lxd.Client, cont string) error
for {
// Parse the text received from the editor
- newdata := shared.BriefContainerInfo{}
+ newdata := api.ContainerPut{}
err = yaml.Unmarshal(content, &newdata)
if err == nil {
err = client.UpdateContainerConfig(cont, newdata)
@@ -742,7 +743,7 @@ func (c *configCmd) deviceSet(config *lxd.Config, which string, args []string) e
dev[key] = value
st.Devices[devname] = dev
- err = client.UpdateContainerConfig(name, st.Brief())
+ err = client.UpdateContainerConfig(name, st.Writable())
if err != nil {
return err
}
@@ -798,7 +799,7 @@ func (c *configCmd) deviceUnset(config *lxd.Config, which string, args []string)
delete(dev, key)
st.Devices[devname] = dev
- err = client.UpdateContainerConfig(name, st.Brief())
+ err = client.UpdateContainerConfig(name, st.Writable())
if err != nil {
return err
}
diff --git a/lxc/exec.go b/lxc/exec.go
index ef278bd..2d2645c 100644
--- a/lxc/exec.go
+++ b/lxc/exec.go
@@ -12,6 +12,7 @@ import (
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
"github.com/lxc/lxd/shared/termios"
@@ -68,7 +69,7 @@ func (c *execCmd) sendTermSize(control *websocket.Conn) error {
return err
}
- msg := shared.ContainerExecControl{}
+ msg := api.ContainerExecControl{}
msg.Command = "window-resize"
msg.Args = make(map[string]string)
msg.Args["width"] = strconv.Itoa(width)
@@ -92,9 +93,9 @@ func (c *execCmd) forwardSignal(control *websocket.Conn, sig syscall.Signal) err
return err
}
- msg := shared.ContainerExecControl{}
+ msg := api.ContainerExecControl{}
msg.Command = "signal"
- msg.Signal = sig
+ msg.Signal = int(sig)
buf, err := json.Marshal(msg)
if err != nil {
diff --git a/lxc/info.go b/lxc/info.go
index a1f6e9f..30a3f66 100644
--- a/lxc/info.go
+++ b/lxc/info.go
@@ -91,8 +91,8 @@ func (c *infoCmd) containerInfo(d *lxd.Client, name string, showLog bool) error
fmt.Printf(i18n.G("Remote: %s")+"\n", d.Remote.Addr)
}
fmt.Printf(i18n.G("Architecture: %s")+"\n", ct.Architecture)
- if ct.CreationDate.UTC().Unix() != 0 {
- fmt.Printf(i18n.G("Created: %s")+"\n", ct.CreationDate.UTC().Format(layout))
+ if ct.CreatedAt.UTC().Unix() != 0 {
+ fmt.Printf(i18n.G("Created: %s")+"\n", ct.CreatedAt.UTC().Format(layout))
}
fmt.Printf(i18n.G("Status: %s")+"\n", ct.Status)
diff --git a/lxc/list.go b/lxc/list.go
index 3d4f75a..8311e90 100644
--- a/lxc/list.go
+++ b/lxc/list.go
@@ -14,6 +14,7 @@ import (
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
)
@@ -25,7 +26,7 @@ type column struct {
NeedsSnapshots bool
}
-type columnData func(shared.ContainerInfo, *shared.ContainerState, []shared.SnapshotInfo) string
+type columnData func(api.Container, *api.ContainerState, []api.ContainerSnapshot) string
type byName [][]string
@@ -142,7 +143,7 @@ func (c *listCmd) dotPrefixMatch(short string, full string) bool {
return true
}
-func (c *listCmd) shouldShow(filters []string, state *shared.ContainerInfo) bool {
+func (c *listCmd) shouldShow(filters []string, state *api.Container) bool {
for _, filter := range filters {
if strings.Contains(filter, "=") {
membs := strings.SplitN(filter, "=", 2)
@@ -207,7 +208,7 @@ func (c *listCmd) shouldShow(filters []string, state *shared.ContainerInfo) bool
return true
}
-func (c *listCmd) listContainers(d *lxd.Client, cinfos []shared.ContainerInfo, filters []string, columns []column) error {
+func (c *listCmd) listContainers(d *lxd.Client, cinfos []api.Container, filters []string, columns []column) error {
headers := []string{}
for _, column := range columns {
headers = append(headers, column.Name)
@@ -218,12 +219,12 @@ func (c *listCmd) listContainers(d *lxd.Client, cinfos []shared.ContainerInfo, f
threads = len(cinfos)
}
- cStates := map[string]*shared.ContainerState{}
+ cStates := map[string]*api.ContainerState{}
cStatesLock := sync.Mutex{}
cStatesQueue := make(chan string, threads)
cStatesWg := sync.WaitGroup{}
- cSnapshots := map[string][]shared.SnapshotInfo{}
+ cSnapshots := map[string][]api.ContainerSnapshot{}
cSnapshotsLock := sync.Mutex{}
cSnapshotsQueue := make(chan string, threads)
cSnapshotsWg := sync.WaitGroup{}
@@ -347,7 +348,7 @@ func (c *listCmd) listContainers(d *lxd.Client, cinfos []shared.ContainerInfo, f
case listFormatJSON:
data := make([]listContainerItem, len(cinfos))
for i := range cinfos {
- data[i].ContainerInfo = &cinfos[i]
+ data[i].Container = &cinfos[i]
data[i].State = cStates[cinfos[i].Name]
data[i].Snapshots = cSnapshots[cinfos[i].Name]
}
@@ -364,9 +365,10 @@ func (c *listCmd) listContainers(d *lxd.Client, cinfos []shared.ContainerInfo, f
}
type listContainerItem struct {
- *shared.ContainerInfo
- State *shared.ContainerState `json:"state"`
- Snapshots []shared.SnapshotInfo `json:"snapshots"`
+ *api.Container
+
+ State *api.ContainerState
+ Snapshots []api.ContainerSnapshot
}
func (c *listCmd) run(config *lxd.Config, args []string) error {
@@ -396,7 +398,7 @@ func (c *listCmd) run(config *lxd.Config, args []string) error {
return err
}
- var cts []shared.ContainerInfo
+ var cts []api.Container
ctslist, err := d.ListContainers()
if err != nil {
return err
@@ -490,7 +492,7 @@ func (c *listCmd) parseColumns() ([]column, error) {
}
}
- column.Data = func(cInfo shared.ContainerInfo, cState *shared.ContainerState, cSnaps []shared.SnapshotInfo) string {
+ column.Data = func(cInfo api.Container, cState *api.ContainerState, cSnaps []api.ContainerSnapshot) string {
v, ok := cInfo.Config[k]
if !ok {
v, ok = cInfo.ExpandedConfig[k]
@@ -509,15 +511,15 @@ func (c *listCmd) parseColumns() ([]column, error) {
return columns, nil
}
-func (c *listCmd) nameColumnData(cInfo shared.ContainerInfo, cState *shared.ContainerState, cSnaps []shared.SnapshotInfo) string {
+func (c *listCmd) nameColumnData(cInfo api.Container, cState *api.ContainerState, cSnaps []api.ContainerSnapshot) string {
return cInfo.Name
}
-func (c *listCmd) statusColumnData(cInfo shared.ContainerInfo, cState *shared.ContainerState, cSnaps []shared.SnapshotInfo) string {
+func (c *listCmd) statusColumnData(cInfo api.Container, cState *api.ContainerState, cSnaps []api.ContainerSnapshot) string {
return strings.ToUpper(cInfo.Status)
}
-func (c *listCmd) IP4ColumnData(cInfo shared.ContainerInfo, cState *shared.ContainerState, cSnaps []shared.SnapshotInfo) string {
+func (c *listCmd) IP4ColumnData(cInfo api.Container, cState *api.ContainerState, cSnaps []api.ContainerSnapshot) string {
if cInfo.IsActive() && cState != nil && cState.Network != nil {
ipv4s := []string{}
for netName, net := range cState.Network {
@@ -541,7 +543,7 @@ func (c *listCmd) IP4ColumnData(cInfo shared.ContainerInfo, cState *shared.Conta
}
}
-func (c *listCmd) IP6ColumnData(cInfo shared.ContainerInfo, cState *shared.ContainerState, cSnaps []shared.SnapshotInfo) string {
+func (c *listCmd) IP6ColumnData(cInfo api.Container, cState *api.ContainerState, cSnaps []api.ContainerSnapshot) string {
if cInfo.IsActive() && cState != nil && cState.Network != nil {
ipv6s := []string{}
for netName, net := range cState.Network {
@@ -565,7 +567,7 @@ func (c *listCmd) IP6ColumnData(cInfo shared.ContainerInfo, cState *shared.Conta
}
}
-func (c *listCmd) typeColumnData(cInfo shared.ContainerInfo, cState *shared.ContainerState, cSnaps []shared.SnapshotInfo) string {
+func (c *listCmd) typeColumnData(cInfo api.Container, cState *api.ContainerState, cSnaps []api.ContainerSnapshot) string {
if cInfo.Ephemeral {
return i18n.G("EPHEMERAL")
} else {
@@ -573,7 +575,7 @@ func (c *listCmd) typeColumnData(cInfo shared.ContainerInfo, cState *shared.Cont
}
}
-func (c *listCmd) numberSnapshotsColumnData(cInfo shared.ContainerInfo, cState *shared.ContainerState, cSnaps []shared.SnapshotInfo) string {
+func (c *listCmd) numberSnapshotsColumnData(cInfo api.Container, cState *api.ContainerState, cSnaps []api.ContainerSnapshot) string {
if cSnaps != nil {
return fmt.Sprintf("%d", len(cSnaps))
}
@@ -581,7 +583,7 @@ func (c *listCmd) numberSnapshotsColumnData(cInfo shared.ContainerInfo, cState *
return ""
}
-func (c *listCmd) PIDColumnData(cInfo shared.ContainerInfo, cState *shared.ContainerState, cSnaps []shared.SnapshotInfo) string {
+func (c *listCmd) PIDColumnData(cInfo api.Container, cState *api.ContainerState, cSnaps []api.ContainerSnapshot) string {
if cInfo.IsActive() && cState != nil {
return fmt.Sprintf("%d", cState.Pid)
}
@@ -589,29 +591,29 @@ func (c *listCmd) PIDColumnData(cInfo shared.ContainerInfo, cState *shared.Conta
return ""
}
-func (c *listCmd) ArchitectureColumnData(cInfo shared.ContainerInfo, cState *shared.ContainerState, cSnaps []shared.SnapshotInfo) string {
+func (c *listCmd) ArchitectureColumnData(cInfo api.Container, cState *api.ContainerState, cSnaps []api.ContainerSnapshot) string {
return cInfo.Architecture
}
-func (c *listCmd) ProfilesColumnData(cInfo shared.ContainerInfo, cState *shared.ContainerState, cSnaps []shared.SnapshotInfo) string {
+func (c *listCmd) ProfilesColumnData(cInfo api.Container, cState *api.ContainerState, cSnaps []api.ContainerSnapshot) string {
return strings.Join(cInfo.Profiles, "\n")
}
-func (c *listCmd) CreatedColumnData(cInfo shared.ContainerInfo, cState *shared.ContainerState, cSnaps []shared.SnapshotInfo) string {
+func (c *listCmd) CreatedColumnData(cInfo api.Container, cState *api.ContainerState, cSnaps []api.ContainerSnapshot) string {
layout := "2006/01/02 15:04 UTC"
- if cInfo.CreationDate.UTC().Unix() != 0 {
- return cInfo.CreationDate.UTC().Format(layout)
+ if cInfo.CreatedAt.UTC().Unix() != 0 {
+ return cInfo.CreatedAt.UTC().Format(layout)
}
return ""
}
-func (c *listCmd) LastUsedColumnData(cInfo shared.ContainerInfo, cState *shared.ContainerState, cSnaps []shared.SnapshotInfo) string {
+func (c *listCmd) LastUsedColumnData(cInfo api.Container, cState *api.ContainerState, cSnaps []api.ContainerSnapshot) string {
layout := "2006/01/02 15:04 UTC"
- if !cInfo.LastUsedDate.IsZero() && cInfo.LastUsedDate.UTC().Unix() != 0 {
- return cInfo.LastUsedDate.UTC().Format(layout)
+ if !cInfo.LastUsedAt.IsZero() && cInfo.LastUsedAt.UTC().Unix() != 0 {
+ return cInfo.LastUsedAt.UTC().Format(layout)
}
return ""
diff --git a/lxc/list_test.go b/lxc/list_test.go
index ae4fe0f..b92f348 100644
--- a/lxc/list_test.go
+++ b/lxc/list_test.go
@@ -8,6 +8,7 @@ import (
"testing"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
)
func TestDotPrefixMatch(t *testing.T) {
@@ -25,7 +26,7 @@ func TestDotPrefixMatch(t *testing.T) {
func TestShouldShow(t *testing.T) {
list := listCmd{}
- state := &shared.ContainerInfo{
+ state := &api.Container{
Name: "foo",
ExpandedConfig: map[string]string{
"security.privileged": "1",
diff --git a/lxc/profile.go b/lxc/profile.go
index 41ce1da..30cc3c8 100644
--- a/lxc/profile.go
+++ b/lxc/profile.go
@@ -267,7 +267,7 @@ func (c *profileCmd) doProfileAdd(client *lxd.Client, d string, p string) error
ct.Profiles = append(ct.Profiles, p)
- err = client.UpdateContainerConfig(d, ct.Brief())
+ err = client.UpdateContainerConfig(d, ct.Writable())
if err != nil {
return err
}
@@ -298,7 +298,7 @@ func (c *profileCmd) doProfileRemove(client *lxd.Client, d string, p string) err
ct.Profiles = profiles
- err = client.UpdateContainerConfig(d, ct.Brief())
+ err = client.UpdateContainerConfig(d, ct.Writable())
if err != nil {
return err
}
diff --git a/lxc/publish.go b/lxc/publish.go
index 38c6bd6..de53f61 100644
--- a/lxc/publish.go
+++ b/lxc/publish.go
@@ -94,7 +94,7 @@ func (c *publishCmd) run(config *lxd.Config, args []string) error {
if ct.Ephemeral {
ct.Ephemeral = false
- err := s.UpdateContainerConfig(cName, ct.Brief())
+ err := s.UpdateContainerConfig(cName, ct.Writable())
if err != nil {
return err
}
@@ -117,7 +117,7 @@ func (c *publishCmd) run(config *lxd.Config, args []string) error {
if wasEphemeral {
ct.Ephemeral = true
- err := s.UpdateContainerConfig(cName, ct.Brief())
+ err := s.UpdateContainerConfig(cName, ct.Writable())
if err != nil {
return err
}
diff --git a/lxd/api_internal.go b/lxd/api_internal.go
index 7fa2205..0fc97f4 100644
--- a/lxd/api_internal.go
+++ b/lxd/api_internal.go
@@ -150,8 +150,8 @@ func internalImport(d *Daemon, r *http.Request) Response {
Architecture: arch,
BaseImage: baseImage,
Config: sf.Container.Config,
- CreationDate: sf.Container.CreationDate,
- LastUsedDate: sf.Container.LastUsedDate,
+ CreationDate: sf.Container.CreatedAt,
+ LastUsedDate: sf.Container.LastUsedAt,
Ctype: cTypeRegular,
Devices: sf.Container.Devices,
Ephemeral: sf.Container.Ephemeral,
diff --git a/lxd/container.go b/lxd/container.go
index 2b916ea..1a7556a 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -11,6 +11,7 @@ import (
"github.com/lxc/lxd/lxd/types"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/osarch"
)
@@ -374,7 +375,7 @@ type container interface {
// Status
Render() (interface{}, interface{}, error)
- RenderState() (*shared.ContainerState, error)
+ RenderState() (*api.ContainerState, error)
IsPrivileged() bool
IsRunning() bool
IsFrozen() bool
diff --git a/lxd/container_exec.go b/lxd/container_exec.go
index 6161dec..60c15f2 100644
--- a/lxd/container_exec.go
+++ b/lxd/container_exec.go
@@ -16,21 +16,12 @@ import (
"github.com/gorilla/websocket"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/version"
log "gopkg.in/inconshreveable/log15.v2"
)
-type commandPostContent struct {
- Command []string `json:"command"`
- WaitForWS bool `json:"wait-for-websocket"`
- RecordOutput bool `json:"record-output"`
- Interactive bool `json:"interactive"`
- Environment map[string]string `json:"environment"`
- Width int `json:"width"`
- Height int `json:"height"`
-}
-
type execWs struct {
command []string
container container
@@ -174,7 +165,7 @@ func (s *execWs) Do(op *operation) error {
break
}
- command := shared.ContainerExecControl{}
+ command := api.ContainerExecControl{}
if err := json.Unmarshal(buf, &command); err != nil {
shared.LogDebugf("Failed to unmarshal control socket command: %s", err)
@@ -200,7 +191,7 @@ func (s *execWs) Do(op *operation) error {
continue
}
} else if command.Command == "signal" {
- if err := syscall.Kill(attachedChildPid, command.Signal); err != nil {
+ if err := syscall.Kill(attachedChildPid, syscall.Signal(command.Signal)); err != nil {
shared.LogDebugf("Failed forwarding signal '%s' to PID %d.", command.Signal, attachedChildPid)
continue
}
@@ -318,7 +309,7 @@ func containerExecPost(d *Daemon, r *http.Request) Response {
return BadRequest(fmt.Errorf("Container is frozen."))
}
- post := commandPostContent{}
+ post := api.ContainerExecPost{}
buf, err := ioutil.ReadAll(r.Body)
if err != nil {
return BadRequest(err)
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index f4b1221..fb9f1c8 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -2403,7 +2403,7 @@ func (c *containerLXC) Render() (interface{}, interface{}, error) {
etag := []interface{}{c.architecture, c.localConfig, c.localDevices, c.ephemeral, c.profiles}
if c.IsSnapshot() {
- return &shared.SnapshotInfo{
+ return &api.ContainerSnapshot{
Architecture: architectureName,
Config: c.localConfig,
CreationDate: c.creationDate,
@@ -2424,25 +2424,28 @@ func (c *containerLXC) Render() (interface{}, interface{}, error) {
}
statusCode := lxcStatusCode(cState)
- return &shared.ContainerInfo{
- Architecture: architectureName,
- Config: c.localConfig,
- CreationDate: c.creationDate,
- Devices: c.localDevices,
- Ephemeral: c.ephemeral,
+ ct := api.Container{
ExpandedConfig: c.expandedConfig,
ExpandedDevices: c.expandedDevices,
- LastUsedDate: c.lastUsedDate,
Name: c.name,
- Profiles: c.profiles,
Status: statusCode.String(),
StatusCode: statusCode,
Stateful: c.stateful,
- }, etag, nil
+ }
+
+ ct.Architecture = architectureName
+ ct.Config = c.localConfig
+ ct.CreatedAt = c.creationDate
+ ct.Devices = c.localDevices
+ ct.Ephemeral = c.ephemeral
+ ct.LastUsedAt = c.lastUsedDate
+ ct.Profiles = c.profiles
+
+ return &ct, etag, nil
}
}
-func (c *containerLXC) RenderState() (*shared.ContainerState, error) {
+func (c *containerLXC) RenderState() (*api.ContainerState, error) {
// Load the go-lxc struct
err := c.initLXC()
if err != nil {
@@ -2454,7 +2457,7 @@ func (c *containerLXC) RenderState() (*shared.ContainerState, error) {
return nil, err
}
statusCode := lxcStatusCode(cState)
- status := shared.ContainerState{
+ status := api.ContainerState{
Status: statusCode.String(),
StatusCode: statusCode,
}
@@ -2786,8 +2789,8 @@ func (c *containerLXC) ConfigKeySet(key string, value string) error {
}
type backupFile struct {
- Container *shared.ContainerInfo `yaml:"container"`
- Snapshots []*shared.SnapshotInfo `yaml:"snapshots"`
+ Container *api.Container `yaml:"container"`
+ Snapshots []*api.ContainerSnapshot `yaml:"snapshots"`
}
func writeBackupFile(c container) error {
@@ -2817,7 +2820,7 @@ func writeBackupFile(c container) error {
return err
}
- var sis []*shared.SnapshotInfo
+ var sis []*api.ContainerSnapshot
for _, s := range snapshots {
si, _, err := s.Render()
@@ -2825,11 +2828,11 @@ func writeBackupFile(c container) error {
return err
}
- sis = append(sis, si.(*shared.SnapshotInfo))
+ sis = append(sis, si.(*api.ContainerSnapshot))
}
data, err := yaml.Marshal(&backupFile{
- Container: ci.(*shared.ContainerInfo),
+ Container: ci.(*api.Container),
Snapshots: sis,
})
if err != nil {
@@ -4560,8 +4563,8 @@ func (c *containerLXC) Exec(command []string, env map[string]string, stdin *os.F
return 0, attachedPid, nil
}
-func (c *containerLXC) cpuState() shared.ContainerStateCPU {
- cpu := shared.ContainerStateCPU{}
+func (c *containerLXC) cpuState() api.ContainerStateCPU {
+ cpu := api.ContainerStateCPU{}
if !cgCpuacctController {
return cpu
@@ -4579,8 +4582,8 @@ func (c *containerLXC) cpuState() shared.ContainerStateCPU {
return cpu
}
-func (c *containerLXC) diskState() map[string]shared.ContainerStateDisk {
- disk := map[string]shared.ContainerStateDisk{}
+func (c *containerLXC) diskState() map[string]api.ContainerStateDisk {
+ disk := map[string]api.ContainerStateDisk{}
for _, name := range c.expandedDevices.DeviceNames() {
d := c.expandedDevices[name]
@@ -4597,14 +4600,14 @@ func (c *containerLXC) diskState() map[string]shared.ContainerStateDisk {
continue
}
- disk[name] = shared.ContainerStateDisk{Usage: usage}
+ disk[name] = api.ContainerStateDisk{Usage: usage}
}
return disk
}
-func (c *containerLXC) memoryState() shared.ContainerStateMemory {
- memory := shared.ContainerStateMemory{}
+func (c *containerLXC) memoryState() api.ContainerStateMemory {
+ memory := api.ContainerStateMemory{}
if !cgMemoryController {
return memory
@@ -4650,8 +4653,8 @@ func (c *containerLXC) memoryState() shared.ContainerStateMemory {
return memory
}
-func (c *containerLXC) networkState() map[string]shared.ContainerStateNetwork {
- result := map[string]shared.ContainerStateNetwork{}
+func (c *containerLXC) networkState() map[string]api.ContainerStateNetwork {
+ result := map[string]api.ContainerStateNetwork{}
pid := c.InitPID()
if pid < 1 {
@@ -4670,7 +4673,7 @@ func (c *containerLXC) networkState() map[string]shared.ContainerStateNetwork {
return result
}
- networks := map[string]shared.ContainerStateNetwork{}
+ networks := map[string]api.ContainerStateNetwork{}
err = json.Unmarshal(out, &networks)
if err != nil {
diff --git a/lxd/container_patch.go b/lxd/container_patch.go
index f7752bb..f650c91 100644
--- a/lxd/container_patch.go
+++ b/lxd/container_patch.go
@@ -10,6 +10,7 @@ import (
"github.com/gorilla/mux"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/osarch"
)
@@ -41,7 +42,7 @@ func containerPatch(d *Daemon, r *http.Request) Response {
return BadRequest(err)
}
- req := containerPutReq{}
+ req := api.ContainerPut{}
if err := json.NewDecoder(rdr2).Decode(&req); err != nil {
return BadRequest(err)
}
diff --git a/lxd/container_post.go b/lxd/container_post.go
index 4139dd7..066a2a3 100644
--- a/lxd/container_post.go
+++ b/lxd/container_post.go
@@ -6,12 +6,9 @@ import (
"net/http"
"github.com/gorilla/mux"
-)
-type containerPostBody struct {
- Migration bool `json:"migration"`
- Name string `json:"name"`
-}
+ "github.com/lxc/lxd/shared/api"
+)
func containerPost(d *Daemon, r *http.Request) Response {
name := mux.Vars(r)["name"]
@@ -25,7 +22,7 @@ func containerPost(d *Daemon, r *http.Request) Response {
return InternalError(err)
}
- body := containerPostBody{}
+ body := api.ContainerPost{}
if err := json.Unmarshal(buf, &body); err != nil {
return BadRequest(err)
}
diff --git a/lxd/container_put.go b/lxd/container_put.go
index 15407df..135a41b 100644
--- a/lxd/container_put.go
+++ b/lxd/container_put.go
@@ -8,22 +8,13 @@ import (
"github.com/gorilla/mux"
- "github.com/lxc/lxd/lxd/types"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/osarch"
log "gopkg.in/inconshreveable/log15.v2"
)
-type containerPutReq struct {
- Architecture string `json:"architecture"`
- Config map[string]string `json:"config"`
- Devices types.Devices `json:"devices"`
- Ephemeral bool `json:"ephemeral"`
- Profiles []string `json:"profiles"`
- Restore string `json:"restore"`
-}
-
/*
* Update configuration, or, if 'restore:snapshot-name' is present, restore
* the named snapshot
@@ -43,7 +34,7 @@ func containerPut(d *Daemon, r *http.Request) Response {
return PreconditionFailed(err)
}
- configRaw := containerPutReq{}
+ configRaw := api.ContainerPut{}
if err := json.NewDecoder(r.Body).Decode(&configRaw); err != nil {
return BadRequest(err)
}
diff --git a/lxd/container_snapshot.go b/lxd/container_snapshot.go
index bc75729..473e2a2 100644
--- a/lxd/container_snapshot.go
+++ b/lxd/container_snapshot.go
@@ -10,14 +10,10 @@ import (
"github.com/gorilla/mux"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/version"
)
-type containerSnapshotPostReq struct {
- Name string `json:"name"`
- Stateful bool `json:"stateful"`
-}
-
func containerSnapshotsGet(d *Daemon, r *http.Request) Response {
recursionStr := r.FormValue("recursion")
recursion, err := strconv.Atoi(recursionStr)
@@ -37,7 +33,7 @@ func containerSnapshotsGet(d *Daemon, r *http.Request) Response {
}
resultString := []string{}
- resultMap := []*shared.SnapshotInfo{}
+ resultMap := []*api.ContainerSnapshot{}
for _, snap := range snaps {
snapName := strings.SplitN(snap.Name(), shared.SnapshotDelimiter, 2)[1]
@@ -50,7 +46,7 @@ func containerSnapshotsGet(d *Daemon, r *http.Request) Response {
continue
}
- resultMap = append(resultMap, render.(*shared.SnapshotInfo))
+ resultMap = append(resultMap, render.(*api.ContainerSnapshot))
}
}
@@ -111,7 +107,7 @@ func containerSnapshotsPost(d *Daemon, r *http.Request) Response {
return SmartError(err)
}
- req := containerSnapshotPostReq{}
+ req := api.ContainerSnapshotsPost{}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return BadRequest(err)
}
@@ -189,7 +185,7 @@ func snapshotGet(sc container, name string) Response {
return SmartError(err)
}
- return SyncResponse(true, render.(*shared.SnapshotInfo))
+ return SyncResponse(true, render.(*api.ContainerSnapshot))
}
func snapshotPost(d *Daemon, r *http.Request, sc container, containerName string) Response {
diff --git a/lxd/container_state.go b/lxd/container_state.go
index 1a4ca8c..3115f58 100644
--- a/lxd/container_state.go
+++ b/lxd/container_state.go
@@ -9,15 +9,9 @@ import (
"github.com/gorilla/mux"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
)
-type containerStatePutReq struct {
- Action string `json:"action"`
- Timeout int `json:"timeout"`
- Force bool `json:"force"`
- Stateful bool `json:"stateful"`
-}
-
func containerState(d *Daemon, r *http.Request) Response {
name := mux.Vars(r)["name"]
c, err := containerLoadByName(d, name)
@@ -36,7 +30,7 @@ func containerState(d *Daemon, r *http.Request) Response {
func containerStatePut(d *Daemon, r *http.Request) Response {
name := mux.Vars(r)["name"]
- raw := containerStatePutReq{}
+ raw := api.ContainerStatePut{}
// We default to -1 (i.e. no timeout) here instead of 0 (instant
// timeout).
diff --git a/lxd/container_test.go b/lxd/container_test.go
index da83174..e389cf9 100644
--- a/lxd/container_test.go
+++ b/lxd/container_test.go
@@ -5,6 +5,7 @@ import (
"github.com/lxc/lxd/lxd/types"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
)
func (suite *lxdTestSuite) TestContainer_ProfilesDefault() {
@@ -87,7 +88,7 @@ func (suite *lxdTestSuite) TestContainer_ProfilesOverwriteDefaultNic() {
out, _, err := c.Render()
suite.Req.Nil(err)
- state := out.(*shared.ContainerInfo)
+ state := out.(*api.Container)
defer c.Delete()
suite.Equal(
diff --git a/lxd/containers_get.go b/lxd/containers_get.go
index d3719ec..2a10110 100644
--- a/lxd/containers_get.go
+++ b/lxd/containers_get.go
@@ -37,7 +37,7 @@ func doContainersGet(d *Daemon, recursion bool) (interface{}, error) {
}
resultString := []string{}
- resultList := []*shared.ContainerInfo{}
+ resultList := []*api.Container{}
if err != nil {
return []string{}, err
}
@@ -49,7 +49,7 @@ func doContainersGet(d *Daemon, recursion bool) (interface{}, error) {
} else {
c, err := doContainerGet(d, container)
if err != nil {
- c = &shared.ContainerInfo{
+ c = &api.Container{
Name: container,
Status: api.Error.String(),
StatusCode: api.Error}
@@ -65,7 +65,7 @@ func doContainersGet(d *Daemon, recursion bool) (interface{}, error) {
return resultList, nil
}
-func doContainerGet(d *Daemon, cname string) (*shared.ContainerInfo, error) {
+func doContainerGet(d *Daemon, cname string) (*api.Container, error) {
c, err := containerLoadByName(d, cname)
if err != nil {
return nil, err
@@ -76,5 +76,5 @@ func doContainerGet(d *Daemon, cname string) (*shared.ContainerInfo, error) {
return nil, err
}
- return cts.(*shared.ContainerInfo), nil
+ return cts.(*api.Container), nil
}
diff --git a/lxd/containers_post.go b/lxd/containers_post.go
index 306c590..fd9babc 100644
--- a/lxd/containers_post.go
+++ b/lxd/containers_post.go
@@ -19,49 +19,7 @@ import (
log "gopkg.in/inconshreveable/log15.v2"
)
-type containerImageSource struct {
- Type string `json:"type"`
- Certificate string `json:"certificate"`
-
- /* for "image" type */
- Alias string `json:"alias"`
- Fingerprint string `json:"fingerprint"`
- Properties map[string]string `json:"properties"`
- Server string `json:"server"`
- Secret string `json:"secret"`
- Protocol string `json:"protocol"`
-
- /*
- * for "migration" and "copy" types, as an optimization users can
- * provide an image hash to extract before the filesystem is rsync'd,
- * potentially cutting down filesystem transfer time. LXD will not go
- * and fetch this image, it will simply use it if it exists in the
- * image store.
- */
- BaseImage string `json:"base-image"`
-
- /* for "migration" type */
- Mode string `json:"mode"`
- Operation string `json:"operation"`
- Websockets map[string]string `json:"secrets"`
-
- /* for "copy" type */
- Source string `json:"source"`
- /* for "migration" type. Whether the migration is live. */
- Live bool `json:"live"`
-}
-
-type containerPostReq struct {
- Architecture string `json:"architecture"`
- Config map[string]string `json:"config"`
- Devices types.Devices `json:"devices"`
- Ephemeral bool `json:"ephemeral"`
- Name string `json:"name"`
- Profiles []string `json:"profiles"`
- Source containerImageSource `json:"source"`
-}
-
-func createFromImage(d *Daemon, req *containerPostReq) Response {
+func createFromImage(d *Daemon, req *api.ContainersPost) Response {
var hash string
var err error
@@ -174,7 +132,7 @@ func createFromImage(d *Daemon, req *containerPostReq) Response {
return OperationResponse(op)
}
-func createFromNone(d *Daemon, req *containerPostReq) Response {
+func createFromNone(d *Daemon, req *api.ContainersPost) Response {
architecture, err := osarch.ArchitectureId(req.Architecture)
if err != nil {
architecture = 0
@@ -206,7 +164,7 @@ func createFromNone(d *Daemon, req *containerPostReq) Response {
return OperationResponse(op)
}
-func createFromMigration(d *Daemon, req *containerPostReq) Response {
+func createFromMigration(d *Daemon, req *api.ContainersPost) Response {
if req.Source.Mode != "pull" && req.Source.Mode != "push" {
return NotImplemented
}
@@ -335,7 +293,7 @@ func createFromMigration(d *Daemon, req *containerPostReq) Response {
return OperationResponse(op)
}
-func createFromCopy(d *Daemon, req *containerPostReq) Response {
+func createFromCopy(d *Daemon, req *api.ContainersPost) Response {
if req.Source.Source == "" {
return BadRequest(fmt.Errorf("must specify a source container"))
}
@@ -406,7 +364,7 @@ func createFromCopy(d *Daemon, req *containerPostReq) Response {
func containersPost(d *Daemon, r *http.Request) Response {
shared.LogDebugf("Responding to container create")
- req := containerPostReq{}
+ req := api.ContainersPost{}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
return BadRequest(err)
}
diff --git a/lxd/main_forkgetnet.go b/lxd/main_forkgetnet.go
index 7e016f3..5ca9fdb 100644
--- a/lxd/main_forkgetnet.go
+++ b/lxd/main_forkgetnet.go
@@ -8,11 +8,11 @@ import (
"strconv"
"strings"
- "github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
)
func cmdForkGetNet() error {
- networks := map[string]shared.ContainerStateNetwork{}
+ networks := map[string]api.ContainerStateNetwork{}
interfaces, err := net.Interfaces()
if err != nil {
@@ -75,9 +75,9 @@ func cmdForkGetNet() error {
netState = "up"
}
- network := shared.ContainerStateNetwork{
- Addresses: []shared.ContainerStateNetworkAddress{},
- Counters: shared.ContainerStateNetworkCounters{},
+ network := api.ContainerStateNetwork{
+ Addresses: []api.ContainerStateNetworkAddress{},
+ Counters: api.ContainerStateNetworkCounters{},
Hwaddr: netIf.HardwareAddr.String(),
Mtu: netIf.MTU,
State: netState,
@@ -114,7 +114,7 @@ func cmdForkGetNet() error {
scope = "link"
}
- address := shared.ContainerStateNetworkAddress{}
+ address := api.ContainerStateNetworkAddress{}
address.Family = family
address.Address = fields[0]
address.Netmask = fields[1]
diff --git a/shared/api/container.go b/shared/api/container.go
new file mode 100644
index 0000000..2bb3437
--- /dev/null
+++ b/shared/api/container.go
@@ -0,0 +1,86 @@
+package api
+
+import (
+ "time"
+)
+
+// ContainersPost represents the fields available for a new LXD container
+type ContainersPost struct {
+ ContainerPut
+
+ Name string `json:"name"`
+ Source ContainerSource `json:"source"`
+}
+
+// ContainerPost represents the fields required to rename/move a LXD container
+type ContainerPost struct {
+ Migration bool `json:"migration"`
+ Name string `json:"name"`
+}
+
+// ContainerPut represents the modifiable fields of a LXD container
+type ContainerPut struct {
+ Architecture string `json:"architecture"`
+ Config map[string]string `json:"config"`
+ Devices map[string]map[string]string `json:"devices"`
+ Ephemeral bool `json:"ephemeral"`
+ Profiles []string `json:"profiles"`
+ Restore string `json:"restore,omitempty"`
+}
+
+// Container represents a LXD container
+type Container struct {
+ ContainerPut
+
+ CreatedAt time.Time `json:"created_at"`
+ ExpandedConfig map[string]string `json:"expanded_config"`
+ ExpandedDevices map[string]map[string]string `json:"expanded_devices"`
+ LastUsedAt time.Time `json:"last_used_at"`
+ Name string `json:"name"`
+ Stateful bool `json:"stateful"`
+ Status string `json:"status"`
+ StatusCode StatusCode `json:"status_code"`
+}
+
+// Convert a full Container struct into a ContainerPut struct (filters read-only fields)
+func (c *Container) Writable() ContainerPut {
+ return c.ContainerPut
+}
+
+// Check whether the container state indicates the container is active
+func (c Container) IsActive() bool {
+ switch c.StatusCode {
+ case Stopped:
+ return false
+ case Error:
+ return false
+ default:
+ return true
+ }
+}
+
+// ContainerSource represents the creation source for a new container
+type ContainerSource struct {
+ Type string `json:"type"`
+ Certificate string `json:"certificate"`
+
+ /* For "image" type */
+ Alias string `json:"alias,omitempty"`
+ Fingerprint string `json:"fingerprint,omitempty"`
+ Properties map[string]string `json:"properties,omitempty"`
+ Server string `json:"server,omitempty"`
+ Secret string `json:"secret,omitempty"`
+ Protocol string `json:"protocol,omitempty"`
+
+ /* For "migration" and "copy" types */
+ BaseImage string `json:"base-image,omitempty"`
+
+ /* For "migration" type */
+ Mode string `json:"mode,omitempty"`
+ Operation string `json:"operation,omitempty"`
+ Websockets map[string]string `json:"secrets,omitempty"`
+ Live bool `json:"live,omitempty"`
+
+ /* For "copy" type */
+ Source string `json:"source,omitempty"`
+}
diff --git a/shared/api/container_exec.go b/shared/api/container_exec.go
new file mode 100644
index 0000000..ab84972
--- /dev/null
+++ b/shared/api/container_exec.go
@@ -0,0 +1,19 @@
+package api
+
+// ContainerExecControl represents a message on the container exec "control" socket
+type ContainerExecControl struct {
+ Command string `json:"command"`
+ Args map[string]string `json:"args"`
+ Signal int `json:"signal"`
+}
+
+// ContainerExecPost represents a LXD container exec request
+type ContainerExecPost struct {
+ Command []string `json:"command"`
+ WaitForWS bool `json:"wait-for-websocket"`
+ RecordOutput bool `json:"record-output"`
+ Interactive bool `json:"interactive"`
+ Environment map[string]string `json:"environment"`
+ Width int `json:"width"`
+ Height int `json:"height"`
+}
diff --git a/shared/api/container_snapshot.go b/shared/api/container_snapshot.go
new file mode 100644
index 0000000..ab65ab7
--- /dev/null
+++ b/shared/api/container_snapshot.go
@@ -0,0 +1,32 @@
+package api
+
+import (
+ "time"
+)
+
+// ContainerSnapshotsPost represents the fields available for a new LXD container snapshot
+type ContainerSnapshotsPost struct {
+ Name string `json:"name"`
+ Stateful bool `json:"stateful"`
+}
+
+// ContainerSnapshotPost represents the fields required to rename/move a LXD container snapshot
+type ContainerSnapshotPost struct {
+ Name string `json:"name"`
+ Migration bool `json:"migration"`
+}
+
+// ContainerSnapshot represents a LXD conainer snapshot
+type ContainerSnapshot struct {
+ Architecture string `json:"architecture"`
+ Config map[string]string `json:"config"`
+ CreationDate time.Time `json:"created_at"`
+ Devices map[string]map[string]string `json:"devices"`
+ Ephemeral bool `json:"ephemeral"`
+ ExpandedConfig map[string]string `json:"expanded_config"`
+ ExpandedDevices map[string]map[string]string `json:"expanded_devices"`
+ LastUsedDate time.Time `json:"last_used_at"`
+ Name string `json:"name"`
+ Profiles []string `json:"profiles"`
+ Stateful bool `json:"stateful"`
+}
diff --git a/shared/api/container_state.go b/shared/api/container_state.go
new file mode 100644
index 0000000..43bc621
--- /dev/null
+++ b/shared/api/container_state.go
@@ -0,0 +1,66 @@
+package api
+
+// ContainerStatePut represents the modifiable fields of a LXD container's state
+type ContainerStatePut struct {
+ Action string `json:"action"`
+ Timeout int `json:"timeout"`
+ Force bool `json:"force"`
+ Stateful bool `json:"stateful"`
+}
+
+// ContainerState represents a LXD container's state
+type ContainerState struct {
+ Status string `json:"status"`
+ StatusCode StatusCode `json:"status_code"`
+ CPU ContainerStateCPU `json:"cpu"`
+ Disk map[string]ContainerStateDisk `json:"disk"`
+ Memory ContainerStateMemory `json:"memory"`
+ Network map[string]ContainerStateNetwork `json:"network"`
+ Pid int64 `json:"pid"`
+ Processes int64 `json:"processes"`
+}
+
+// ContainerStateDisk represents the disk information section of a LXD container's state
+type ContainerStateDisk struct {
+ Usage int64 `json:"usage"`
+}
+
+// ContainerStateCPU represents the cpu information section of a LXD container's state
+type ContainerStateCPU struct {
+ Usage int64 `json:"usage"`
+}
+
+// ContainerStateMemory represents the memory information section of a LXD container's state
+type ContainerStateMemory struct {
+ Usage int64 `json:"usage"`
+ UsagePeak int64 `json:"usage_peak"`
+ SwapUsage int64 `json:"swap_usage"`
+ SwapUsagePeak int64 `json:"swap_usage_peak"`
+}
+
+// ContainerStateNetwork represents the network information section of a LXD container's state
+type ContainerStateNetwork struct {
+ Addresses []ContainerStateNetworkAddress `json:"addresses"`
+ Counters ContainerStateNetworkCounters `json:"counters"`
+ Hwaddr string `json:"hwaddr"`
+ HostName string `json:"host_name"`
+ Mtu int `json:"mtu"`
+ State string `json:"state"`
+ Type string `json:"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"`
+ Address string `json:"address"`
+ Netmask string `json:"netmask"`
+ Scope string `json:"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"`
+ BytesSent int64 `json:"bytes_sent"`
+ PacketsReceived int64 `json:"packets_received"`
+ PacketsSent int64 `json:"packets_sent"`
+}
diff --git a/shared/container.go b/shared/container.go
index ded95c4..34363c4 100644
--- a/shared/container.go
+++ b/shared/container.go
@@ -4,139 +4,8 @@ import (
"fmt"
"strconv"
"strings"
- "syscall"
- "time"
-
- "github.com/lxc/lxd/shared/api"
)
-type ContainerState struct {
- Status string `json:"status"`
- StatusCode api.StatusCode `json:"status_code"`
- CPU ContainerStateCPU `json:"cpu"`
- Disk map[string]ContainerStateDisk `json:"disk"`
- Memory ContainerStateMemory `json:"memory"`
- Network map[string]ContainerStateNetwork `json:"network"`
- Pid int64 `json:"pid"`
- Processes int64 `json:"processes"`
-}
-
-type ContainerStateDisk struct {
- Usage int64 `json:"usage"`
-}
-
-type ContainerStateCPU struct {
- Usage int64 `json:"usage"`
-}
-
-type ContainerStateMemory struct {
- Usage int64 `json:"usage"`
- UsagePeak int64 `json:"usage_peak"`
- SwapUsage int64 `json:"swap_usage"`
- SwapUsagePeak int64 `json:"swap_usage_peak"`
-}
-
-type ContainerStateNetwork struct {
- Addresses []ContainerStateNetworkAddress `json:"addresses"`
- Counters ContainerStateNetworkCounters `json:"counters"`
- Hwaddr string `json:"hwaddr"`
- HostName string `json:"host_name"`
- Mtu int `json:"mtu"`
- State string `json:"state"`
- Type string `json:"type"`
-}
-
-type ContainerStateNetworkAddress struct {
- Family string `json:"family"`
- Address string `json:"address"`
- Netmask string `json:"netmask"`
- Scope string `json:"scope"`
-}
-
-type ContainerStateNetworkCounters struct {
- BytesReceived int64 `json:"bytes_received"`
- BytesSent int64 `json:"bytes_sent"`
- PacketsReceived int64 `json:"packets_received"`
- PacketsSent int64 `json:"packets_sent"`
-}
-
-type ContainerExecControl struct {
- Command string `json:"command"`
- Args map[string]string `json:"args"`
- Signal syscall.Signal `json:"signal"`
-}
-
-type SnapshotInfo struct {
- Architecture string `json:"architecture"`
- Config map[string]string `json:"config"`
- CreationDate time.Time `json:"created_at"`
- Devices map[string]map[string]string `json:"devices"`
- Ephemeral bool `json:"ephemeral"`
- ExpandedConfig map[string]string `json:"expanded_config"`
- ExpandedDevices map[string]map[string]string `json:"expanded_devices"`
- LastUsedDate time.Time `json:"last_used_at"`
- Name string `json:"name"`
- Profiles []string `json:"profiles"`
- Stateful bool `json:"stateful"`
-}
-
-type ContainerInfo struct {
- Architecture string `json:"architecture"`
- Config map[string]string `json:"config"`
- CreationDate time.Time `json:"created_at"`
- Devices map[string]map[string]string `json:"devices"`
- Ephemeral bool `json:"ephemeral"`
- ExpandedConfig map[string]string `json:"expanded_config"`
- ExpandedDevices map[string]map[string]string `json:"expanded_devices"`
- LastUsedDate time.Time `json:"last_used_at"`
- Name string `json:"name"`
- Profiles []string `json:"profiles"`
- Stateful bool `json:"stateful"`
- Status string `json:"status"`
- StatusCode api.StatusCode `json:"status_code"`
-}
-
-func (c ContainerInfo) IsActive() bool {
- switch c.StatusCode {
- case api.Stopped:
- return false
- case api.Error:
- return false
- default:
- return true
- }
-}
-
-/*
- * BriefContainerState contains a subset of the fields in
- * ContainerState, namely those which a user may update
- */
-type BriefContainerInfo struct {
- Name string `json:"name"`
- Profiles []string `json:"profiles"`
- Config map[string]string `json:"config"`
- Devices map[string]map[string]string `json:"devices"`
- Ephemeral bool `json:"ephemeral"`
-}
-
-func (c *ContainerInfo) Brief() BriefContainerInfo {
- retstate := BriefContainerInfo{Name: c.Name,
- Profiles: c.Profiles,
- Config: c.Config,
- Devices: c.Devices,
- Ephemeral: c.Ephemeral}
- return retstate
-}
-
-func (c *ContainerInfo) BriefExpanded() BriefContainerInfo {
- retstate := BriefContainerInfo{Name: c.Name,
- Profiles: c.Profiles,
- Config: c.ExpandedConfig,
- Devices: c.ExpandedDevices,
- Ephemeral: c.Ephemeral}
- return retstate
-}
-
type ContainerAction string
const (
diff --git a/test/lxd-benchmark/main.go b/test/lxd-benchmark/main.go
index dc06f60..e8f619c 100644
--- a/test/lxd-benchmark/main.go
+++ b/test/lxd-benchmark/main.go
@@ -10,6 +10,7 @@ import (
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/gnuflag"
)
@@ -258,7 +259,7 @@ func deleteContainers(c *lxd.Client) error {
return err
}
- containers := []shared.ContainerInfo{}
+ containers := []api.Container{}
for _, container := range allContainers {
if container.Config["user.lxd-benchmark"] != "true" {
continue
@@ -277,7 +278,7 @@ func deleteContainers(c *lxd.Client) error {
wgBatch := sync.WaitGroup{}
nextStat := batch
- deleteContainer := func(ct shared.ContainerInfo) {
+ deleteContainer := func(ct api.Container) {
defer wgBatch.Done()
// Stop
From 063430d844c92d409fb78ec98aa30d74dd017d9b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Thu, 22 Dec 2016 17:12:21 -0500
Subject: [PATCH 9/9] shared: Move REST API to new package: response
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
client.go | 250 ++++++++++++++++++++-----------------------------
lxc/action.go | 2 +-
lxc/config.go | 4 +-
lxc/copy.go | 3 +-
lxc/init.go | 2 +-
lxc/launch.go | 3 +-
lxd/daemon.go | 7 +-
lxd/daemon_images.go | 3 +-
lxd/main_callhook.go | 3 +-
lxd/main_import.go | 3 +-
lxd/main_ready.go | 3 +-
lxd/main_waitready.go | 3 +-
lxd/remote.go | 3 +-
lxd/response.go | 42 ++++-----
shared/api/response.go | 82 ++++++++++++++++
15 files changed, 221 insertions(+), 192 deletions(-)
create mode 100644 shared/api/response.go
diff --git a/client.go b/client.go
index 1635e50..4faf2e6 100644
--- a/client.go
+++ b/client.go
@@ -45,14 +45,6 @@ type Client struct {
simplestreams *simplestreams.SimpleStreams
}
-type ResponseType string
-
-const (
- Sync ResponseType = "sync"
- Async ResponseType = "async"
- Error ResponseType = "error"
-)
-
var (
// LXDErrors are special errors; the client library hoists error codes
// to these errors internally so that user code can compare against
@@ -64,61 +56,17 @@ var (
}
)
-type Response struct {
- Type ResponseType `json:"type"`
-
- /* Valid only for Sync responses */
- Status string `json:"status"`
- StatusCode int `json:"status_code"`
-
- /* Valid only for Async responses */
- Operation string `json:"operation"`
-
- /* Valid only for Error responses */
- Code int `json:"error_code"`
- Error string `json:"error"`
-
- /* Valid for Sync and Error responses */
- Metadata json.RawMessage `json:"metadata"`
-}
-
-func (r *Response) MetadataAsMap() (*shared.Jmap, error) {
- ret := shared.Jmap{}
- if err := json.Unmarshal(r.Metadata, &ret); err != nil {
- return nil, err
- }
- return &ret, nil
-}
-
-func (r *Response) MetadataAsOperation() (*api.Operation, error) {
- op := api.Operation{}
- if err := json.Unmarshal(r.Metadata, &op); err != nil {
- return nil, err
- }
-
- return &op, nil
-}
-
-func (r *Response) MetadataAsStringSlice() ([]string, error) {
- sl := []string{}
- if err := json.Unmarshal(r.Metadata, &sl); err != nil {
- return nil, err
- }
-
- return sl, nil
-}
-
// ParseResponse parses a lxd style response out of an http.Response. Note that
// this does _not_ automatically convert error responses to golang errors. To
// do that, use ParseError. Internal client library uses should probably use
// HoistResponse, unless they are interested in accessing the underlying Error
// response (e.g. to inspect the error code).
-func ParseResponse(r *http.Response) (*Response, error) {
+func ParseResponse(r *http.Response) (*api.Response, error) {
if r == nil {
return nil, fmt.Errorf("no response!")
}
defer r.Body.Close()
- ret := Response{}
+ ret := api.Response{}
s, err := ioutil.ReadAll(r.Body)
if err != nil {
@@ -135,13 +83,13 @@ func ParseResponse(r *http.Response) (*Response, error) {
// HoistResponse hoists a regular http response into a response of type rtype
// or returns a golang error.
-func HoistResponse(r *http.Response, rtype ResponseType) (*Response, error) {
+func HoistResponse(r *http.Response, rtype api.ResponseType) (*api.Response, error) {
resp, err := ParseResponse(r)
if err != nil {
return nil, err
}
- if resp.Type == Error {
+ if resp.Type == api.ErrorResponse {
// Try and use a known error if we have one for this code.
err, ok := LXDErrors[resp.Code]
if !ok {
@@ -393,13 +341,13 @@ func (c *Client) Addresses() ([]string, error) {
return addresses, nil
}
-func (c *Client) get(base string) (*Response, error) {
+func (c *Client) get(base string) (*api.Response, error) {
uri := c.url(version.APIVersion, base)
return c.baseGet(uri)
}
-func (c *Client) baseGet(getUrl string) (*Response, error) {
+func (c *Client) baseGet(getUrl string) (*api.Response, error) {
req, err := http.NewRequest("GET", getUrl, nil)
if err != nil {
return nil, err
@@ -412,10 +360,10 @@ func (c *Client) baseGet(getUrl string) (*Response, error) {
return nil, err
}
- return HoistResponse(resp, Sync)
+ return HoistResponse(resp, api.SyncResponse)
}
-func (c *Client) doUpdateMethod(method string, base string, args interface{}, rtype ResponseType) (*Response, error) {
+func (c *Client) doUpdateMethod(method string, base string, args interface{}, rtype api.ResponseType) (*api.Response, error) {
uri := c.url(version.APIVersion, base)
buf := bytes.Buffer{}
@@ -441,19 +389,19 @@ func (c *Client) doUpdateMethod(method string, base string, args interface{}, rt
return HoistResponse(resp, rtype)
}
-func (c *Client) put(base string, args interface{}, rtype ResponseType) (*Response, error) {
+func (c *Client) put(base string, args interface{}, rtype api.ResponseType) (*api.Response, error) {
return c.doUpdateMethod("PUT", base, args, rtype)
}
-func (c *Client) patch(base string, args interface{}, rtype ResponseType) (*Response, error) {
+func (c *Client) patch(base string, args interface{}, rtype api.ResponseType) (*api.Response, error) {
return c.doUpdateMethod("PATCH", base, args, rtype)
}
-func (c *Client) post(base string, args interface{}, rtype ResponseType) (*Response, error) {
+func (c *Client) post(base string, args interface{}, rtype api.ResponseType) (*api.Response, error) {
return c.doUpdateMethod("POST", base, args, rtype)
}
-func (c *Client) delete(base string, args interface{}, rtype ResponseType) (*Response, error) {
+func (c *Client) delete(base string, args interface{}, rtype api.ResponseType) (*api.Response, error) {
return c.doUpdateMethod("DELETE", base, args, rtype)
}
@@ -471,7 +419,7 @@ func (c *Client) getRaw(uri string) (*http.Response, error) {
// because it is raw data, we need to check for http status
if raw.StatusCode != 200 {
- resp, err := HoistResponse(raw, Sync)
+ resp, err := HoistResponse(raw, api.SyncResponse)
if err != nil {
return nil, err
}
@@ -518,7 +466,7 @@ func (c *Client) url(elem ...string) string {
return strings.TrimSuffix(uri, "/")
}
-func (c *Client) GetServerConfig() (*Response, error) {
+func (c *Client) GetServerConfig() (*api.Response, error) {
if c.Remote.Protocol == "simplestreams" {
return nil, fmt.Errorf("This function isn't supported by simplestreams remote.")
}
@@ -575,12 +523,12 @@ func (c *Client) AmTrusted() bool {
shared.LogDebugf("%s", resp)
- jmap, err := resp.MetadataAsMap()
+ meta, err := resp.MetadataAsMap()
if err != nil {
return false
}
- auth, err := jmap.GetString("auth")
+ auth, err := shared.Jmap(meta).GetString("auth")
if err != nil {
return false
}
@@ -596,12 +544,12 @@ func (c *Client) IsPublic() bool {
shared.LogDebugf("%s", resp)
- jmap, err := resp.MetadataAsMap()
+ meta, err := resp.MetadataAsMap()
if err != nil {
return false
}
- public, err := jmap.GetBool("public")
+ public, err := shared.Jmap(meta).GetBool("public")
if err != nil {
return false
}
@@ -621,7 +569,7 @@ func (c *Client) ListContainers() ([]api.Container, error) {
var result []api.Container
- if err := json.Unmarshal(resp.Metadata, &result); err != nil {
+ if err := resp.MetadataAsStruct(&result); err != nil {
return nil, err
}
@@ -650,7 +598,7 @@ func (c *Client) CopyImage(image string, dest *Client, copy_aliases bool, aliase
if c.Remote.Protocol != "simplestreams" && !info.Public {
var secret string
- resp, err := c.post("images/"+image+"/secret", nil, Async)
+ resp, err := c.post("images/"+image+"/secret", nil, api.AsyncResponse)
if err != nil {
return err
}
@@ -717,7 +665,7 @@ func (c *Client) CopyImage(image string, dest *Client, copy_aliases bool, aliase
source["server"] = sourceUrl
body := shared.Jmap{"public": public, "auto_update": autoUpdate, "source": source}
- resp, err := dest.post("images", body, Async)
+ resp, err := dest.post("images", body, api.AsyncResponse)
if err != nil {
continue
}
@@ -941,7 +889,7 @@ func (c *Client) PostImageURL(imageFile string, properties []string, public bool
go c.Monitor([]string{"operation"}, handler, nil)
}
- resp, err := c.post("images", body, Async)
+ resp, err := c.post("images", body, api.AsyncResponse)
if err != nil {
return "", err
}
@@ -1110,7 +1058,7 @@ func (c *Client) PostImage(imageFile string, rootfsFile string, properties []str
return "", err
}
- resp, err := HoistResponse(raw, Async)
+ resp, err := HoistResponse(raw, api.AsyncResponse)
if err != nil {
return "", err
}
@@ -1148,7 +1096,7 @@ func (c *Client) GetImageInfo(image string) (*api.Image, error) {
}
info := api.Image{}
- if err := json.Unmarshal(resp.Metadata, &info); err != nil {
+ if err := resp.MetadataAsStruct(&info); err != nil {
return nil, err
}
@@ -1160,7 +1108,7 @@ func (c *Client) PutImageInfo(name string, p api.ImagePut) error {
return fmt.Errorf("This function isn't supported by public remotes.")
}
- _, err := c.put(fmt.Sprintf("images/%s", name), p, Sync)
+ _, err := c.put(fmt.Sprintf("images/%s", name), p, api.SyncResponse)
return err
}
@@ -1175,7 +1123,7 @@ func (c *Client) ListImages() ([]api.Image, error) {
}
var result []api.Image
- if err := json.Unmarshal(resp.Metadata, &result); err != nil {
+ if err := resp.MetadataAsStruct(&result); err != nil {
return nil, err
}
@@ -1187,7 +1135,7 @@ func (c *Client) DeleteImage(image string) error {
return fmt.Errorf("This function isn't supported by public remotes.")
}
- resp, err := c.delete(fmt.Sprintf("images/%s", image), nil, Async)
+ resp, err := c.delete(fmt.Sprintf("images/%s", image), nil, api.AsyncResponse)
if err != nil {
return err
@@ -1203,7 +1151,7 @@ func (c *Client) PostAlias(alias string, desc string, target string) error {
body := shared.Jmap{"description": desc, "target": target, "name": alias}
- _, err := c.post("images/aliases", body, Sync)
+ _, err := c.post("images/aliases", body, api.SyncResponse)
return err
}
@@ -1212,7 +1160,7 @@ func (c *Client) DeleteAlias(alias string) error {
return fmt.Errorf("This function isn't supported by public remotes.")
}
- _, err := c.delete(fmt.Sprintf("images/aliases/%s", alias), nil, Sync)
+ _, err := c.delete(fmt.Sprintf("images/aliases/%s", alias), nil, api.SyncResponse)
return err
}
@@ -1228,7 +1176,7 @@ func (c *Client) ListAliases() ([]api.ImageAliasesEntry, error) {
var result []api.ImageAliasesEntry
- if err := json.Unmarshal(resp.Metadata, &result); err != nil {
+ if err := resp.MetadataAsStruct(&result); err != nil {
return nil, err
}
@@ -1246,7 +1194,7 @@ func (c *Client) CertificateList() ([]api.Certificate, error) {
}
var result []api.Certificate
- if err := json.Unmarshal(resp.Metadata, &result); err != nil {
+ if err := resp.MetadataAsStruct(&result); err != nil {
return nil, err
}
@@ -1260,7 +1208,7 @@ func (c *Client) AddMyCertToServer(pwd string) error {
body := shared.Jmap{"type": "client", "password": pwd}
- _, err := c.post("certificates", body, Sync)
+ _, err := c.post("certificates", body, api.SyncResponse)
return err
}
@@ -1270,7 +1218,7 @@ func (c *Client) CertificateAdd(cert *x509.Certificate, name string) error {
}
b64 := base64.StdEncoding.EncodeToString(cert.Raw)
- _, err := c.post("certificates", shared.Jmap{"type": "client", "certificate": b64, "name": name}, Sync)
+ _, err := c.post("certificates", shared.Jmap{"type": "client", "certificate": b64, "name": name}, api.SyncResponse)
return err
}
@@ -1279,7 +1227,7 @@ func (c *Client) CertificateRemove(fingerprint string) error {
return fmt.Errorf("This function isn't supported by public remotes.")
}
- _, err := c.delete(fmt.Sprintf("certificates/%s", fingerprint), nil, Sync)
+ _, err := c.delete(fmt.Sprintf("certificates/%s", fingerprint), nil, api.SyncResponse)
return err
}
@@ -1305,12 +1253,12 @@ func (c *Client) GetAlias(alias string) string {
return ""
}
- if resp.Type == Error {
+ if resp.Type == api.ErrorResponse {
return ""
}
var result api.ImageAliasesEntry
- if err := json.Unmarshal(resp.Metadata, &result); err != nil {
+ if err := resp.MetadataAsStruct(&result); err != nil {
return ""
}
return result.Target
@@ -1318,7 +1266,7 @@ func (c *Client) GetAlias(alias string) string {
// Init creates a container from either a fingerprint or an alias; you must
// provide at least one.
-func (c *Client) Init(name string, imgremote string, image string, profiles *[]string, config map[string]string, devices map[string]map[string]string, ephem bool) (*Response, error) {
+func (c *Client) Init(name string, imgremote string, image string, profiles *[]string, config map[string]string, devices map[string]map[string]string, ephem bool) (*api.Response, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -1366,7 +1314,7 @@ func (c *Client) Init(name string, imgremote string, image string, profiles *[]s
image = target
- resp, err := tmpremote.post("images/"+image+"/secret", nil, Async)
+ resp, err := tmpremote.post("images/"+image+"/secret", nil, api.AsyncResponse)
if err != nil {
return nil, err
}
@@ -1428,7 +1376,7 @@ func (c *Client) Init(name string, imgremote string, image string, profiles *[]s
body["ephemeral"] = ephem
}
- var resp *Response
+ var resp *api.Response
if imgremote != c.Name {
var addresses []string
@@ -1440,7 +1388,7 @@ func (c *Client) Init(name string, imgremote string, image string, profiles *[]s
for _, addr := range addresses {
body["source"].(shared.Jmap)["server"] = "https://" + addr
- resp, err = c.post("containers", body, Async)
+ resp, err = c.post("containers", body, api.AsyncResponse)
if err != nil {
continue
}
@@ -1448,7 +1396,7 @@ func (c *Client) Init(name string, imgremote string, image string, profiles *[]s
break
}
} else {
- resp, err = c.post("containers", body, Async)
+ resp, err = c.post("containers", body, api.AsyncResponse)
}
if err != nil {
@@ -1461,7 +1409,7 @@ func (c *Client) Init(name string, imgremote string, image string, profiles *[]s
return resp, nil
}
-func (c *Client) LocalCopy(source string, name string, config map[string]string, profiles []string, ephemeral bool) (*Response, error) {
+func (c *Client) LocalCopy(source string, name string, config map[string]string, profiles []string, ephemeral bool) (*api.Response, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -1477,7 +1425,7 @@ func (c *Client) LocalCopy(source string, name string, config map[string]string,
"ephemeral": ephemeral,
}
- return c.post("containers", body, Async)
+ return c.post("containers", body, api.AsyncResponse)
}
func (c *Client) Monitor(types []string, handler func(interface{}), done chan bool) error {
@@ -1556,7 +1504,7 @@ func (c *Client) Exec(name string, cmd []string, env map[string]string,
body["height"] = height
}
- resp, err := c.post(fmt.Sprintf("containers/%s/exec", name), body, Async)
+ resp, err := c.post(fmt.Sprintf("containers/%s/exec", name), body, api.AsyncResponse)
if err != nil {
return -1, err
}
@@ -1658,7 +1606,7 @@ func (c *Client) Exec(name string, cmd []string, env map[string]string,
return shared.Jmap(op.Metadata).GetInt("return")
}
-func (c *Client) Action(name string, action shared.ContainerAction, timeout int, force bool, stateful bool) (*Response, error) {
+func (c *Client) Action(name string, action shared.ContainerAction, timeout int, force bool, stateful bool) (*api.Response, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -1672,10 +1620,10 @@ func (c *Client) Action(name string, action shared.ContainerAction, timeout int,
body["stateful"] = stateful
}
- return c.put(fmt.Sprintf("containers/%s/state", name), body, Async)
+ return c.put(fmt.Sprintf("containers/%s/state", name), body, api.AsyncResponse)
}
-func (c *Client) Delete(name string) (*Response, error) {
+func (c *Client) Delete(name string) (*api.Response, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -1688,7 +1636,7 @@ func (c *Client) Delete(name string) (*Response, error) {
url = fmt.Sprintf("containers/%s", name)
}
- return c.delete(url, nil, Async)
+ return c.delete(url, nil, api.AsyncResponse)
}
func (c *Client) ServerStatus() (*api.Server, error) {
@@ -1699,7 +1647,7 @@ func (c *Client) ServerStatus() (*api.Server, error) {
return nil, err
}
- if err := json.Unmarshal(resp.Metadata, &ss); err != nil {
+ if err := resp.MetadataAsStruct(&ss); err != nil {
return nil, err
}
@@ -1727,7 +1675,7 @@ func (c *Client) ContainerInfo(name string) (*api.Container, error) {
return nil, err
}
- if err := json.Unmarshal(resp.Metadata, &ct); err != nil {
+ if err := resp.MetadataAsStruct(&ct); err != nil {
return nil, err
}
@@ -1746,7 +1694,7 @@ func (c *Client) ContainerState(name string) (*api.ContainerState, error) {
return nil, err
}
- if err := json.Unmarshal(resp.Metadata, &ct); err != nil {
+ if err := resp.MetadataAsStruct(&ct); err != nil {
return nil, err
}
@@ -1779,7 +1727,7 @@ func (c *Client) ProfileConfig(name string) (*api.Profile, error) {
return nil, err
}
- if err := json.Unmarshal(resp.Metadata, &ct); err != nil {
+ if err := resp.MetadataAsStruct(&ct); err != nil {
return nil, err
}
@@ -1816,7 +1764,7 @@ func (c *Client) PushFile(container string, p string, gid int, uid int, mode str
return err
}
- _, err = HoistResponse(raw, Sync)
+ _, err = HoistResponse(raw, api.SyncResponse)
return err
}
@@ -1842,7 +1790,7 @@ func (c *Client) Mkdir(container string, p string, mode os.FileMode) error {
return err
}
- _, err = HoistResponse(raw, Sync)
+ _, err = HoistResponse(raw, api.SyncResponse)
return err
}
@@ -1935,7 +1883,7 @@ func (c *Client) PullFile(container string, p string) (int, int, int, string, io
uid, gid, mode, type_ := shared.ParseLXDFileHeaders(r.Header)
if type_ == "directory" {
- resp, err := HoistResponse(r, Sync)
+ resp, err := HoistResponse(r, api.SyncResponse)
if err != nil {
return 0, 0, 0, "", nil, nil, err
}
@@ -1999,7 +1947,7 @@ func (c *Client) RecursivePullFile(container string, p string, targetDir string)
return nil
}
-func (c *Client) GetMigrationSourceWS(container string) (*Response, error) {
+func (c *Client) GetMigrationSourceWS(container string) (*api.Response, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -2015,14 +1963,14 @@ func (c *Client) GetMigrationSourceWS(container string) (*Response, error) {
url = fmt.Sprintf("containers/%s/snapshots/%s", pieces[0], pieces[1])
}
- return c.post(url, body, Async)
+ return c.post(url, body, api.AsyncResponse)
}
func (c *Client) MigrateFrom(name string, operation string, certificate string,
sourceSecrets map[string]string, architecture string, config map[string]string,
devices map[string]map[string]string, profiles []string,
baseImage string, ephemeral bool, push bool, sourceClient *Client,
- sourceOperation string) (*Response, error) {
+ sourceOperation string) (*api.Response, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -2094,7 +2042,7 @@ func (c *Client) MigrateFrom(name string, operation string, certificate string,
// Post to target server and request and retrieve a set of
// websockets + secrets matching those of the source server.
- resp, err := c.post("containers", body, Async)
+ resp, err := c.post("containers", body, api.AsyncResponse)
if err != nil {
return nil, err
}
@@ -2192,10 +2140,10 @@ func (c *Client) MigrateFrom(name string, operation string, certificate string,
return resp, nil
}
- return c.post("containers", body, Async)
+ return c.post("containers", body, api.AsyncResponse)
}
-func (c *Client) Rename(name string, newName string) (*Response, error) {
+func (c *Client) Rename(name string, newName string) (*api.Response, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -2207,13 +2155,13 @@ func (c *Client) Rename(name string, newName string) (*Response, error) {
}
if len(oldNameParts) == 1 {
body := shared.Jmap{"name": newName}
- return c.post(fmt.Sprintf("containers/%s", name), body, Async)
+ return c.post(fmt.Sprintf("containers/%s", name), body, api.AsyncResponse)
}
if oldNameParts[0] != newNameParts[0] {
return nil, fmt.Errorf("Attempting to rename snapshot of one container into a snapshot of another container.")
}
body := shared.Jmap{"name": newNameParts[1]}
- return c.post(fmt.Sprintf("containers/%s/snapshots/%s", oldNameParts[0], oldNameParts[1]), body, Async)
+ return c.post(fmt.Sprintf("containers/%s/snapshots/%s", oldNameParts[0], oldNameParts[1]), body, api.AsyncResponse)
}
/* Wait for an operation */
@@ -2262,22 +2210,22 @@ func (c *Client) WaitForSuccessOp(waitURL string) (*api.Operation, error) {
return op, fmt.Errorf(op.Err)
}
-func (c *Client) RestoreSnapshot(container string, snapshotName string, stateful bool) (*Response, error) {
+func (c *Client) RestoreSnapshot(container string, snapshotName string, stateful bool) (*api.Response, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
body := shared.Jmap{"restore": snapshotName, "stateful": stateful}
- return c.put(fmt.Sprintf("containers/%s", container), body, Async)
+ return c.put(fmt.Sprintf("containers/%s", container), body, api.AsyncResponse)
}
-func (c *Client) Snapshot(container string, snapshotName string, stateful bool) (*Response, error) {
+func (c *Client) Snapshot(container string, snapshotName string, stateful bool) (*api.Response, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
body := shared.Jmap{"name": snapshotName, "stateful": stateful}
- return c.post(fmt.Sprintf("containers/%s/snapshots", container), body, Async)
+ return c.post(fmt.Sprintf("containers/%s/snapshots", container), body, api.AsyncResponse)
}
func (c *Client) ListSnapshots(container string) ([]api.ContainerSnapshot, error) {
@@ -2293,7 +2241,7 @@ func (c *Client) ListSnapshots(container string) ([]api.ContainerSnapshot, error
var result []api.ContainerSnapshot
- if err := json.Unmarshal(resp.Metadata, &result); err != nil {
+ if err := resp.MetadataAsStruct(&result); err != nil {
return nil, err
}
@@ -2318,7 +2266,7 @@ func (c *Client) SnapshotInfo(snapName string) (*api.ContainerSnapshot, error) {
var result api.ContainerSnapshot
- if err := json.Unmarshal(resp.Metadata, &result); err != nil {
+ if err := resp.MetadataAsStruct(&result); err != nil {
return nil, err
}
@@ -2348,7 +2296,7 @@ func (c *Client) GetServerConfigString() ([]string, error) {
return resp, nil
}
-func (c *Client) SetServerConfig(key string, value string) (*Response, error) {
+func (c *Client) SetServerConfig(key string, value string) (*api.Response, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -2360,15 +2308,15 @@ func (c *Client) SetServerConfig(key string, value string) (*Response, error) {
ss.Config[key] = value
- return c.put("", ss, Sync)
+ return c.put("", ss, api.SyncResponse)
}
-func (c *Client) UpdateServerConfig(ss api.ServerPut) (*Response, error) {
+func (c *Client) UpdateServerConfig(ss api.ServerPut) (*api.Response, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
- return c.put("", ss, Sync)
+ return c.put("", ss, api.SyncResponse)
}
/*
@@ -2419,7 +2367,7 @@ func (c *Client) SetContainerConfig(container, key, value string) error {
* snapshot), we expect config to be a sync operation, so let's just
* handle it here.
*/
- resp, err := c.put(fmt.Sprintf("containers/%s", container), st, Async)
+ resp, err := c.put(fmt.Sprintf("containers/%s", container), st, api.AsyncResponse)
if err != nil {
return err
}
@@ -2432,7 +2380,7 @@ func (c *Client) UpdateContainerConfig(container string, st api.ContainerPut) er
return fmt.Errorf("This function isn't supported by public remotes.")
}
- resp, err := c.put(fmt.Sprintf("containers/%s", container), st, Async)
+ resp, err := c.put(fmt.Sprintf("containers/%s", container), st, api.AsyncResponse)
if err != nil {
return err
}
@@ -2447,7 +2395,7 @@ func (c *Client) ProfileCreate(p string) error {
body := shared.Jmap{"name": p}
- _, err := c.post("profiles", body, Sync)
+ _, err := c.post("profiles", body, api.SyncResponse)
return err
}
@@ -2456,7 +2404,7 @@ func (c *Client) ProfileDelete(p string) error {
return fmt.Errorf("This function isn't supported by public remotes.")
}
- _, err := c.delete(fmt.Sprintf("profiles/%s", p), nil, Sync)
+ _, err := c.delete(fmt.Sprintf("profiles/%s", p), nil, api.SyncResponse)
return err
}
@@ -2490,7 +2438,7 @@ func (c *Client) SetProfileConfigItem(profile, key, value string) error {
st.Config[key] = value
}
- _, err = c.put(fmt.Sprintf("profiles/%s", profile), st, Sync)
+ _, err = c.put(fmt.Sprintf("profiles/%s", profile), st, api.SyncResponse)
return err
}
@@ -2499,7 +2447,7 @@ func (c *Client) PutProfile(name string, profile api.ProfilePut) error {
return fmt.Errorf("This function isn't supported by public remotes.")
}
- _, err := c.put(fmt.Sprintf("profiles/%s", name), profile, Sync)
+ _, err := c.put(fmt.Sprintf("profiles/%s", name), profile, api.SyncResponse)
return err
}
@@ -2514,14 +2462,14 @@ func (c *Client) ListProfiles() ([]api.Profile, error) {
}
profiles := []api.Profile{}
- if err := json.Unmarshal(resp.Metadata, &profiles); err != nil {
+ if err := resp.MetadataAsStruct(&profiles); err != nil {
return nil, err
}
return profiles, nil
}
-func (c *Client) AssignProfile(container, profile string) (*Response, error) {
+func (c *Client) AssignProfile(container, profile string) (*api.Response, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -2537,10 +2485,10 @@ func (c *Client) AssignProfile(container, profile string) (*Response, error) {
st.Profiles = nil
}
- return c.put(fmt.Sprintf("containers/%s", container), st, Async)
+ return c.put(fmt.Sprintf("containers/%s", container), st, api.AsyncResponse)
}
-func (c *Client) ContainerDeviceDelete(container, devname string) (*Response, error) {
+func (c *Client) ContainerDeviceDelete(container, devname string) (*api.Response, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -2553,14 +2501,14 @@ func (c *Client) ContainerDeviceDelete(container, devname string) (*Response, er
for n, _ := range st.Devices {
if n == devname {
delete(st.Devices, n)
- return c.put(fmt.Sprintf("containers/%s", container), st, Async)
+ return c.put(fmt.Sprintf("containers/%s", container), st, api.AsyncResponse)
}
}
return nil, fmt.Errorf("Device doesn't exist.")
}
-func (c *Client) ContainerDeviceAdd(container, devname, devtype string, props []string) (*Response, error) {
+func (c *Client) ContainerDeviceAdd(container, devname, devtype string, props []string) (*api.Response, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -2592,7 +2540,7 @@ func (c *Client) ContainerDeviceAdd(container, devname, devtype string, props []
st.Devices[devname] = newdev
- return c.put(fmt.Sprintf("containers/%s", container), st, Async)
+ return c.put(fmt.Sprintf("containers/%s", container), st, api.AsyncResponse)
}
func (c *Client) ContainerListDevices(container string) ([]string, error) {
@@ -2611,7 +2559,7 @@ func (c *Client) ContainerListDevices(container string) ([]string, error) {
return devs, nil
}
-func (c *Client) ProfileDeviceDelete(profile, devname string) (*Response, error) {
+func (c *Client) ProfileDeviceDelete(profile, devname string) (*api.Response, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -2624,14 +2572,14 @@ func (c *Client) ProfileDeviceDelete(profile, devname string) (*Response, error)
for n, _ := range st.Devices {
if n == devname {
delete(st.Devices, n)
- return c.put(fmt.Sprintf("profiles/%s", profile), st, Sync)
+ return c.put(fmt.Sprintf("profiles/%s", profile), st, api.SyncResponse)
}
}
return nil, fmt.Errorf("Device doesn't exist.")
}
-func (c *Client) ProfileDeviceAdd(profile, devname, devtype string, props []string) (*Response, error) {
+func (c *Client) ProfileDeviceAdd(profile, devname, devtype string, props []string) (*api.Response, error) {
if c.Remote.Public {
return nil, fmt.Errorf("This function isn't supported by public remotes.")
}
@@ -2663,7 +2611,7 @@ func (c *Client) ProfileDeviceAdd(profile, devname, devtype string, props []stri
st.Devices[devname] = newdev
- return c.put(fmt.Sprintf("profiles/%s", profile), st, Sync)
+ return c.put(fmt.Sprintf("profiles/%s", profile), st, api.SyncResponse)
}
func (c *Client) ProfileListDevices(profile string) ([]string, error) {
@@ -2687,7 +2635,7 @@ func (c *Client) ProfileListDevices(profile string) ([]string, error) {
func WebsocketDial(dialer websocket.Dialer, url string) (*websocket.Conn, error) {
conn, raw, err := dialer.Dial(url, http.Header{})
if err != nil {
- _, err2 := HoistResponse(raw, Error)
+ _, err2 := HoistResponse(raw, api.ErrorResponse)
if err2 != nil {
/* The response isn't one we understand, so return
* whatever the original error was. */
@@ -2710,11 +2658,11 @@ func (c *Client) ProfileCopy(name, newname string, dest *Client) error {
}
body := shared.Jmap{"config": st.Config, "name": newname, "devices": st.Devices}
- _, err = dest.post("profiles", body, Sync)
+ _, err = dest.post("profiles", body, api.SyncResponse)
return err
}
-func (c *Client) AsyncWaitMeta(resp *Response) (map[string]interface{}, error) {
+func (c *Client) AsyncWaitMeta(resp *api.Response) (map[string]interface{}, error) {
op, err := c.WaitFor(resp.Operation)
if err != nil {
return nil, err
@@ -2746,7 +2694,7 @@ func (c *Client) ImageFromContainer(cname string, public bool, aliases []string,
body["compression_algorithm"] = compression_algorithm
}
- resp, err := c.post("images", body, Async)
+ resp, err := c.post("images", body, api.AsyncResponse)
if err != nil {
return "", err
}
@@ -2781,7 +2729,7 @@ func (c *Client) NetworkCreate(name string, config map[string]string) error {
body := shared.Jmap{"name": name, "config": config}
- _, err := c.post("networks", body, Sync)
+ _, err := c.post("networks", body, api.SyncResponse)
return err
}
@@ -2796,7 +2744,7 @@ func (c *Client) NetworkGet(name string) (api.Network, error) {
}
network := api.Network{}
- if err := json.Unmarshal(resp.Metadata, &network); err != nil {
+ if err := resp.MetadataAsStruct(&network); err != nil {
return api.Network{}, err
}
@@ -2808,7 +2756,7 @@ func (c *Client) NetworkPut(name string, network api.NetworkPut) error {
return fmt.Errorf("This function isn't supported by public remotes.")
}
- _, err := c.put(fmt.Sprintf("networks/%s", name), network, Sync)
+ _, err := c.put(fmt.Sprintf("networks/%s", name), network, api.SyncResponse)
return err
}
@@ -2817,7 +2765,7 @@ func (c *Client) NetworkDelete(name string) error {
return fmt.Errorf("This function isn't supported by public remotes.")
}
- _, err := c.delete(fmt.Sprintf("networks/%s", name), nil, Sync)
+ _, err := c.delete(fmt.Sprintf("networks/%s", name), nil, api.SyncResponse)
return err
}
@@ -2832,7 +2780,7 @@ func (c *Client) ListNetworks() ([]api.Network, error) {
}
networks := []api.Network{}
- if err := json.Unmarshal(resp.Metadata, &networks); err != nil {
+ if err := resp.MetadataAsStruct(&networks); err != nil {
return nil, err
}
diff --git a/lxc/action.go b/lxc/action.go
index ab66cfe..238cad9 100644
--- a/lxc/action.go
+++ b/lxc/action.go
@@ -92,7 +92,7 @@ func (c *actionCmd) run(config *lxd.Config, args []string) error {
return err
}
- if resp.Type != lxd.Async {
+ if resp.Type != api.AsyncResponse {
return fmt.Errorf(i18n.G("bad result type from action"))
}
diff --git a/lxc/config.go b/lxc/config.go
index 0f2ad48..3314ccf 100644
--- a/lxc/config.go
+++ b/lxc/config.go
@@ -633,7 +633,7 @@ func (c *configCmd) deviceAdd(config *lxd.Config, which string, args []string) e
props = []string{}
}
- var resp *lxd.Response
+ var resp *api.Response
if which == "profile" {
resp, err = client.ProfileDeviceAdd(name, devname, devtype, props)
} else {
@@ -820,7 +820,7 @@ func (c *configCmd) deviceRm(config *lxd.Config, which string, args []string) er
}
devname := args[3]
- var resp *lxd.Response
+ var resp *api.Response
if which == "profile" {
resp, err = client.ProfileDeviceDelete(name, devname)
} else {
diff --git a/lxc/copy.go b/lxc/copy.go
index 4c98735..efdb155 100644
--- a/lxc/copy.go
+++ b/lxc/copy.go
@@ -6,6 +6,7 @@ import (
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/gnuflag"
"github.com/lxc/lxd/shared/i18n"
)
@@ -207,7 +208,7 @@ func (c *copyCmd) copyContainer(config *lxd.Config, sourceResource string, destR
* report that.
*/
for _, addr := range addresses {
- var migration *lxd.Response
+ var migration *api.Response
sourceWSUrl := "https://" + addr + sourceWSResponse.Operation
migration, err = dest.MigrateFrom(destName, sourceWSUrl, source.Certificate, secrets, status.Architecture, status.Config, status.Devices, status.Profiles, baseImage, ephemeral == 1, false, source, sourceWSResponse.Operation)
diff --git a/lxc/init.go b/lxc/init.go
index c94c225..0e6df1d 100644
--- a/lxc/init.go
+++ b/lxc/init.go
@@ -173,7 +173,7 @@ func (c *initCmd) run(config *lxd.Config, args []string) error {
profiles = append(profiles, p)
}
- var resp *lxd.Response
+ var resp *api.Response
if name == "" {
fmt.Printf(i18n.G("Creating the container") + "\n")
} else {
diff --git a/lxc/launch.go b/lxc/launch.go
index 1f8a414..fd3d531 100644
--- a/lxc/launch.go
+++ b/lxc/launch.go
@@ -6,6 +6,7 @@ import (
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/i18n"
"github.com/lxc/lxd/shared/version"
)
@@ -62,7 +63,7 @@ func (c *launchCmd) run(config *lxd.Config, args []string) error {
* initRequestedEmptyProfiles means user requested empty
* !initRequestedEmptyProfiles but len(profArgs) == 0 means use profile default
*/
- var resp *lxd.Response
+ var resp *api.Response
profiles := []string{}
for _, p := range c.init.profArgs {
profiles = append(profiles, p)
diff --git a/lxd/daemon.go b/lxd/daemon.go
index 80c39da..7d565a5 100644
--- a/lxd/daemon.go
+++ b/lxd/daemon.go
@@ -31,6 +31,7 @@ import (
"github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/logging"
"github.com/lxc/lxd/shared/osarch"
"github.com/lxc/lxd/shared/version"
@@ -144,7 +145,7 @@ func (d *Daemon) httpClient(certificate string) (*http.Client, error) {
return &myhttp, nil
}
-func (d *Daemon) httpGetSync(url string, certificate string) (*lxd.Response, error) {
+func (d *Daemon) httpGetSync(url string, certificate string) (*api.Response, error) {
var err error
req, err := http.NewRequest("GET", url, nil)
@@ -169,7 +170,7 @@ func (d *Daemon) httpGetSync(url string, certificate string) (*lxd.Response, err
return nil, err
}
- if resp.Type != lxd.Sync {
+ if resp.Type != api.SyncResponse {
return nil, fmt.Errorf("unexpected non-sync response")
}
@@ -197,7 +198,7 @@ func (d *Daemon) httpGetFile(url string, certificate string) (*http.Response, er
}
if raw.StatusCode != 200 {
- _, err := lxd.HoistResponse(raw, lxd.Error)
+ _, err := lxd.HoistResponse(raw, api.ErrorResponse)
if err != nil {
return nil, err
}
diff --git a/lxd/daemon_images.go b/lxd/daemon_images.go
index 6a8c7ee..3998d7b 100644
--- a/lxd/daemon_images.go
+++ b/lxd/daemon_images.go
@@ -1,7 +1,6 @@
package main
import (
- "encoding/json"
"fmt"
"io"
"io/ioutil"
@@ -292,7 +291,7 @@ func (d *Daemon) ImageDownload(op *operation, server string, protocol string, ce
return "", err
}
- if err := json.Unmarshal(resp.Metadata, &info); err != nil {
+ if err := resp.MetadataAsStruct(&info); err != nil {
return "", err
}
diff --git a/lxd/main_callhook.go b/lxd/main_callhook.go
index b8e2737..31e2231 100644
--- a/lxd/main_callhook.go
+++ b/lxd/main_callhook.go
@@ -7,6 +7,7 @@ import (
"time"
"github.com/lxc/lxd"
+ "github.com/lxc/lxd/shared/api"
)
func cmdCallHook(args []string) error {
@@ -52,7 +53,7 @@ func cmdCallHook(args []string) error {
return
}
- _, err = lxd.HoistResponse(raw, lxd.Sync)
+ _, err = lxd.HoistResponse(raw, api.SyncResponse)
if err != nil {
hook <- err
return
diff --git a/lxd/main_import.go b/lxd/main_import.go
index d7c6b7e..2e74863 100644
--- a/lxd/main_import.go
+++ b/lxd/main_import.go
@@ -5,6 +5,7 @@ import (
"net/http"
"github.com/lxc/lxd"
+ "github.com/lxc/lxd/shared/api"
)
func cmdImport(args []string) error {
@@ -23,7 +24,7 @@ func cmdImport(args []string) error {
}
raw, err := c.Http.Do(req)
- _, err = lxd.HoistResponse(raw, lxd.Sync)
+ _, err = lxd.HoistResponse(raw, api.SyncResponse)
if err != nil {
return err
}
diff --git a/lxd/main_ready.go b/lxd/main_ready.go
index 4e4ebfb..1093234 100644
--- a/lxd/main_ready.go
+++ b/lxd/main_ready.go
@@ -4,6 +4,7 @@ import (
"net/http"
"github.com/lxc/lxd"
+ "github.com/lxc/lxd/shared/api"
)
func cmdReady() error {
@@ -22,7 +23,7 @@ func cmdReady() error {
return err
}
- _, err = lxd.HoistResponse(raw, lxd.Sync)
+ _, err = lxd.HoistResponse(raw, api.SyncResponse)
if err != nil {
return err
}
diff --git a/lxd/main_waitready.go b/lxd/main_waitready.go
index 7e2b4cf..74534b3 100644
--- a/lxd/main_waitready.go
+++ b/lxd/main_waitready.go
@@ -6,6 +6,7 @@ import (
"time"
"github.com/lxc/lxd"
+ "github.com/lxc/lxd/shared/api"
)
func cmdWaitReady() error {
@@ -38,7 +39,7 @@ func cmdWaitReady() error {
continue
}
- _, err = lxd.HoistResponse(raw, lxd.Sync)
+ _, err = lxd.HoistResponse(raw, api.SyncResponse)
if err != nil {
time.Sleep(500 * time.Millisecond)
continue
diff --git a/lxd/remote.go b/lxd/remote.go
index 0a42483..da8bfc4 100644
--- a/lxd/remote.go
+++ b/lxd/remote.go
@@ -1,7 +1,6 @@
package main
import (
- "encoding/json"
"fmt"
"github.com/lxc/lxd/shared/api"
@@ -19,7 +18,7 @@ func remoteGetImageFingerprint(d *Daemon, server string, certificate string, ali
}
var result api.ImageAliasesEntry
- if err = json.Unmarshal(resp.Metadata, &result); err != nil {
+ if err = resp.MetadataAsStruct(&result); err != nil {
return "", fmt.Errorf("Error reading alias")
}
diff --git a/lxd/response.go b/lxd/response.go
index 062e8e1..bf1731c 100644
--- a/lxd/response.go
+++ b/lxd/response.go
@@ -13,26 +13,10 @@ import (
"github.com/mattn/go-sqlite3"
- "github.com/lxc/lxd"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
)
-type syncResp struct {
- Type lxd.ResponseType `json:"type"`
- Status string `json:"status"`
- StatusCode api.StatusCode `json:"status_code"`
- Metadata interface{} `json:"metadata"`
-}
-
-type asyncResp struct {
- Type lxd.ResponseType `json:"type"`
- Status string `json:"status"`
- StatusCode api.StatusCode `json:"status_code"`
- Metadata interface{} `json:"metadata"`
- Operation string `json:"operation"`
-}
-
type Response interface {
Render(w http.ResponseWriter) error
String() string
@@ -73,7 +57,14 @@ func (r *syncResponse) Render(w http.ResponseWriter) error {
w.WriteHeader(201)
}
- resp := syncResp{Type: lxd.Sync, Status: status.String(), StatusCode: status, Metadata: r.metadata}
+ resp := api.ResponseRaw{
+ Response: api.Response{
+ Type: api.SyncResponse,
+ Status: status.String(),
+ StatusCode: int(status)},
+ Metadata: r.metadata,
+ }
+
return WriteJSON(w, resp)
}
@@ -232,12 +223,15 @@ func (r *operationResponse) Render(w http.ResponseWriter) error {
return err
}
- body := asyncResp{
- Type: lxd.Async,
- Status: api.OperationCreated.String(),
- StatusCode: api.OperationCreated,
- Operation: url,
- Metadata: md}
+ body := api.ResponseRaw{
+ Response: api.Response{
+ Type: api.AsyncResponse,
+ Status: api.OperationCreated.String(),
+ StatusCode: int(api.OperationCreated),
+ Operation: url,
+ },
+ Metadata: md,
+ }
w.Header().Set("Location", url)
w.WriteHeader(202)
@@ -279,7 +273,7 @@ func (r *errorResponse) Render(w http.ResponseWriter) error {
output = io.MultiWriter(buf, captured)
}
- err := json.NewEncoder(output).Encode(shared.Jmap{"type": lxd.Error, "error": r.msg, "error_code": r.code})
+ err := json.NewEncoder(output).Encode(shared.Jmap{"type": api.ErrorResponse, "error": r.msg, "error_code": r.code})
if err != nil {
return err
diff --git a/shared/api/response.go b/shared/api/response.go
new file mode 100644
index 0000000..0515985
--- /dev/null
+++ b/shared/api/response.go
@@ -0,0 +1,82 @@
+package api
+
+import (
+ "encoding/json"
+)
+
+// ResponseRaw represents a LXD operation in its original form
+type ResponseRaw struct {
+ Response
+
+ Metadata interface{} `json:"metadata"`
+}
+
+// Response represents a LXD operation
+type Response struct {
+ Type ResponseType `json:"type"`
+
+ /* Valid only for Sync responses */
+ Status string `json:"status"`
+ StatusCode int `json:"status_code"`
+
+ /* Valid only for Async responses */
+ Operation string `json:"operation"`
+
+ /* Valid only for Error responses */
+ Code int `json:"error_code"`
+ Error string `json:"error"`
+
+ /* Valid for Sync and Error responses */
+ Metadata json.RawMessage `json:"metadata"`
+}
+
+// MetadataAsMap parses the Response metadata into a map
+func (r *Response) MetadataAsMap() (map[string]interface{}, error) {
+ ret := map[string]interface{}{}
+ err := r.MetadataAsStruct(&ret)
+ if err != nil {
+ return nil, err
+ }
+
+ return ret, nil
+}
+
+// MetadataAsMap turns the Response metadata into an Operation
+func (r *Response) MetadataAsOperation() (*Operation, error) {
+ op := Operation{}
+ err := r.MetadataAsStruct(&op)
+ if err != nil {
+ return nil, err
+ }
+
+ return &op, nil
+}
+
+// MetadataAsMap parses the Response metadata into a slice of string
+func (r *Response) MetadataAsStringSlice() ([]string, error) {
+ sl := []string{}
+ err := r.MetadataAsStruct(&sl)
+ if err != nil {
+ return nil, err
+ }
+
+ return sl, nil
+}
+
+// MetadataAsMap parses the Response metadata into a provided struct
+func (r *Response) MetadataAsStruct(target interface{}) error {
+ if err := json.Unmarshal(r.Metadata, &target); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// ResponseType represents a valid LXD response type
+type ResponseType string
+
+const (
+ SyncResponse ResponseType = "sync"
+ AsyncResponse ResponseType = "async"
+ ErrorResponse ResponseType = "error"
+)
More information about the lxc-devel
mailing list