Allow customizations to be specified as a toml list
Support both
[customizations]
hostname = "whatever"
and
[[customizations]]
hostname = "whatever"
in the blueprint data. The [[ syntax matches the other customization
directives (user, group, sshkey), and as such it's easy to accidentally
use it for the hostname without even realizing it's specifying something
different.
Add some tests for converting customizations to kickstarts.
(cherry picked from commit 35ab6a1336
)
Resolves: rhbz#1666517
This commit is contained in:
parent
56d0e704c0
commit
252c9f6226
@ -231,6 +231,10 @@ def add_customizations(f, recipe):
|
|||||||
return
|
return
|
||||||
customizations = recipe["customizations"]
|
customizations = recipe["customizations"]
|
||||||
|
|
||||||
|
# allow customizations to be incorrectly specified as [[customizations]] instead of [customizations]
|
||||||
|
if isinstance(customizations, list):
|
||||||
|
customizations = customizations[0]
|
||||||
|
|
||||||
if "hostname" in customizations:
|
if "hostname" in customizations:
|
||||||
f.write("network --hostname=%s\n" % customizations["hostname"])
|
f.write("network --hostname=%s\n" % customizations["hostname"])
|
||||||
|
|
||||||
|
@ -22,8 +22,12 @@ import tempfile
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import pylorax.api.recipes as recipes
|
import pylorax.api.recipes as recipes
|
||||||
|
from pylorax.api.compose import add_customizations
|
||||||
from pylorax.sysutils import joinpaths
|
from pylorax.sysutils import joinpaths
|
||||||
|
|
||||||
|
from pykickstart.parser import KickstartParser
|
||||||
|
from pykickstart.version import makeVersion
|
||||||
|
|
||||||
class BasicRecipeTest(unittest.TestCase):
|
class BasicRecipeTest(unittest.TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(self):
|
def setUpClass(self):
|
||||||
@ -351,3 +355,215 @@ class GetRevisionFromTagTests(unittest.TestCase):
|
|||||||
def test_02_invalid_tag_missing_revision_string(self):
|
def test_02_invalid_tag_missing_revision_string(self):
|
||||||
revision = recipes.get_revision_from_tag('branch/filename/mybranch')
|
revision = recipes.get_revision_from_tag('branch/filename/mybranch')
|
||||||
self.assertIsNone(revision)
|
self.assertIsNone(revision)
|
||||||
|
|
||||||
|
class CustomizationsTests(unittest.TestCase):
|
||||||
|
@staticmethod
|
||||||
|
def _blueprint_to_ks(blueprint_data):
|
||||||
|
recipe_obj = recipes.recipe_from_toml(blueprint_data)
|
||||||
|
ks = KickstartParser(makeVersion())
|
||||||
|
|
||||||
|
# write out the customization data, and parse the resulting kickstart
|
||||||
|
with tempfile.NamedTemporaryFile(prefix="lorax.test.customizations", mode="w") as f:
|
||||||
|
add_customizations(f, recipe_obj)
|
||||||
|
f.flush()
|
||||||
|
ks.readKickstart(f.name)
|
||||||
|
|
||||||
|
return ks
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _find_user(ks, username):
|
||||||
|
for user in ks.handler.user.userList:
|
||||||
|
if user.name == username:
|
||||||
|
return user
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _find_sshkey(ks, username):
|
||||||
|
for key in ks.handler.sshkey.sshUserList:
|
||||||
|
if key.username == username:
|
||||||
|
return key
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _find_group(ks, groupname):
|
||||||
|
for group in ks.handler.group.groupList:
|
||||||
|
if group.name == groupname:
|
||||||
|
return group
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def test_hostname(self):
|
||||||
|
blueprint_data = """name = "test-hostname"
|
||||||
|
description = "test recipe"
|
||||||
|
version = "0.0.1"
|
||||||
|
|
||||||
|
[customizations]
|
||||||
|
hostname = "testy.example.com"
|
||||||
|
"""
|
||||||
|
ks = self._blueprint_to_ks(blueprint_data)
|
||||||
|
self.assertEqual(ks.handler.network.hostname, "testy.example.com")
|
||||||
|
|
||||||
|
def test_hostname_list(self):
|
||||||
|
"""Test that the hostname still works when using [[customizations]] instead of [customizations]"""
|
||||||
|
|
||||||
|
blueprint_data = """name = "test-hostname-list"
|
||||||
|
description = "test recipe"
|
||||||
|
version = "0.0.1"
|
||||||
|
|
||||||
|
[[customizations]]
|
||||||
|
hostname = "testy.example.com"
|
||||||
|
"""
|
||||||
|
ks = self._blueprint_to_ks(blueprint_data)
|
||||||
|
self.assertEqual(ks.handler.network.hostname, "testy.example.com")
|
||||||
|
|
||||||
|
def test_user(self):
|
||||||
|
blueprint_data = """name = "test-user"
|
||||||
|
description = "test recipe"
|
||||||
|
version = "0.0.1"
|
||||||
|
|
||||||
|
[[customizations.user]]
|
||||||
|
name = "admin"
|
||||||
|
description = "Widget admin account"
|
||||||
|
password = "$6$CHO2$3rN8eviE2t50lmVyBYihTgVRHcaecmeCk31LeOUleVK/R/aeWVHVZDi26zAH.o0ywBKH9Tc0/wm7sW/q39uyd1"
|
||||||
|
home = "/srv/widget/"
|
||||||
|
shell = "/usr/bin/bash"
|
||||||
|
groups = ["widget", "users", "students"]
|
||||||
|
uid = 1200
|
||||||
|
|
||||||
|
[[customizations.user]]
|
||||||
|
name = "bart"
|
||||||
|
key = "SSH KEY FOR BART"
|
||||||
|
groups = ["students"]
|
||||||
|
"""
|
||||||
|
|
||||||
|
ks = self._blueprint_to_ks(blueprint_data)
|
||||||
|
|
||||||
|
admin = self._find_user(ks, "admin")
|
||||||
|
self.assertIsNotNone(admin)
|
||||||
|
self.assertEqual(admin.name, "admin")
|
||||||
|
self.assertEqual(admin.password, "$6$CHO2$3rN8eviE2t50lmVyBYihTgVRHcaecmeCk31LeOUleVK/R/aeWVHVZDi26zAH.o0ywBKH9Tc0/wm7sW/q39uyd1")
|
||||||
|
self.assertEqual(admin.homedir, "/srv/widget/")
|
||||||
|
self.assertEqual(admin.shell, "/usr/bin/bash")
|
||||||
|
# order is unimportant, so use a set instead of comparing lists directly
|
||||||
|
self.assertEqual(set(admin.groups), {"widget", "users", "students"})
|
||||||
|
self.assertEqual(admin.uid, 1200)
|
||||||
|
|
||||||
|
bart = self._find_user(ks, "bart")
|
||||||
|
self.assertIsNotNone(bart)
|
||||||
|
self.assertEqual(bart.name, "bart")
|
||||||
|
self.assertEqual(bart.groups, ["students"])
|
||||||
|
|
||||||
|
bartkey = self._find_sshkey(ks, "bart")
|
||||||
|
self.assertIsNotNone(bartkey)
|
||||||
|
self.assertEqual(bartkey.username, "bart")
|
||||||
|
self.assertEqual(bartkey.key, "SSH KEY FOR BART")
|
||||||
|
|
||||||
|
def test_group(self):
|
||||||
|
blueprint_data = """name = "test-group"
|
||||||
|
description = "test recipe"
|
||||||
|
version = "0.0.1"
|
||||||
|
|
||||||
|
[[customizations.group]]
|
||||||
|
name = "widget"
|
||||||
|
|
||||||
|
[[customizations.group]]
|
||||||
|
name = "students"
|
||||||
|
"""
|
||||||
|
|
||||||
|
ks = self._blueprint_to_ks(blueprint_data)
|
||||||
|
|
||||||
|
widget = self._find_group(ks, "widget")
|
||||||
|
self.assertIsNotNone(widget)
|
||||||
|
|
||||||
|
students = self._find_group(ks, "students")
|
||||||
|
self.assertIsNotNone(students)
|
||||||
|
|
||||||
|
def test_full(self):
|
||||||
|
blueprint_data = """name = "custom-base"
|
||||||
|
description = "A base system with customizations"
|
||||||
|
version = "0.0.1"
|
||||||
|
modules = []
|
||||||
|
groups = []
|
||||||
|
|
||||||
|
[[packages]]
|
||||||
|
name = "bash"
|
||||||
|
version = "4.4.*"
|
||||||
|
|
||||||
|
[[customizations]]
|
||||||
|
hostname = "custom-base"
|
||||||
|
|
||||||
|
[[customizations.sshkey]]
|
||||||
|
user = "root"
|
||||||
|
key = "ssh-rsa"
|
||||||
|
|
||||||
|
[[customizations.user]]
|
||||||
|
name = "widget"
|
||||||
|
description = "Widget process user account"
|
||||||
|
home = "/srv/widget/"
|
||||||
|
shell = "/usr/bin/false"
|
||||||
|
groups = ["dialout", "users"]
|
||||||
|
|
||||||
|
[[customizations.user]]
|
||||||
|
name = "admin"
|
||||||
|
description = "Widget admin account"
|
||||||
|
password = ""
|
||||||
|
home = "/srv/widget/"
|
||||||
|
shell = "/usr/bin/bash"
|
||||||
|
groups = ["widget", "users", "students"]
|
||||||
|
uid = 1200
|
||||||
|
|
||||||
|
[[customizations.user]]
|
||||||
|
name = "plain"
|
||||||
|
password = "password"
|
||||||
|
|
||||||
|
[[customizations.user]]
|
||||||
|
name = "bart"
|
||||||
|
key = ""
|
||||||
|
groups = ["students"]
|
||||||
|
|
||||||
|
[[customizations.group]]
|
||||||
|
name = "widget"
|
||||||
|
|
||||||
|
[[customizations.group]]
|
||||||
|
name = "students"
|
||||||
|
"""
|
||||||
|
ks = self._blueprint_to_ks(blueprint_data)
|
||||||
|
|
||||||
|
self.assertEqual(ks.handler.network.hostname, "custom-base")
|
||||||
|
|
||||||
|
rootkey = self._find_sshkey(ks, "root")
|
||||||
|
self.assertIsNotNone(rootkey)
|
||||||
|
self.assertEqual(rootkey.username, "root")
|
||||||
|
self.assertEqual(rootkey.key, "ssh-rsa")
|
||||||
|
|
||||||
|
widget = self._find_user(ks, "widget")
|
||||||
|
self.assertIsNotNone(widget)
|
||||||
|
self.assertEqual(widget.name, "widget")
|
||||||
|
self.assertEqual(widget.homedir, "/srv/widget/")
|
||||||
|
self.assertEqual(widget.shell, "/usr/bin/false")
|
||||||
|
self.assertEqual(set(widget.groups), {"dialout", "users"})
|
||||||
|
|
||||||
|
admin = self._find_user(ks, "admin")
|
||||||
|
self.assertIsNotNone(admin)
|
||||||
|
self.assertEqual(admin.name, "admin")
|
||||||
|
self.assertEqual(admin.password, "")
|
||||||
|
self.assertEqual(admin.homedir, "/srv/widget/")
|
||||||
|
self.assertEqual(admin.shell, "/usr/bin/bash")
|
||||||
|
self.assertEqual(set(admin.groups), {"widget", "users", "students"})
|
||||||
|
self.assertEqual(admin.uid, 1200)
|
||||||
|
|
||||||
|
plain = self._find_user(ks, "plain")
|
||||||
|
self.assertIsNotNone(plain)
|
||||||
|
self.assertEqual(plain.name, "plain")
|
||||||
|
self.assertEqual(plain.password, "password")
|
||||||
|
|
||||||
|
# widget does not appear as a separate group line, since a widget
|
||||||
|
# group is created for the widget user
|
||||||
|
widgetGroup = self._find_group(ks, "widget")
|
||||||
|
self.assertIsNone(widgetGroup)
|
||||||
|
|
||||||
|
studentsGroup = self._find_group(ks, "students")
|
||||||
|
self.assertIsNotNone(studentsGroup)
|
||||||
|
self.assertEqual(studentsGroup.name, "students")
|
||||||
|
Loading…
Reference in New Issue
Block a user