[lxc-devel] [distrobuilder/master] Add cloud-init generator

monstermunchkin on Github lxc-bot at linuxcontainers.org
Tue Jul 9 21:06:54 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 310 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20190709/bbdc99c8/attachment.bin>
-------------- next part --------------
From 306b851623bbc31e3843b348854b9d4dc298e944 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Tue, 9 Jul 2019 22:25:49 +0200
Subject: [PATCH 1/2] generators: Add cloud-init generator

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 generators/cloud-init.go      | 108 ++++++++++++++++++++++++++++++++++
 generators/cloud-init_test.go |  97 ++++++++++++++++++++++++++++++
 generators/generators.go      |   2 +
 shared/definition.go          |   1 +
 4 files changed, 208 insertions(+)
 create mode 100644 generators/cloud-init.go
 create mode 100644 generators/cloud-init_test.go

diff --git a/generators/cloud-init.go b/generators/cloud-init.go
new file mode 100644
index 0000000..2078afc
--- /dev/null
+++ b/generators/cloud-init.go
@@ -0,0 +1,108 @@
+package generators
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/lxc/lxd/shared/api"
+
+	"github.com/lxc/distrobuilder/image"
+	"github.com/lxc/distrobuilder/shared"
+)
+
+// CloudInitGenerator represents the cloud-init generator.
+type CloudInitGenerator struct{}
+
+// RunLXC does nothing.
+func (g CloudInitGenerator) RunLXC(cacheDir, sourceDir string, img *image.LXCImage,
+	defFile shared.DefinitionFile) error {
+	// no cloud-init support for LXC, ignoring generator
+	return nil
+}
+
+// RunLXD creates cloud-init template files.
+func (g CloudInitGenerator) RunLXD(cacheDir, sourceDir string, img *image.LXDImage,
+	defFile shared.DefinitionFile) error {
+	templateDir := filepath.Join(cacheDir, "templates")
+
+	err := os.MkdirAll(templateDir, 0755)
+	if err != nil {
+		return err
+	}
+
+	var content string
+	properties := make(map[string]string)
+
+	switch defFile.Name {
+	case "user-data":
+		content = `{{ config_get("user.user-data", properties.default) }}
+`
+		properties["default"] = `#cloud-config
+{}`
+	case "meta-data":
+		content = `instance-id: {{ container.name }}
+local-hostname: {{ container.name }}
+{{ config_get("user.meta-data", "") }}
+`
+	case "vendor-data":
+		content = `{{ config_get("user.vendor-data", properties.default) }}
+`
+		properties["default"] = `#cloud-config
+{}`
+	case "network-config":
+		content = `{% if config_get("user.network-config", "") == "" %}version: 1
+config:
+  - type: physical
+    name: eth0
+    subnets:
+      - type: {% if config_get("user.network_mode", "") == "link-local" %}manual{% else %}dhcp{% endif %}
+        control: auto{% else %}{{ config_get("user.network-config", "") }}{% endif %}
+`
+	default:
+		return fmt.Errorf("Unknown cloud-init configuration: %s", defFile.Name)
+	}
+
+	template := fmt.Sprintf("cloud-init-%s.tpl", defFile.Name)
+
+	file, err := os.Create(filepath.Join(templateDir, template))
+	if err != nil {
+		return err
+	}
+
+	defer file.Close()
+
+	if defFile.Content != "" {
+		content = defFile.Content
+	}
+
+	// Append final new line if missing
+	if !strings.HasSuffix(content, "\n") {
+		content += "\n"
+	}
+
+	_, err = file.WriteString(content)
+	if err != nil {
+		return fmt.Errorf("Failed to write to content to %s template: %s", defFile.Name, err)
+	}
+
+	if len(defFile.Template.Properties) > 0 {
+		properties = defFile.Template.Properties
+	}
+
+	// Add to LXD templates
+	img.Metadata.Templates[filepath.Join("/var/lib/cloud/seed/nocloud-net", defFile.Name)] = &api.ImageMetadataTemplate{
+		Template:   template,
+		Properties: properties,
+		When:       []string{"create", "copy"},
+	}
+
+	return err
+}
+
+// Run does nothing.
+func (g CloudInitGenerator) Run(cacheDir, sourceDir string,
+	defFile shared.DefinitionFile) error {
+	return nil
+}
diff --git a/generators/cloud-init_test.go b/generators/cloud-init_test.go
new file mode 100644
index 0000000..c6b341f
--- /dev/null
+++ b/generators/cloud-init_test.go
@@ -0,0 +1,97 @@
+package generators
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"testing"
+
+	"github.com/lxc/distrobuilder/image"
+	"github.com/lxc/distrobuilder/shared"
+	"github.com/stretchr/testify/require"
+)
+
+func TestCloudInitGeneratorRunLXD(t *testing.T) {
+
+	cacheDir := filepath.Join(os.TempDir(), "distrobuilder-test")
+	rootfsDir := filepath.Join(cacheDir, "rootfs")
+
+	setup(t, cacheDir)
+	defer teardown(cacheDir)
+
+	generator := Get("cloud-init")
+	require.Equal(t, CloudInitGenerator{}, generator)
+
+	definition := shared.Definition{
+		Image: shared.DefinitionImage{
+			Distribution: "ubuntu",
+			Release:      "artful",
+		},
+	}
+
+	image := image.NewLXDImage(cacheDir, "", cacheDir, definition)
+
+	tests := []struct {
+		name       string
+		expected   string
+		shouldFail bool
+	}{
+		{
+			"user-data",
+			`{{ config_get("user.user-data", properties.default) }}
+`,
+			false,
+		},
+		{
+			"meta-data",
+			`instance-id: {{ container.name }}
+local-hostname: {{ container.name }}
+{{ config_get("user.meta-data", "") }}
+`,
+			false,
+		},
+		{
+			"vendor-data",
+			`{{ config_get("user.vendor-data", properties.default) }}
+`,
+			false,
+		},
+		{
+			"network-config",
+			`{% if config_get("user.network-config", "") == "" %}version: 1
+config:
+  - type: physical
+    name: eth0
+    subnets:
+      - type: {% if config_get("user.network_mode", "") == "link-local" %}manual{% else %}dhcp{% endif %}
+        control: auto{% else %}{{ config_get("user.network-config", "") }}{% endif %}
+`,
+			false,
+		},
+		{
+			"foo",
+			"Unknown cloud-init configuration: foo",
+			true,
+		},
+	}
+
+	for i, tt := range tests {
+		log.Printf("Running test #%d: %s", i, tt.name)
+
+		err := generator.RunLXD(cacheDir, rootfsDir, image, shared.DefinitionFile{
+			Generator: "cloud-init",
+			Name:      tt.name,
+		})
+
+		if !tt.shouldFail {
+			require.NoError(t, err)
+		} else {
+			require.Regexp(t, tt.expected, err)
+			continue
+		}
+
+		validateTestFile(t, filepath.Join(cacheDir, "templates", fmt.Sprintf("cloud-init-%s.tpl", tt.name)), tt.expected)
+	}
+
+}
diff --git a/generators/generators.go b/generators/generators.go
index 12b6dd3..f4f1ef6 100644
--- a/generators/generators.go
+++ b/generators/generators.go
@@ -33,6 +33,8 @@ func Get(generator string) Generator {
 		return TemplateGenerator{}
 	case "upstart-tty":
 		return UpstartTTYGenerator{}
+	case "cloud-init":
+		return CloudInitGenerator{}
 	}
 
 	return nil
diff --git a/shared/definition.go b/shared/definition.go
index 9f666ca..21463d0 100644
--- a/shared/definition.go
+++ b/shared/definition.go
@@ -319,6 +319,7 @@ func (d *Definition) Validate() error {
 		"hosts",
 		"remove",
 		"upstart-tty",
+		"cloud-init",
 	}
 
 	for _, file := range d.Files {

From 1628a20d66b1edac70774063ffb971a974853111 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Tue, 9 Jul 2019 22:26:41 +0200
Subject: [PATCH 2/2] doc: Use cloud-init generator in Debian example

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 doc/examples/debian-cloud-init | 65 ++++++----------------------------
 1 file changed, 11 insertions(+), 54 deletions(-)

diff --git a/doc/examples/debian-cloud-init b/doc/examples/debian-cloud-init
index 1fc56e6..15f3431 100644
--- a/doc/examples/debian-cloud-init
+++ b/doc/examples/debian-cloud-init
@@ -72,60 +72,17 @@ files:
      auto eth0
      iface eth0 inet dhcp
 
- - name: cloud-init-meta
-   path: /var/lib/cloud/seed/nocloud-net/meta-data
-   generator: template
-   template:
-     when:
-       - create
-       - copy
-   content: |
-     #cloud-config
-     instance-id: {{ container.name }}
-     local-hostname: {{ container.name }}
-     {{ config_get("user.meta-data", "") }}
-
- - name: cloud-init-network
-   path: /var/lib/cloud/seed/nocloud-net/network-config
-   generator: template
-   template:
-     when:
-       - create
-       - copy
-   content: |
-     {% if config_get("user.network-config", "") == "" %}version: 1
-     config:
-        - type: physical
-          name: eth0
-          subnets:
-              - type: {% if config_get("user.network_mode", "") == "link-local" %}manual{% else %}dhcp{% endif %}
-                control: auto{% else %}{{ config_get("user.network-config", "") }}{% endif %}
-
- - name: cloud-init-user-data
-   path: /var/lib/cloud/seed/nocloud-net/user-data
-   generator: template
-   content: '{{ config_get("user.user-data", properties.default) }}'
-   template:
-     properties:
-       default: |
-         #cloud-config
-         {}
-     when:
-       - create
-       - copy
-
- - name: cloud-init-vedor-data
-   path: /var/lib/cloud/seed/nocloud-net/vendor-data
-   generator: template
-   content: '{{ config_get("user.vendor-data", properties.default) }}'
-   template:
-     properties:
-       default: |
-         #cloud-config
-         {}
-     when:
-       - create
-       - copy
+ - name: meta-data
+   generator: cloud-init
+
+ - name: network-config
+   generator: cloud-init
+
+ - name: user-data
+   generator: cloud-init
+
+ - name: vendor-data
+   generator: cloud-init
 
 packages:
   manager: apt


More information about the lxc-devel mailing list