Merge ~tagatac/cloud-init:docs into cloud-init:master

Proposed by David Tagatac
Status: Merged
Merged at revision: d690216b67ab6f4cf0aaaba78abd1aa3da4e38df
Proposed branch: ~tagatac/cloud-init:docs
Merge into: cloud-init:master
Diff against target: 416 lines (+199/-202)
2 files modified
dev/null (+0/-201)
doc/rtd/topics/merging.rst (+199/-1)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
cloud-init Commiters Pending
Review via email: mp+322239@code.launchpad.net

Commit message

doc: correct grammar and improve clarity in merging.rst

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
David Tagatac (tagatac) wrote :

A more useful diff:
$ git --no-pager diff master:doc/merging.rst doc/rtd/topics/merging.rst
diff --git a/doc/rtd/topics/merging.rst b/doc/rtd/topics/merging.rst
index bf49b909..2f927a47 100644
--- a/doc/rtd/topics/merging.rst
+++ b/doc/rtd/topics/merging.rst
@@ -1,3 +1,7 @@
+**************************
+Merging User-Data Sections
+**************************
+
 Overview
 ========

@@ -8,9 +12,9 @@ performing an #include).

 Since previously the merging algorithm was very simple and would only overwrite
 and not append lists, or strings, and so on it was decided to create a new and
-improved way to merge dictionaries (and there contained objects) together in a
+improved way to merge dictionaries (and their contained objects) together in a
 way that is customizable, thus allowing for users who provide cloud-config
-user-data to determine exactly how there objects will be merged.
+user-data to determine exactly how their objects will be merged.

 For example.

@@ -26,7 +30,7 @@ For example.
      - bash3
      - bash4

-The previous way of merging the following 2 objects would result in a final
+The previous way of merging the two objects above would result in a final
 cloud-config object that contains the following.

 .. code-block:: yaml
@@ -36,7 +40,7 @@ cloud-config object that contains the following.
      - bash3
      - bash4

-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

@@ -48,18 +52,17 @@ Typically this is not what users want, instead they would likely prefer:
      - bash4

 This way makes it easier to combine the various cloud-config objects you have
-into a more useful list, thus reducing duplication that would have had to
-occur in the previous method to accomplish the same result.
+into a more useful list, thus reducing duplication necessary to accomplish the
+same result with the previous method.

 Customizability
 ===============

-Since the above merging algorithm may not always be the desired merging
-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
-a new concept call 'merge classes'.
+Because the above merging algorithm may not always be desired (just as the
+previous merging algorithm was not always the preferred one), the concept of
+customized merging was introduced through 'merge classes'.

-A merge class is a class defintion which provides functions that can be used
+A merge class is a class definition which provides functions that can be used
 to merge a given type with another given type.

 An example of one of these merging classes is the following:
@@ -156,8 +159,8 @@ following:
 Dictionary format
 -----------------

-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.
+A dictionary can be used when it specifies the same information as the
+string format (i.e. the second option above), for example:

 .. code-block:: python

Revision history for this message
Scott Moser (smoser) wrote :

Why did you move the file ?

Revision history for this message
David Tagatac (tagatac) wrote :

I moved the file to match the directory structure of the rest of the topics. merging.rst was the only file that sourced its content from another file, and I didn't see any advantage to the modularity. Does the move break something that I'm missing? I couldn't find the dev flow for building the docs.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/doc/merging.rst b/doc/merging.rst
2deleted file mode 100644
3index bf49b90..0000000
4--- a/doc/merging.rst
5+++ /dev/null
6@@ -1,201 +0,0 @@
7-Overview
8-========
9-
10-This was implemented because it has been a common feature request that there be
11-a way to specify how cloud-config yaml "dictionaries" provided as user-data are
12-merged together when there are multiple yamls to merge together (say when
13-performing an #include).
14-
15-Since previously the merging algorithm was very simple and would only overwrite
16-and not append lists, or strings, and so on it was decided to create a new and
17-improved way to merge dictionaries (and there contained objects) together in a
18-way that is customizable, thus allowing for users who provide cloud-config
19-user-data to determine exactly how there objects will be merged.
20-
21-For example.
22-
23-.. code-block:: yaml
24-
25- #cloud-config (1)
26- run_cmd:
27- - bash1
28- - bash2
29-
30- #cloud-config (2)
31- run_cmd:
32- - bash3
33- - bash4
34-
35-The previous way of merging the following 2 objects would result in a final
36-cloud-config object that contains the following.
37-
38-.. code-block:: yaml
39-
40- #cloud-config (merged)
41- run_cmd:
42- - bash3
43- - bash4
44-
45-Typically this is not what users want, instead they would likely prefer:
46-
47-.. code-block:: yaml
48-
49- #cloud-config (merged)
50- run_cmd:
51- - bash1
52- - bash2
53- - bash3
54- - bash4
55-
56-This way makes it easier to combine the various cloud-config objects you have
57-into a more useful list, thus reducing duplication that would have had to
58-occur in the previous method to accomplish the same result.
59-
60-Customizability
61-===============
62-
63-Since the above merging algorithm may not always be the desired merging
64-algorithm (like how the previous merging algorithm was not always the preferred
65-one) the concept of customizing how merging can be done was introduced through
66-a new concept call 'merge classes'.
67-
68-A merge class is a class defintion which provides functions that can be used
69-to merge a given type with another given type.
70-
71-An example of one of these merging classes is the following:
72-
73-.. code-block:: python
74-
75- class Merger(object):
76- def __init__(self, merger, opts):
77- self._merger = merger
78- self._overwrite = 'overwrite' in opts
79-
80- # This merging algorithm will attempt to merge with
81- # another dictionary, on encountering any other type of object
82- # it will not merge with said object, but will instead return
83- # the original value
84- #
85- # On encountering a dictionary, it will create a new dictionary
86- # composed of the original and the one to merge with, if 'overwrite'
87- # is enabled then keys that exist in the original will be overwritten
88- # by keys in the one to merge with (and associated values). Otherwise
89- # if not in overwrite mode the 2 conflicting keys themselves will
90- # be merged.
91- def _on_dict(self, value, merge_with):
92- if not isinstance(merge_with, (dict)):
93- return value
94- merged = dict(value)
95- for (k, v) in merge_with.items():
96- if k in merged:
97- if not self._overwrite:
98- merged[k] = self._merger.merge(merged[k], v)
99- else:
100- merged[k] = v
101- else:
102- merged[k] = v
103- return merged
104-
105-As you can see there is a '_on_dict' method here that will be given a source
106-value and a value to merge with. The result will be the merged object. This
107-code itself is called by another merging class which 'directs' the merging to
108-happen by analyzing the types of the objects to merge and attempting to find a
109-know object that will merge that type. I will avoid pasting that here, but it
110-can be found in the `mergers/__init__.py` file (see `LookupMerger` and
111-`UnknownMerger`).
112-
113-So following the typical cloud-init way of allowing source code to be
114-downloaded and used dynamically, it is possible for users to inject there own
115-merging files to handle specific types of merging as they choose (the basic
116-ones included will handle lists, dicts, and strings). Note how each merge can
117-have options associated with it which affect how the merging is performed, for
118-example a dictionary merger can be told to overwrite instead of attempt to
119-merge, or a string merger can be told to append strings instead of discarding
120-other strings to merge with.
121-
122-How to activate
123-===============
124-
125-There are a few ways to activate the merging algorithms, and to customize them
126-for your own usage.
127-
128-1. The first way involves the usage of MIME messages in cloud-init to specify
129- multipart documents (this is one way in which multiple cloud-config is
130- joined together into a single cloud-config). Two new headers are looked
131- for, both of which can define the way merging is done (the first header to
132- exist wins). These new headers (in lookup order) are 'Merge-Type' and
133- 'X-Merge-Type'. The value should be a string which will satisfy the new
134- merging format defintion (see below for this format).
135-
136-2. The second way is actually specifying the merge-type in the body of the
137- cloud-config dictionary. There are 2 ways to specify this, either as a
138- string or as a dictionary (see format below). The keys that are looked up
139- for this definition are the following (in order), 'merge_how',
140- 'merge_type'.
141-
142-String format
143--------------
144-
145-The string format that is expected is the following.
146-
147-::
148-
149- classname1(option1,option2)+classname2(option3,option4)....
150-
151-The class name there will be connected to class names used when looking for the
152-class that can be used to merge and options provided will be given to the class
153-on construction of that class.
154-
155-For example, the default string that is used when none is provided is the
156-following:
157-
158-::
159-
160- list()+dict()+str()
161-
162-Dictionary format
163------------------
164-
165-In cases where a dictionary can be used to specify the same information as the
166-string format (ie option #2 of above) it can be used, for example.
167-
168-.. code-block:: python
169-
170- {'merge_how': [{'name': 'list', 'settings': ['extend']},
171- {'name': 'dict', 'settings': []},
172- {'name': 'str', 'settings': ['append']}]}
173-
174-This would be the equivalent format for default string format but in dictionary
175-form instead of string form.
176-
177-Specifying multiple types and its effect
178-========================================
179-
180-Now you may be asking yourself, if I specify a merge-type header or dictionary
181-for every cloud-config that I provide, what exactly happens?
182-
183-The answer is that when merging, a stack of 'merging classes' is kept, the
184-first one on that stack is the default merging classes, this set of mergers
185-will be used when the first cloud-config is merged with the initial empty
186-cloud-config dictionary. If the cloud-config that was just merged provided a
187-set of merging classes (via the above formats) then those merging classes will
188-be pushed onto the stack. Now if there is a second cloud-config to be merged
189-then the merging classes from the cloud-config before the first will be used
190-(not the default) and so on. This way a cloud-config can decide how it will
191-merge with a cloud-config dictionary coming after it.
192-
193-Other uses
194-==========
195-
196-In addition to being used for merging user-data sections, the default merging
197-algorithm for merging 'conf.d' yaml files (which form an initial yaml config
198-for cloud-init) was also changed to use this mechanism so its full
199-benefits (and customization) can also be used there as well. Other places that
200-used the previous merging are also, similarly, now extensible (metadata
201-merging, for example).
202-
203-Note, however, that merge algorithms are not used *across* types of
204-configuration. As was the case before merging was implemented,
205-user-data will overwrite conf.d configuration without merging.
206-
207-.. vi: textwidth=78
208diff --git a/doc/rtd/topics/merging.rst b/doc/rtd/topics/merging.rst
209index eca118f..2f927a4 100644
210--- a/doc/rtd/topics/merging.rst
211+++ b/doc/rtd/topics/merging.rst
212@@ -2,5 +2,203 @@
213 Merging User-Data Sections
214 **************************
215
216-.. include:: ../../merging.rst
217+Overview
218+========
219+
220+This was implemented because it has been a common feature request that there be
221+a way to specify how cloud-config yaml "dictionaries" provided as user-data are
222+merged together when there are multiple yamls to merge together (say when
223+performing an #include).
224+
225+Since previously the merging algorithm was very simple and would only overwrite
226+and not append lists, or strings, and so on it was decided to create a new and
227+improved way to merge dictionaries (and their contained objects) together in a
228+way that is customizable, thus allowing for users who provide cloud-config
229+user-data to determine exactly how their objects will be merged.
230+
231+For example.
232+
233+.. code-block:: yaml
234+
235+ #cloud-config (1)
236+ run_cmd:
237+ - bash1
238+ - bash2
239+
240+ #cloud-config (2)
241+ run_cmd:
242+ - bash3
243+ - bash4
244+
245+The previous way of merging the two objects above would result in a final
246+cloud-config object that contains the following.
247+
248+.. code-block:: yaml
249+
250+ #cloud-config (merged)
251+ run_cmd:
252+ - bash3
253+ - bash4
254+
255+Typically this is not what users want; instead they would likely prefer:
256+
257+.. code-block:: yaml
258+
259+ #cloud-config (merged)
260+ run_cmd:
261+ - bash1
262+ - bash2
263+ - bash3
264+ - bash4
265+
266+This way makes it easier to combine the various cloud-config objects you have
267+into a more useful list, thus reducing duplication necessary to accomplish the
268+same result with the previous method.
269+
270+Customizability
271+===============
272+
273+Because the above merging algorithm may not always be desired (just as the
274+previous merging algorithm was not always the preferred one), the concept of
275+customized merging was introduced through 'merge classes'.
276+
277+A merge class is a class definition which provides functions that can be used
278+to merge a given type with another given type.
279+
280+An example of one of these merging classes is the following:
281+
282+.. code-block:: python
283+
284+ class Merger(object):
285+ def __init__(self, merger, opts):
286+ self._merger = merger
287+ self._overwrite = 'overwrite' in opts
288+
289+ # This merging algorithm will attempt to merge with
290+ # another dictionary, on encountering any other type of object
291+ # it will not merge with said object, but will instead return
292+ # the original value
293+ #
294+ # On encountering a dictionary, it will create a new dictionary
295+ # composed of the original and the one to merge with, if 'overwrite'
296+ # is enabled then keys that exist in the original will be overwritten
297+ # by keys in the one to merge with (and associated values). Otherwise
298+ # if not in overwrite mode the 2 conflicting keys themselves will
299+ # be merged.
300+ def _on_dict(self, value, merge_with):
301+ if not isinstance(merge_with, (dict)):
302+ return value
303+ merged = dict(value)
304+ for (k, v) in merge_with.items():
305+ if k in merged:
306+ if not self._overwrite:
307+ merged[k] = self._merger.merge(merged[k], v)
308+ else:
309+ merged[k] = v
310+ else:
311+ merged[k] = v
312+ return merged
313+
314+As you can see there is a '_on_dict' method here that will be given a source
315+value and a value to merge with. The result will be the merged object. This
316+code itself is called by another merging class which 'directs' the merging to
317+happen by analyzing the types of the objects to merge and attempting to find a
318+know object that will merge that type. I will avoid pasting that here, but it
319+can be found in the `mergers/__init__.py` file (see `LookupMerger` and
320+`UnknownMerger`).
321+
322+So following the typical cloud-init way of allowing source code to be
323+downloaded and used dynamically, it is possible for users to inject there own
324+merging files to handle specific types of merging as they choose (the basic
325+ones included will handle lists, dicts, and strings). Note how each merge can
326+have options associated with it which affect how the merging is performed, for
327+example a dictionary merger can be told to overwrite instead of attempt to
328+merge, or a string merger can be told to append strings instead of discarding
329+other strings to merge with.
330+
331+How to activate
332+===============
333+
334+There are a few ways to activate the merging algorithms, and to customize them
335+for your own usage.
336+
337+1. The first way involves the usage of MIME messages in cloud-init to specify
338+ multipart documents (this is one way in which multiple cloud-config is
339+ joined together into a single cloud-config). Two new headers are looked
340+ for, both of which can define the way merging is done (the first header to
341+ exist wins). These new headers (in lookup order) are 'Merge-Type' and
342+ 'X-Merge-Type'. The value should be a string which will satisfy the new
343+ merging format defintion (see below for this format).
344+
345+2. The second way is actually specifying the merge-type in the body of the
346+ cloud-config dictionary. There are 2 ways to specify this, either as a
347+ string or as a dictionary (see format below). The keys that are looked up
348+ for this definition are the following (in order), 'merge_how',
349+ 'merge_type'.
350+
351+String format
352+-------------
353+
354+The string format that is expected is the following.
355+
356+::
357+
358+ classname1(option1,option2)+classname2(option3,option4)....
359+
360+The class name there will be connected to class names used when looking for the
361+class that can be used to merge and options provided will be given to the class
362+on construction of that class.
363+
364+For example, the default string that is used when none is provided is the
365+following:
366+
367+::
368+
369+ list()+dict()+str()
370+
371+Dictionary format
372+-----------------
373+
374+A dictionary can be used when it specifies the same information as the
375+string format (i.e. the second option above), for example:
376+
377+.. code-block:: python
378+
379+ {'merge_how': [{'name': 'list', 'settings': ['extend']},
380+ {'name': 'dict', 'settings': []},
381+ {'name': 'str', 'settings': ['append']}]}
382+
383+This would be the equivalent format for default string format but in dictionary
384+form instead of string form.
385+
386+Specifying multiple types and its effect
387+========================================
388+
389+Now you may be asking yourself, if I specify a merge-type header or dictionary
390+for every cloud-config that I provide, what exactly happens?
391+
392+The answer is that when merging, a stack of 'merging classes' is kept, the
393+first one on that stack is the default merging classes, this set of mergers
394+will be used when the first cloud-config is merged with the initial empty
395+cloud-config dictionary. If the cloud-config that was just merged provided a
396+set of merging classes (via the above formats) then those merging classes will
397+be pushed onto the stack. Now if there is a second cloud-config to be merged
398+then the merging classes from the cloud-config before the first will be used
399+(not the default) and so on. This way a cloud-config can decide how it will
400+merge with a cloud-config dictionary coming after it.
401+
402+Other uses
403+==========
404+
405+In addition to being used for merging user-data sections, the default merging
406+algorithm for merging 'conf.d' yaml files (which form an initial yaml config
407+for cloud-init) was also changed to use this mechanism so its full
408+benefits (and customization) can also be used there as well. Other places that
409+used the previous merging are also, similarly, now extensible (metadata
410+merging, for example).
411+
412+Note, however, that merge algorithms are not used *across* types of
413+configuration. As was the case before merging was implemented,
414+user-data will overwrite conf.d configuration without merging.
415+
416 .. vi: textwidth=78

Subscribers

People subscribed via source and target branches