[lxc-devel] [lxd/master] client: Add container template files operations
albertodonato on Github
lxc-bot at linuxcontainers.org
Mon Jul 24 12:45:18 UTC 2017
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 405 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170724/f5b71cda/attachment.bin>
-------------- next part --------------
From 9658d34230e135e7c2d646db378c247663657a79 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.donato at canonical.com>
Date: Mon, 24 Jul 2017 11:00:08 +0200
Subject: [PATCH] client: Add container template files operations.
Signed-off-by: Alberto Donato <alberto.donato at canonical.com>
---
client/interfaces.go | 6 ++
client/lxd_containers.go | 100 +++++++++++++++++++++++++++++++
doc/rest-api.md | 2 +-
lxc/config.go | 152 +++++++++++++++++++++++++++++++++++++++++++++++
test/suites/config.sh | 16 +++--
5 files changed, 269 insertions(+), 7 deletions(-)
diff --git a/client/interfaces.go b/client/interfaces.go
index ed72b5c28..f0cee532e 100644
--- a/client/interfaces.go
+++ b/client/interfaces.go
@@ -89,6 +89,12 @@ type ContainerServer interface {
GetContainerMetadata(name string) (*api.ImageMetadata, string, error)
SetContainerMetadata(name string, metadata api.ImageMetadata, ETag string) error
+ GetContainerTemplateFiles(containerName string) (templates []string, err error)
+ GetContainerTemplateFile(containerName string, templateName string) (content io.ReadCloser, err error)
+ CreateContainerTemplateFile(containerName string, templateName string) (err error)
+ UpdateContainerTemplateFile(containerName string, templateName string, content io.ReadSeeker) (err error)
+ DeleteContainerTemplateFile(name string, templateName string) (err error)
+
// Event handling functions
GetEvents() (listener *EventListener, err error)
diff --git a/client/lxd_containers.go b/client/lxd_containers.go
index 00cf9d0e9..ff7ed61d0 100644
--- a/client/lxd_containers.go
+++ b/client/lxd_containers.go
@@ -1,6 +1,7 @@
package lxd
import (
+ "bytes"
"encoding/json"
"fmt"
"io"
@@ -1304,3 +1305,102 @@ func (r *ProtocolLXD) SetContainerMetadata(name string, metadata api.ImageMetada
return nil
}
+
+// GetContainerTemplateFiles returns the list of name of a template files for a container.
+func (r *ProtocolLXD) GetContainerTemplateFiles(containerName string) ([]string, error) {
+ if !r.HasExtension("container_edit_metadata") {
+ return nil, fmt.Errorf("The server is missing the required \"container_edit_metadata\" API extension")
+ }
+
+ templates := []string{}
+
+ url := fmt.Sprintf("/containers/%s/metadata/templates", containerName)
+ _, err := r.queryStruct("GET", url, nil, "", &templates)
+ if err != nil {
+ return nil, err
+ }
+
+ return templates, nil
+}
+
+// GetContainerTemplateFile returns the content of a template file for a container.
+func (r *ProtocolLXD) GetContainerTemplateFile(containerName string, templateName string) (io.ReadCloser, error) {
+ if !r.HasExtension("container_edit_metadata") {
+ return nil, fmt.Errorf("The server is missing the required \"container_edit_metadata\" API extension")
+ }
+
+ url := fmt.Sprintf("%s/1.0/containers/%s/metadata/templates?path=%s", r.httpHost, containerName, templateName)
+ req, err := http.NewRequest("GET", url, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ // Set the user agent
+ if r.httpUserAgent != "" {
+ req.Header.Set("User-Agent", r.httpUserAgent)
+ }
+
+ // Send the request
+ resp, err := r.http.Do(req)
+ if err != nil {
+ return nil, err
+ }
+
+ // Check the return value for a cleaner error
+ if resp.StatusCode != http.StatusOK {
+ _, _, err := r.parseResponse(resp)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return resp.Body, err
+}
+
+// CreateContainerTemplateFile creates an empty file template for a container.
+func (r *ProtocolLXD) CreateContainerTemplateFile(containerName string, templateName string) error {
+ return r.setContainerTemplateFile(containerName, templateName, bytes.NewReader(nil), "POST")
+}
+
+// UpdateContainerTemplateFile updates content for a container template file.
+func (r *ProtocolLXD) UpdateContainerTemplateFile(containerName string, templateName string, content io.ReadSeeker) error {
+ return r.setContainerTemplateFile(containerName, templateName, content, "PUT")
+}
+
+func (r *ProtocolLXD) setContainerTemplateFile(containerName string, templateName string, content io.ReadSeeker, httpMethod string) error {
+ if !r.HasExtension("container_edit_metadata") {
+ return fmt.Errorf("The server is missing the required \"container_edit_metadata\" API extension")
+ }
+
+ url := fmt.Sprintf("%s/1.0/containers/%s/metadata/templates?path=%s", r.httpHost, containerName, templateName)
+ req, err := http.NewRequest(httpMethod, url, content)
+ if err != nil {
+ return err
+ }
+ req.Header.Set("Content-Type", "application/octet-stream")
+
+ // Set the user agent
+ if r.httpUserAgent != "" {
+ req.Header.Set("User-Agent", r.httpUserAgent)
+ }
+
+ // Send the request
+ resp, err := r.http.Do(req)
+ // Check the return value for a cleaner error
+ if resp.StatusCode != http.StatusOK {
+ _, _, err := r.parseResponse(resp)
+ if err != nil {
+ return err
+ }
+ }
+ return err
+}
+
+// DeleteContainerTemplateFile deletes a template file for a container.
+func (r *ProtocolLXD) DeleteContainerTemplateFile(name string, templateName string) error {
+ if !r.HasExtension("container_edit_metadata") {
+ return fmt.Errorf("The server is missing the required \"container_edit_metadata\" API extension")
+ }
+ _, _, err := r.query("DELETE", fmt.Sprintf("/containers/%s/metadata/templates?path=%s", name, templateName), nil, "")
+ return err
+}
diff --git a/doc/rest-api.md b/doc/rest-api.md
index 6ba4cc8c7..cffafd85c 100644
--- a/doc/rest-api.md
+++ b/doc/rest-api.md
@@ -1268,7 +1268,7 @@ Return:
* Operation: Sync
* Return: the content of the template
-## POST (?path=\<template\>)
+### POST (?path=\<template\>)
* Description: Add a continer template
* Introduced: with API extension "container\_edit\_metadata"
* Authentication: trusted
diff --git a/lxc/config.go b/lxc/config.go
index e66fb24ee..aaa58dff6 100644
--- a/lxc/config.go
+++ b/lxc/config.go
@@ -1,6 +1,7 @@
package main
import (
+ "bytes"
"crypto/x509"
"encoding/base64"
"encoding/pem"
@@ -111,6 +112,24 @@ lxc config metadata show [<remote>:][container]
lxc config metadata edit [<remote>:][container]
Edit the container metadata.yaml, either by launching external editor or reading STDIN.
+*Container templates*
+
+lxc config template list [<remote>:][container]
+ List the names of template files for a container.
+
+lxc config template show [<remote>:][container] [template]
+ Show the content of a template file for a container.
+
+lxc config template create [<remote>:][container] [template]
+ Add an empty template file for a container.
+
+lxc config template edit [<remote>:][container] [template]
+ Edit the content of a template file for a container, either by launching external editor or reading STDIN.
+
+lxc config template delete [<remote>:][container] [template]
+ Delete a template file for a container.
+
+
*Device management*
lxc config device add [<remote>:]<container> <device> <type> [key=value...]
@@ -650,6 +669,73 @@ func (c *configCmd) run(conf *config.Config, args []string) error {
return errArgs
}
+ case "template":
+ if len(args) < 3 {
+ return errArgs
+ }
+
+ remote, container, err := conf.ParseRemote(args[2])
+ if err != nil {
+ return err
+ }
+
+ d, err := conf.GetContainerServer(remote)
+ if err != nil {
+ return err
+ }
+
+ switch args[1] {
+ case "list":
+ templates, err := d.GetContainerTemplateFiles(container)
+ if err != nil {
+ return err
+ }
+
+ c.listTemplateFiles(templates)
+ return nil
+
+ case "show":
+ if len(args) != 4 {
+ return errArgs
+ }
+ templateName := args[3]
+
+ template, err := d.GetContainerTemplateFile(container, templateName)
+ if err != nil {
+ return err
+ }
+ content, err := ioutil.ReadAll(template)
+ if err != nil {
+ return err
+ }
+ fmt.Printf("%s", content)
+ return nil
+
+ case "create":
+ if len(args) != 4 {
+ return errArgs
+ }
+ templateName := args[3]
+ return c.doContainerTemplateFileCreate(d, container, templateName)
+
+ case "edit":
+ if len(args) != 4 {
+ return errArgs
+ }
+ templateName := args[3]
+ return c.doContainerTemplateFileEdit(d, container, templateName)
+
+ case "delete":
+ if len(args) != 4 {
+ return errArgs
+ }
+ templateName := args[3]
+ return d.DeleteContainerTemplateFile(container, templateName)
+
+ default:
+ return errArgs
+ }
+
default:
return errArgs
}
@@ -657,6 +743,22 @@ func (c *configCmd) run(conf *config.Config, args []string) error {
return errArgs
}
+func (c *configCmd) listTemplateFiles(templates []string) {
+ data := [][]string{}
+ for _, template := range templates {
+ data = append(data, []string{template})
+ }
+
+ table := tablewriter.NewWriter(os.Stdout)
+ table.SetAutoWrapText(false)
+ table.SetAlignment(tablewriter.ALIGN_LEFT)
+ table.SetRowLine(true)
+ table.SetHeader([]string{i18n.G("FILENAME")})
+ sort.Sort(byName(data))
+ table.AppendBulk(data)
+ table.Render()
+}
+
func (c *configCmd) doContainerConfigEdit(client lxd.ContainerServer, cont string) error {
// If stdin isn't a terminal, read text from it
if !termios.IsTerminal(int(syscall.Stdin)) {
@@ -1246,3 +1348,53 @@ func (c *configCmd) doContainerMetadataEdit(client lxd.ContainerServer, name str
return nil
}
+
+func (c *configCmd) doContainerTemplateFileCreate(client lxd.ContainerServer, containerName string, templateName string) error {
+ return client.CreateContainerTemplateFile(containerName, templateName)
+}
+
+func (c *configCmd) doContainerTemplateFileEdit(client lxd.ContainerServer, containerName string, templateName string) error {
+ if !termios.IsTerminal(int(syscall.Stdin)) {
+ return client.UpdateContainerTemplateFile(containerName, templateName, os.Stdin)
+ }
+
+ reader, err := client.GetContainerTemplateFile(containerName, templateName)
+ if err != nil {
+ return err
+ }
+ content, err := ioutil.ReadAll(reader)
+ if err != nil {
+ return err
+ }
+
+ // Spawn the editor
+ content, err = shared.TextEditor("", content)
+ if err != nil {
+ return err
+ }
+
+ for {
+ reader := bytes.NewReader(content)
+ err := client.UpdateContainerTemplateFile(containerName, templateName, reader)
+ // Respawn the editor
+ if err != nil {
+ fmt.Fprintf(os.Stderr, i18n.G("Error updating template file: %s")+"\n", err)
+ fmt.Println(i18n.G("Press enter to start the editor again"))
+
+ _, err := os.Stdin.Read(make([]byte, 1))
+ if err != nil {
+ return err
+ }
+
+ content, err = shared.TextEditor("", content)
+ if err != nil {
+ return err
+ }
+ continue
+ }
+
+ break
+ }
+
+ return nil
+}
diff --git a/test/suites/config.sh b/test/suites/config.sh
index 9b3bf551a..3231bd431 100644
--- a/test/suites/config.sh
+++ b/test/suites/config.sh
@@ -273,18 +273,22 @@ test_container_metadata() {
lxc config metadata show c | grep -q BB
# templates can be listed
- curl -s --unix-socket "${LXD_DIR}/unix.socket" lxd/1.0/containers/c/metadata/templates | grep -q template.tpl
+ lxc config template list c | grep -q template.tpl
# template content can be returned
- curl -s --unix-socket "${LXD_DIR}/unix.socket" "lxd/1.0/containers/c/metadata/templates?path=template.tpl" | grep -q "name:"
+ lxc config template show c template.tpl | grep -q "name:"
# templates can be added
- curl -s --unix-socket "${LXD_DIR}/unix.socket" "lxd/1.0/containers/c/metadata/templates?path=my.tpl" -H 'Content-type: application/octet-stream' -d "some content"
- curl -s --unix-socket "${LXD_DIR}/unix.socket" "lxd/1.0/containers/c/metadata/templates?path=my.tpl" | grep -q "some content"
+ lxc config template create c my.tpl
+ lxc config template list c | grep -q my.tpl
+
+ # template content can be updated
+ echo "some content" | lxc config template edit c my.tpl
+ lxc config template show c my.tpl | grep -q "some content"
# templates can be removed
- curl -s --unix-socket "${LXD_DIR}/unix.socket" "lxd/1.0/containers/c/metadata/templates?path=my.tpl" -X DELETE
- ! (curl -s --unix-socket "${LXD_DIR}/unix.socket" lxd/1.0/containers/c/metadata/templates | grep -q my.tpl)
+ lxc config template delete c my.tpl
+ ! (lxc config template list c | grep -q my.tpl)
lxc delete c
}
More information about the lxc-devel
mailing list