Enable the merging.txt to be in .rst format for public viewing
This commit is contained in:
parent
c4a5f6a8ec
commit
99d90f764d
@ -1,6 +1,3 @@
|
|||||||
Arriving in 0.7.2 is a new way to handle dictionary merging in cloud-init.
|
|
||||||
---
|
|
||||||
|
|
||||||
Overview
|
Overview
|
||||||
--------
|
--------
|
||||||
|
|
||||||
@ -16,6 +13,8 @@ to determine exactly how there objects will be merged.
|
|||||||
|
|
||||||
For example.
|
For example.
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
#cloud-config (1)
|
#cloud-config (1)
|
||||||
run_cmd:
|
run_cmd:
|
||||||
- bash1
|
- bash1
|
||||||
@ -29,6 +28,8 @@ run_cmd:
|
|||||||
The previous way of merging the following 2 objects would result in a final
|
The previous way of merging the following 2 objects would result in a final
|
||||||
cloud-config object that contains the following.
|
cloud-config object that contains the following.
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
#cloud-config (merged)
|
#cloud-config (merged)
|
||||||
run_cmd:
|
run_cmd:
|
||||||
- bash3
|
- bash3
|
||||||
@ -36,6 +37,8 @@ run_cmd:
|
|||||||
|
|
||||||
Typically this is not what users want, instead they would likely prefer:
|
Typically this is not what users want, instead they would likely prefer:
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
#cloud-config (merged)
|
#cloud-config (merged)
|
||||||
run_cmd:
|
run_cmd:
|
||||||
- bash1
|
- bash1
|
||||||
@ -51,7 +54,7 @@ Customizability
|
|||||||
---------------
|
---------------
|
||||||
|
|
||||||
Since the above merging algorithm may not always be the desired merging
|
Since the above merging algorithm may not always be the desired merging
|
||||||
algorithm (like how the merging algorithm in < 0.7.2 was not always the preferred
|
algorithm (like how the previous merging algorithm was not always the preferred
|
||||||
one) the concept of customizing how merging can be done was introduced through
|
one) the concept of customizing how merging can be done was introduced through
|
||||||
a new concept call 'merge classes'.
|
a new concept call 'merge classes'.
|
||||||
|
|
||||||
@ -60,6 +63,8 @@ to merge a given type with another given type.
|
|||||||
|
|
||||||
An example of one of these merging classes is the following:
|
An example of one of these merging classes is the following:
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
class Merger(object):
|
class Merger(object):
|
||||||
def __init__(self, merger, opts):
|
def __init__(self, merger, opts):
|
||||||
self._merger = merger
|
self._merger = merger
|
||||||
@ -95,7 +100,7 @@ and a value to merge with. The result will be the merged object. This code itsel
|
|||||||
is called by another merging class which 'directs' the merging to happen by
|
is called by another merging class which 'directs' the merging to happen by
|
||||||
analyzing the types of the objects to merge and attempting to find a know object
|
analyzing the types of the objects to merge and attempting to find a know object
|
||||||
that will merge that type. I will avoid pasting that here, but it can be found
|
that will merge that type. I will avoid pasting that here, but it can be found
|
||||||
in the mergers/__init__.py file (see LookupMerger and UnknownMerger).
|
in the `mergers/__init__.py` file (see `LookupMerger` and `UnknownMerger`).
|
||||||
|
|
||||||
So following the typical cloud-init way of allowing source code to be downloaded
|
So following the typical cloud-init way of allowing source code to be downloaded
|
||||||
and used dynamically, it is possible for users to inject there own merging files
|
and used dynamically, it is possible for users to inject there own merging files
|
||||||
@ -123,11 +128,14 @@ for your own usage.
|
|||||||
or as a dictionary (see format below). The keys that are looked up for this
|
or as a dictionary (see format below). The keys that are looked up for this
|
||||||
definition are the following (in order), 'merge_how', 'merge_type'.
|
definition are the following (in order), 'merge_how', 'merge_type'.
|
||||||
|
|
||||||
*String format*
|
String format
|
||||||
|
********
|
||||||
|
|
||||||
The string format that is expected is the following.
|
The string format that is expected is the following.
|
||||||
|
|
||||||
"classname(option1,option2)+classname2(option3,option4)" (and so on)
|
::
|
||||||
|
|
||||||
|
classname1(option1,option2)+classname2(option3,option4)....
|
||||||
|
|
||||||
The class name there will be connected to class names used when looking for the
|
The class name there will be connected to class names used when looking for the
|
||||||
class that can be used to merge and options provided will be given to the class
|
class that can be used to merge and options provided will be given to the class
|
||||||
@ -135,20 +143,21 @@ on construction of that class.
|
|||||||
|
|
||||||
For example, the default string that is used when none is provided is the following:
|
For example, the default string that is used when none is provided is the following:
|
||||||
|
|
||||||
"list(extend)+dict()+str(append)"
|
::
|
||||||
|
|
||||||
*Dictionary format*
|
list(extend)+dict()+str(append)
|
||||||
|
|
||||||
|
Dictionary format
|
||||||
|
********
|
||||||
|
|
||||||
In cases where a dictionary can be used to specify the same information as the
|
In cases where a dictionary can be used to specify the same information as the
|
||||||
string format (ie option #2 of above) it can be used, for example.
|
string format (ie option #2 of above) it can be used, for example.
|
||||||
|
|
||||||
merge_how:
|
.. code-block:: python
|
||||||
- name: list
|
|
||||||
settings: [extend]
|
{'merge_how': [{'name': 'list', 'settings': ['extend']},
|
||||||
- name: dict
|
{'name': 'dict', 'settings': []},
|
||||||
settings: []
|
{'name': 'str', 'settings': ['append']}]}
|
||||||
- name: str
|
|
||||||
settings: [append]
|
|
||||||
|
|
||||||
This would be the equivalent format for default string format but in dictionary
|
This would be the equivalent format for default string format but in dictionary
|
||||||
form instead of string form.
|
form instead of string form.
|
||||||
@ -172,7 +181,7 @@ cloud-config dictionary coming after it.
|
|||||||
Other uses
|
Other uses
|
||||||
----------
|
----------
|
||||||
|
|
||||||
The default merging algorithm for merging conf.d yaml files (which form a initial
|
The default merging algorithm for merging 'conf.d' yaml files (which form a initial
|
||||||
yaml config for cloud-init) was also changed to use this mechanism so its full
|
yaml config for cloud-init) was also changed to use this mechanism so its full
|
||||||
benefits (and customization) can also be used there as well. Other places that
|
benefits (and customization) can also be used there as well. Other places that
|
||||||
used the previous merging are also similar now extensible (metadata merging for
|
used the previous merging are also similar now extensible (metadata merging for
|
@ -24,6 +24,7 @@ Summary
|
|||||||
topics/examples
|
topics/examples
|
||||||
topics/datasources
|
topics/datasources
|
||||||
topics/modules
|
topics/modules
|
||||||
|
topics/merging
|
||||||
topics/moreinfo
|
topics/moreinfo
|
||||||
topics/hacking
|
topics/hacking
|
||||||
|
|
||||||
|
5
doc/rtd/topics/merging.rst
Normal file
5
doc/rtd/topics/merging.rst
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
=========
|
||||||
|
Merging
|
||||||
|
=========
|
||||||
|
|
||||||
|
.. include:: ../../merging.rst
|
@ -175,6 +175,7 @@ class FilesystemMockingTestCase(ResourceUsingTestCase):
|
|||||||
def patchOS(self, new_root):
|
def patchOS(self, new_root):
|
||||||
patch_funcs = {
|
patch_funcs = {
|
||||||
os.path: ['isfile', 'exists', 'islink', 'isdir'],
|
os.path: ['isfile', 'exists', 'islink', 'isdir'],
|
||||||
|
os: ['listdir'],
|
||||||
}
|
}
|
||||||
for (mod, funcs) in patch_funcs.items():
|
for (mod, funcs) in patch_funcs.items():
|
||||||
for f in funcs:
|
for f in funcs:
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
"""Tests of the built-in user data handlers."""
|
"""Tests of the built-in user data handlers."""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import unittest
|
|
||||||
|
|
||||||
from mocker import MockerTestCase
|
from tests.unittests import helpers as test_helpers
|
||||||
|
|
||||||
from cloudinit import handlers
|
from cloudinit import handlers
|
||||||
from cloudinit import helpers
|
from cloudinit import helpers
|
||||||
@ -14,7 +13,7 @@ from cloudinit.handlers import upstart_job
|
|||||||
from cloudinit.settings import (PER_ALWAYS, PER_INSTANCE)
|
from cloudinit.settings import (PER_ALWAYS, PER_INSTANCE)
|
||||||
|
|
||||||
|
|
||||||
class TestBuiltins(MockerTestCase):
|
class TestBuiltins(test_helpers.FilesystemMockingTestCase):
|
||||||
|
|
||||||
def test_upstart_frequency_no_out(self):
|
def test_upstart_frequency_no_out(self):
|
||||||
c_root = self.makeDir()
|
c_root = self.makeDir()
|
||||||
@ -35,17 +34,21 @@ class TestBuiltins(MockerTestCase):
|
|||||||
None, None, None)
|
None, None, None)
|
||||||
self.assertEquals(0, len(os.listdir(up_root)))
|
self.assertEquals(0, len(os.listdir(up_root)))
|
||||||
|
|
||||||
@unittest.skip("until LP: #1124384 fixed")
|
|
||||||
def test_upstart_frequency_single(self):
|
def test_upstart_frequency_single(self):
|
||||||
# files should be written out when frequency is ! per-instance
|
# files should be written out when frequency is ! per-instance
|
||||||
c_root = self.makeDir()
|
new_root = self.makeDir()
|
||||||
up_root = self.makeDir()
|
|
||||||
paths = helpers.Paths({
|
|
||||||
'cloud_dir': c_root,
|
|
||||||
'upstart_dir': up_root,
|
|
||||||
})
|
|
||||||
freq = PER_INSTANCE
|
freq = PER_INSTANCE
|
||||||
|
|
||||||
|
self.patchOS(new_root)
|
||||||
|
self.patchUtils(new_root)
|
||||||
|
paths = helpers.Paths({
|
||||||
|
'upstart_dir': "/etc/upstart",
|
||||||
|
})
|
||||||
|
|
||||||
|
util.ensure_dir("/run")
|
||||||
|
util.ensure_dir("/etc/upstart")
|
||||||
|
util.write_file("/run/cloud-init-upstart-reload", 'test')
|
||||||
|
|
||||||
mock_subp = self.mocker.replace(util.subp, passthrough=False)
|
mock_subp = self.mocker.replace(util.subp, passthrough=False)
|
||||||
mock_subp(["initctl", "reload-configuration"], capture=False)
|
mock_subp(["initctl", "reload-configuration"], capture=False)
|
||||||
self.mocker.replay()
|
self.mocker.replay()
|
||||||
@ -57,4 +60,5 @@ class TestBuiltins(MockerTestCase):
|
|||||||
'test.conf', 'blah', freq)
|
'test.conf', 'blah', freq)
|
||||||
h.handle_part('', handlers.CONTENT_END,
|
h.handle_part('', handlers.CONTENT_END,
|
||||||
None, None, None)
|
None, None, None)
|
||||||
self.assertEquals(1, len(os.listdir(up_root)))
|
|
||||||
|
self.assertEquals(1, len(os.listdir('/etc/upstart')))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user