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

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) ===

From db0da736dd3737f2d2071263857248edd02a4ac1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com>
Date: Sun, 1 Mar 2020 15:16:39 +0100
Subject: [PATCH 1/2] lxd/vm: Generate the template files
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgra...@ubuntu.com>
---
 lxd/instance/drivers/driver_qemu.go | 146 ++++++++++++++++++++++++++++
 1 file changed, 146 insertions(+)

diff --git a/lxd/instance/drivers/driver_qemu.go 
b/lxd/instance/drivers/driver_qemu.go
index a8f0e41006..4c9e633c70 100644
--- a/lxd/instance/drivers/driver_qemu.go
+++ b/lxd/instance/drivers/driver_qemu.go
@@ -17,10 +17,12 @@ import (
        "text/template"
        "time"
 
+       "github.com/flosch/pongo2"
        "github.com/gorilla/websocket"
        "github.com/pborman/uuid"
        "github.com/pkg/errors"
        "golang.org/x/sys/unix"
+       "gopkg.in/yaml.v2"
 
        lxdClient "github.com/lxc/lxd/client"
        "github.com/lxc/lxd/lxd/backup"
@@ -41,6 +43,7 @@ import (
        "github.com/lxc/lxd/lxd/state"
        storagePools "github.com/lxc/lxd/lxd/storage"
        storageDrivers "github.com/lxc/lxd/lxd/storage/drivers"
+       pongoTemplate "github.com/lxc/lxd/lxd/template"
        "github.com/lxc/lxd/lxd/util"
        "github.com/lxc/lxd/lxd/vsock"
        "github.com/lxc/lxd/shared"
@@ -1267,6 +1270,144 @@ echo "To start it now, unmount this filesystem and run: 
systemctl start lxd-agen
                return err
        }
 
+       // Templated files.
+       err = os.MkdirAll(filepath.Join(configDrivePath, "files"), 0500)
+       if err != nil {
+               return err
+       }
+
+       // Template anything that needs templating.
+       key := "volatile.apply_template"
+       if vm.localConfig[key] != "" {
+               // Run any template that needs running.
+               err = vm.templateApplyNow(vm.localConfig[key], 
filepath.Join(configDrivePath, "files"))
+               if err != nil {
+                       return err
+               }
+
+               // Remove the volatile key from the DB.
+               err := vm.state.Cluster.ContainerConfigRemove(vm.id, key)
+               if err != nil {
+                       return err
+               }
+       }
+
+       err = vm.templateApplyNow("start", filepath.Join(configDrivePath, 
"files"))
+       if err != nil {
+               return err
+       }
+
+       // Copy the template metadata itself too.
+       metaPath := filepath.Join(vm.Path(), "metadata.yaml")
+       if shared.PathExists(metaPath) {
+               err = shared.FileCopy(metaPath, filepath.Join(configDrivePath, 
"files/metadata.yaml"))
+               if err != nil {
+                       return err
+               }
+       }
+
+       return nil
+}
+
+func (vm *qemu) templateApplyNow(trigger string, path string) error {
+       // If there's no metadata, just return.
+       fname := filepath.Join(vm.Path(), "metadata.yaml")
+       if !shared.PathExists(fname) {
+               return nil
+       }
+
+       // Parse the metadata.
+       content, err := ioutil.ReadFile(fname)
+       if err != nil {
+               return errors.Wrap(err, "Failed to read metadata")
+       }
+
+       metadata := new(api.ImageMetadata)
+       err = yaml.Unmarshal(content, &metadata)
+       if err != nil {
+               return errors.Wrapf(err, "Could not parse %s", fname)
+       }
+
+       // Figure out the instance architecture.
+       arch, err := osarch.ArchitectureName(vm.architecture)
+       if err != nil {
+               arch, err = 
osarch.ArchitectureName(vm.state.OS.Architectures[0])
+               if err != nil {
+                       return errors.Wrap(err, "Failed to detect system 
architecture")
+               }
+       }
+
+       // Generate the container metadata
+       instanceMeta := make(map[string]string)
+       instanceMeta["name"] = vm.name
+       instanceMeta["architecture"] = arch
+
+       if vm.ephemeral {
+               instanceMeta["ephemeral"] = "true"
+       } else {
+               instanceMeta["ephemeral"] = "false"
+       }
+
+       // Go through the templates
+       for tplPath, tpl := range metadata.Templates {
+               var w *os.File
+
+               // Check if the template should be applied now
+               found := false
+               for _, tplTrigger := range tpl.When {
+                       if tplTrigger == trigger {
+                               found = true
+                               break
+                       }
+               }
+
+               if !found {
+                       continue
+               }
+
+               // Create the file itself
+               w, err = os.Create(filepath.Join(path, fmt.Sprintf("%s.out", 
tpl.Template)))
+               if err != nil {
+                       return err
+               }
+
+               // Fix ownership and mode
+               w.Chmod(0644)
+               defer w.Close()
+
+               // Read the template
+               tplString, err := 
ioutil.ReadFile(filepath.Join(vm.TemplatesPath(), tpl.Template))
+               if err != nil {
+                       return errors.Wrap(err, "Failed to read template file")
+               }
+
+               // Restrict filesystem access to within the container's rootfs
+               tplSet := pongo2.NewSet(fmt.Sprintf("%s-%s", vm.name, 
tpl.Template), pongoTemplate.ChrootLoader{Path: vm.TemplatesPath()})
+               tplRender, err := tplSet.FromString("{% autoescape off %}" + 
string(tplString) + "{% endautoescape %}")
+               if err != nil {
+                       return errors.Wrap(err, "Failed to render template")
+               }
+
+               configGet := func(confKey, confDefault *pongo2.Value) 
*pongo2.Value {
+                       val, ok := vm.expandedConfig[confKey.String()]
+                       if !ok {
+                               return confDefault
+                       }
+
+                       return pongo2.AsValue(strings.TrimRight(val, "\r\n"))
+               }
+
+               // Render the template
+               tplRender.ExecuteWriter(pongo2.Context{"trigger": trigger,
+                       "path":       tplPath,
+                       "instance":   instanceMeta,
+                       "container":  instanceMeta, // FIXME: remove once most 
images have moved away.
+                       "config":     vm.expandedConfig,
+                       "devices":    vm.expandedDevices,
+                       "properties": tpl.Properties,
+                       "config_get": configGet}, w)
+       }
+
        return nil
 }
 
@@ -3436,6 +3577,11 @@ func (vm *qemu) StorageStop() (bool, error) {
 
 // DeferTemplateApply not used currently.
 func (vm *qemu) DeferTemplateApply(trigger string) error {
+       err := vm.VolatileSet(map[string]string{"volatile.apply_template": 
trigger})
+       if err != nil {
+               return errors.Wrap(err, "Failed to set apply_template volatile 
key")
+       }
+
        return nil
 }
 

From 381cedceb7f601fb17404e75a3902018bdd90cf2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgra...@ubuntu.com>
Date: Mon, 2 Mar 2020 15:22:02 +0100
Subject: [PATCH 2/2] lxd-agent: Put templates in place
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgra...@ubuntu.com>
---
 lxd-agent/main_agent.go | 38 ++++++++++++------
 lxd-agent/templates.go  | 86 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 112 insertions(+), 12 deletions(-)
 create mode 100644 lxd-agent/templates.go

diff --git a/lxd-agent/main_agent.go b/lxd-agent/main_agent.go
index 3a6aaf9fa9..8b05ae7c3c 100644
--- a/lxd-agent/main_agent.go
+++ b/lxd-agent/main_agent.go
@@ -3,9 +3,9 @@ package main
 import (
        "context"
        "fmt"
+       "io"
        "os"
        "os/signal"
-       "path/filepath"
        "time"
 
        "github.com/pkg/errors"
@@ -49,24 +49,38 @@ func (c *cmdAgent) Run(cmd *cobra.Command, args []string) 
error {
        logger.Info("lxd-agent starting")
        defer logger.Info("lxd-agent stopped")
 
-       // Setup cloud-init.
-       if shared.PathExists("/etc/cloud") && 
!shared.PathExists("/var/lib/cloud/seed/nocloud-net") {
-               err := os.MkdirAll("/var/lib/cloud/seed/nocloud-net/", 0700)
+       // Apply the templated files.
+       files, err := templatesApply("files/")
+       if err != nil {
+               return err
+       }
+
+       // Sync the hostname.
+       if shared.PathExists("/proc/sys/kernel/hostname") && 
shared.StringInSlice("/etc/hostname", files) {
+               // Open the two files.
+               src, err := os.Open("/etc/hostname")
                if err != nil {
                        return err
                }
 
-               for _, fName := range []string{"meta-data", "user-data", 
"vendor-data", "network-config"} {
-                       if !shared.PathExists(filepath.Join("cloud-init", 
fName)) {
-                               continue
-                       }
+               dst, err := os.Create("/proc/sys/kernel/hostname")
+               if err != nil {
+                       return err
+               }
 
-                       err := shared.FileCopy(filepath.Join("cloud-init", 
fName), filepath.Join("/var/lib/cloud/seed/nocloud-net", fName))
-                       if err != nil {
-                               return err
-                       }
+               // Copy the data.
+               _, err = io.Copy(dst, src)
+               if err != nil {
+                       return err
                }
 
+               // Close the files.
+               src.Close()
+               dst.Close()
+       }
+
+       // Run cloud-init.
+       if shared.PathExists("/etc/cloud") && 
shared.StringInSlice("/var/lib/cloud/seed/nocloud-net/meta-data", files) {
                if shared.PathExists("/run/cloud-init") {
                        err = os.RemoveAll("/run/cloud-init")
                        if err != nil {
diff --git a/lxd-agent/templates.go b/lxd-agent/templates.go
new file mode 100644
index 0000000000..8a2833d892
--- /dev/null
+++ b/lxd-agent/templates.go
@@ -0,0 +1,86 @@
+package main
+
+import (
+       "fmt"
+       "io"
+       "io/ioutil"
+       "os"
+       "path/filepath"
+
+       "github.com/pkg/errors"
+       "gopkg.in/yaml.v2"
+
+       "github.com/lxc/lxd/shared"
+       "github.com/lxc/lxd/shared/api"
+)
+
+func templatesApply(path string) ([]string, error) {
+       metaName := filepath.Join(path, "metadata.yaml")
+       if !shared.PathExists(metaName) {
+               return nil, nil
+       }
+
+       // Parse the metadata.
+       content, err := ioutil.ReadFile(metaName)
+       if err != nil {
+               return nil, errors.Wrap(err, "Failed to read metadata")
+       }
+
+       metadata := new(api.ImageMetadata)
+       err = yaml.Unmarshal(content, &metadata)
+       if err != nil {
+               return nil, errors.Wrap(err, "Could not parse metadata.yaml")
+       }
+
+       // Go through the files and copy them into place.
+       files := []string{}
+       for tplPath, tpl := range metadata.Templates {
+               filePath := filepath.Join(path, fmt.Sprintf("%s.out", 
tpl.Template))
+
+               if !shared.PathExists(filePath) {
+                       continue
+               }
+
+               var w *os.File
+               if shared.PathExists(tplPath) {
+                       if tpl.CreateOnly {
+                               continue
+                       }
+
+                       // Open the existing file.
+                       w, err = os.Create(tplPath)
+                       if err != nil {
+                               return nil, errors.Wrap(err, "Failed to create 
template file")
+                       }
+               } else {
+                       // Create the directories leading to the file.
+                       os.MkdirAll(filepath.Dir(tplPath), 0755)
+
+                       // Create the file itself.
+                       w, err = os.Create(tplPath)
+                       if err != nil {
+                               return nil, err
+                       }
+
+                       // Fix mode.
+                       w.Chmod(0644)
+               }
+               defer w.Close()
+
+               // Do the copy.
+               src, err := os.Open(filePath)
+               if err != nil {
+                       return nil, err
+               }
+               defer src.Close()
+
+               _, err = io.Copy(w, src)
+               if err != nil {
+                       return nil, err
+               }
+
+               files = append(files, tplPath)
+       }
+
+       return files, nil
+}
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to