The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/3584

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
Add a new `lxc config template <op>` section to the CLI to perform operations related to template files.
From 9658d34230e135e7c2d646db378c247663657a79 Mon Sep 17 00:00:00 2001
From: Alberto Donato <[email protected]>
Date: Mon, 24 Jul 2017 11:00:08 +0200
Subject: [PATCH] client: Add container template files operations.

Signed-off-by: Alberto Donato <[email protected]>
---
 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
 }
_______________________________________________
lxc-devel mailing list
[email protected]
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to