forked from ungleich-public/cdist
		
	Merge pull request #663 from asteven/type/__line
RFC: rewrite __line type for --before and --after support
This commit is contained in:
		
				commit
				
					
						f75af95bda
					
				
			
		
					 5 changed files with 228 additions and 116 deletions
				
			
		|  | @ -1,6 +1,6 @@ | ||||||
| #!/bin/sh | #!/bin/sh -e | ||||||
| # | # | ||||||
| # 2012-2013 Nico Schottelius (nico-cdist at schottelius.org) | # 2018 Steven Armstrong (steven-cdist at armstrong.cc) | ||||||
| # | # | ||||||
| # This file is part of cdist. | # This file is part of cdist. | ||||||
| # | # | ||||||
|  | @ -17,26 +17,74 @@ | ||||||
| # You should have received a copy of the GNU General Public License | # You should have received a copy of the GNU General Public License | ||||||
| # along with cdist. If not, see <http://www.gnu.org/licenses/>. | # along with cdist. If not, see <http://www.gnu.org/licenses/>. | ||||||
| # | # | ||||||
| # |  | ||||||
| 
 | 
 | ||||||
| file="/$__object_id" | if [ -f "$__object/parameter/before" ]; then | ||||||
| [ -f "$__object/parameter/file" ] && file=$(cat "$__object/parameter/file") |    position="before" | ||||||
|  | elif [ -f "$__object/parameter/after" ]; then | ||||||
|  |    position="after" | ||||||
|  | else | ||||||
|  |    # By default we append to the end of the file. | ||||||
|  |    position="end" | ||||||
|  | fi | ||||||
| 
 | 
 | ||||||
| if [ -f "$__object/parameter/regex" ]; then | if [ -f "$__object/parameter/regex" ]; then | ||||||
|     regex=$(cat "$__object/parameter/regex") |    needle="regex" | ||||||
|     greparg="" |  | ||||||
| else | else | ||||||
|     if [ ! -f "$__object/parameter/line" ]; then |    needle="line" | ||||||
|         echo "Parameter line and regex missing - cannot explore" >&2 |  | ||||||
|         exit 1 |  | ||||||
|     fi |  | ||||||
|     regex="$(cat "$__object/parameter/line")" |  | ||||||
|     greparg="-F -x" |  | ||||||
| fi | fi | ||||||
| 
 | 
 | ||||||
| # Allow missing file - thus 2>/dev/null | if [ -f "$__object/parameter/file" ]; then | ||||||
| if grep -q $greparg -- "$regex" "$file" 2>/dev/null; then |    file="$(cat "$__object/parameter/file")" | ||||||
|     echo present |  | ||||||
| else | else | ||||||
|     echo absent |    file="/$__object_id" | ||||||
| fi | fi | ||||||
|  | 
 | ||||||
|  | awk -v position="$position" -v needle="$needle" ' | ||||||
|  | function _find(_text, _pattern) { | ||||||
|  |    if (needle == "regex") { | ||||||
|  |       return match(_text, _pattern) | ||||||
|  |    } else { | ||||||
|  |       return index(_text, _pattern) | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | BEGIN { | ||||||
|  |    getline anchor < (ENVIRON["__object"] "/parameter/" position) | ||||||
|  |    getline pattern < (ENVIRON["__object"] "/parameter/" needle) | ||||||
|  |    state = "absent" | ||||||
|  | } | ||||||
|  | { | ||||||
|  |    if (position == "after") { | ||||||
|  |       if (match($0, anchor)) { | ||||||
|  |          getline | ||||||
|  |          if (_find($0, pattern)) { | ||||||
|  |             state = "present" | ||||||
|  |          } | ||||||
|  |          else { | ||||||
|  |             state = "wrongposition" | ||||||
|  |          } | ||||||
|  |          exit 0 | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  |    else if (position == "before") { | ||||||
|  |       if (_find($0, pattern)) { | ||||||
|  |          getline | ||||||
|  |          if (match($0, anchor)) { | ||||||
|  |             state = "present" | ||||||
|  |          } | ||||||
|  |          else { | ||||||
|  |             state = "wrongposition" | ||||||
|  |          } | ||||||
|  |          exit 0 | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  |    else { | ||||||
|  |       if (_find($0, pattern)) { | ||||||
|  |          state = "present" | ||||||
|  |          exit 0 | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | END { | ||||||
|  |    print state | ||||||
|  | } | ||||||
|  | ' "$file" | ||||||
|  |  | ||||||
|  | @ -1,7 +1,6 @@ | ||||||
| #!/bin/sh -e | #!/bin/sh -e | ||||||
| # | # | ||||||
| # 2012 Nico Schottelius (nico-cdist at schottelius.org) | # 2018 Steven Armstrong (steven-cdist at armstrong.cc) | ||||||
| # 2014 Steven Armstrong (steven-cdist at armstrong.cc) |  | ||||||
| # | # | ||||||
| # This file is part of cdist. | # This file is part of cdist. | ||||||
| # | # | ||||||
|  | @ -18,76 +17,108 @@ | ||||||
| # You should have received a copy of the GNU General Public License | # You should have received a copy of the GNU General Public License | ||||||
| # along with cdist. If not, see <http://www.gnu.org/licenses/>. | # along with cdist. If not, see <http://www.gnu.org/licenses/>. | ||||||
| # | # | ||||||
| # |  | ||||||
| 
 | 
 | ||||||
| file="/$__object_id" | if [ -f "$__object/parameter/before" -a -f "$__object/parameter/after" ]; then | ||||||
| regex="" |    echo "Use either --before OR --after but not both." >&2 | ||||||
| state_should="present" |    exit 1 | ||||||
| [ -f "$__object/parameter/file" ]  && file=$(cat "$__object/parameter/file") | fi | ||||||
| [ -f "$__object/parameter/regex" ] && regex=$(cat "$__object/parameter/regex") |  | ||||||
| [ -f "$__object/parameter/state" ] && state_should=$(cat "$__object/parameter/state") |  | ||||||
| [ -f "$__object/parameter/line" ]  && line=$(cat "$__object/parameter/line") |  | ||||||
| 
 | 
 | ||||||
|  | state_should="$(cat "$__object/parameter/state")" | ||||||
| state_is="$(cat "$__object/explorer/state")" | state_is="$(cat "$__object/explorer/state")" | ||||||
| 
 | 
 | ||||||
| [ "$state_should" = "$state_is" ] && exit 0 | if [ "$state_should" = "$state_is" ]; then | ||||||
|  |    # nothing to do | ||||||
|  |    exit 0 | ||||||
|  | fi | ||||||
| 
 | 
 | ||||||
|  | if [ -f "$__object/parameter/before" ]; then | ||||||
|  |    position="before" | ||||||
|  | elif [ -f "$__object/parameter/after" ]; then | ||||||
|  |    position="after" | ||||||
|  | else | ||||||
|  |    # By default we append to the end of the file. | ||||||
|  |    position="end" | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | if [ -f "$__object/parameter/regex" ]; then | ||||||
|  |    needle="regex" | ||||||
|  | else | ||||||
|  |    needle="line" | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | if [ -f "$__object/parameter/file" ]; then | ||||||
|  |    file="$(cat "$__object/parameter/file")" | ||||||
|  | else | ||||||
|  |    file="/$__object_id" | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | add=0 | ||||||
|  | remove=0 | ||||||
| case "$state_should" in | case "$state_should" in | ||||||
|    present) |    present) | ||||||
|         if [ ! "$line" ]; then |       if [ "$state_is" = "wrongposition" ]; then | ||||||
|             echo "Required parameter \"line\" is missing" >&2 |          echo updated >> "$__messages_out" | ||||||
|             exit 1 |          remove=1 | ||||||
|  |       else | ||||||
|  |          echo added >> "$__messages_out" | ||||||
|       fi |       fi | ||||||
| 
 |       add=1 | ||||||
|         #echo "echo \"$line\" >> $file" |  | ||||||
|         #line_sanitised=$(cat "$__object/parameter/line" | sed 's/"/\"/g') |  | ||||||
|         # Idea: replace ' in the string: |  | ||||||
|         # '"'"' |  | ||||||
|         # |------> ': end the string |  | ||||||
|         #  |-|---> "'": create ' in the output string |  | ||||||
|         #     |--> ': continue the string |  | ||||||
|         # |  | ||||||
|         # Replace all \ so \t and other combinations are not interpreted |  | ||||||
|         # |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|         # line_sanitised=$(cat "$__object/parameter/line" | sed -e "s/'/'\"'\"'/g" -e 's/\\/\\\\/g') |  | ||||||
|         # The one above does not work: |  | ||||||
|         #     --line "PS1='[\t] \[\033[1m\]\h\[\033[0m\]:\w\\$ '" |  | ||||||
|         # becomes |  | ||||||
|         # PS1='[\\t] \\[\\033[1m\\]\\h\\[\\033[0m\\]:\\w\\$ ' |  | ||||||
| 
 |  | ||||||
|         # Only replace ' with '"'"' and keep \ as they are |  | ||||||
|         line_sanitised=$(cat "$__object/parameter/line" | sed -e "s/'/'\"'\"'/g") |  | ||||||
|         printf '%s' "printf '%s\n' '$line_sanitised' >> $file" |  | ||||||
|         echo "added" >> "$__messages_out" |  | ||||||
| 
 |  | ||||||
|    ;; |    ;; | ||||||
|    absent) |    absent) | ||||||
|         if [ "$regex" -a "$line" ]; then |       echo removed >> "$__messages_out" | ||||||
|             echo "Mutally exclusive parameters regex and line given for state absent" >&2 |       remove=1 | ||||||
|             exit 1 |    ;; | ||||||
|         fi | esac | ||||||
| 
 | 
 | ||||||
|         greparg="" | cat << DONE | ||||||
|         if [ "$line" ]; then |  | ||||||
|             regex="$line" |  | ||||||
|             greparg="-F -x" |  | ||||||
|         fi |  | ||||||
| 
 |  | ||||||
|         cat << eof |  | ||||||
| tmpfile=\$(mktemp ${file}.cdist.XXXXXXXXXX) | tmpfile=\$(mktemp ${file}.cdist.XXXXXXXXXX) | ||||||
| # preserve ownership and permissions of existing file | # preserve ownership and permissions of existing file | ||||||
| if [ -f "$file" ]; then | if [ -f "$file" ]; then | ||||||
|    cp -p "$file" "\$tmpfile" |    cp -p "$file" "\$tmpfile" | ||||||
| fi | fi | ||||||
| grep -v $greparg "$regex" '$file' > \$tmpfile || true | 
 | ||||||
|  | awk -v position="$position" -v needle="$needle" -v remove=$remove -v add=$add ' | ||||||
|  | function _find(_text, _pattern) { | ||||||
|  |    if (needle == "regex") { | ||||||
|  |       return match(_text, _pattern) | ||||||
|  |    } else { | ||||||
|  |       return index(_text, _pattern) | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | BEGIN { | ||||||
|  |    line_file = ENVIRON["__object"] "/parameter/line" | ||||||
|  |    getline line < line_file | ||||||
|  |    # Need to close line file as it may be re-read as pattern below. | ||||||
|  |    close(line_file) | ||||||
|  |    getline pattern < (ENVIRON["__object"] "/parameter/" needle) | ||||||
|  |    getline anchor < (ENVIRON["__object"] "/parameter/" position) | ||||||
|  | } | ||||||
|  | { | ||||||
|  |    if (remove) { | ||||||
|  |       if (_find(\$0, pattern)) { | ||||||
|  |          # skip over this line -> remove it | ||||||
|  |          next | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  |    if (add) { | ||||||
|  |       if (anchor && match(\$0, anchor)) { | ||||||
|  |          if (position == "before") { | ||||||
|  |             print line | ||||||
|  |             print | ||||||
|  |          } else if (position == "after") { | ||||||
|  |             print | ||||||
|  |             print line | ||||||
|  |          } | ||||||
|  |          next | ||||||
|  |       } | ||||||
|  |    } | ||||||
|  |    print | ||||||
|  | } | ||||||
|  | END { | ||||||
|  |    if (add && position == "end") { | ||||||
|  |       print line | ||||||
|  |    } | ||||||
|  | } | ||||||
|  | ' "$file" > "\$tmpfile" | ||||||
| mv -f "\$tmpfile" "$file" | mv -f "\$tmpfile" "$file" | ||||||
| eof | DONE | ||||||
|         echo "removed" >> "$__messages_out" |  | ||||||
|     ;; |  | ||||||
|     *) |  | ||||||
|         echo "Unknown state: $state_should" >&2 |  | ||||||
|         exit 1 |  | ||||||
|     ;; |  | ||||||
| esac |  | ||||||
|  |  | ||||||
|  | @ -13,72 +13,102 @@ This cdist type allows you to add lines and remove lines from files. | ||||||
| 
 | 
 | ||||||
| REQUIRED PARAMETERS | REQUIRED PARAMETERS | ||||||
| ------------------- | ------------------- | ||||||
|  | None. | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| OPTIONAL PARAMETERS | OPTIONAL PARAMETERS | ||||||
| ------------------- | ------------------- | ||||||
| state | after | ||||||
|     'present' or 'absent', defaults to 'present' |     Insert the given line after this pattern. | ||||||
| 
 | 
 | ||||||
| line | before | ||||||
|     Specifies the line which should be absent or present |     Insert the given line before this pattern. | ||||||
| 
 |  | ||||||
|     Must be present, if state is present. |  | ||||||
|     Must not be combined with regex, if state is absent. |  | ||||||
| 
 |  | ||||||
| regex |  | ||||||
|     If state is present, search for this pattern and add |  | ||||||
|     given line, if the given regular expression does not match. |  | ||||||
| 
 |  | ||||||
|     In case of absent, ensure all lines matching the |  | ||||||
|     regular expression are absent. |  | ||||||
| 
 |  | ||||||
|     The regular expression is interpreted by grep. |  | ||||||
| 
 |  | ||||||
|     Must not be combined with line, if state is absent. |  | ||||||
| 
 | 
 | ||||||
| file | file | ||||||
|     If supplied, use this as the destination file. |     If supplied, use this as the destination file. | ||||||
|     Otherwise the object_id is used. |     Otherwise the object_id is used. | ||||||
| 
 | 
 | ||||||
|  | line | ||||||
|  |     Specifies the line which should be absent or present. | ||||||
|  | 
 | ||||||
|  |     Must be present, if state is 'present'. | ||||||
|  |     Ignored if regex is given and state is 'absent'. | ||||||
|  | 
 | ||||||
|  | regex | ||||||
|  |     If state is 'present', search for this pattern and if it matches add | ||||||
|  |     the given line. | ||||||
|  | 
 | ||||||
|  |     If state is 'absent', ensure all lines matching the regular expression | ||||||
|  |     are absent. | ||||||
|  | 
 | ||||||
|  |     The regular expression is interpreted by awk's match function. | ||||||
|  | 
 | ||||||
|  | state | ||||||
|  |     'present' or 'absent', defaults to 'present' | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | BOOLEAN PARAMETERS | ||||||
|  | ------------------ | ||||||
|  | None. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| MESSAGES | MESSAGES | ||||||
| -------- | -------- | ||||||
| added | added | ||||||
|     The line was added. |     The line was added. | ||||||
| 
 | 
 | ||||||
|  | updated | ||||||
|  |     The line or its position was changed. | ||||||
|  | 
 | ||||||
| removed | removed | ||||||
|     The line was removed. |     The line was removed. | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| EXAMPLES | EXAMPLES | ||||||
| -------- | -------- | ||||||
| 
 | 
 | ||||||
| .. code-block:: sh | .. code-block:: sh | ||||||
| 
 | 
 | ||||||
|     # Manage the DAEMONS line in rc.conf |     # Manage a hosts entry for www.example.com. | ||||||
|     __line daemons --file /etc/rc.conf --line 'DAEMONS=(hwclock !network sshd crond postfix)' |     __line /etc/hosts \ | ||||||
|  |         --line '127.0.0.2 www.example.com' | ||||||
| 
 | 
 | ||||||
|     # Ensure the home mount is present in /etc/fstab - explicitly make it present |     # Manage another hosts entry for test.example.com. | ||||||
|     __line home-fstab \ |     __line hosts:test.example.com \ | ||||||
|         --file /etc/fstab \ |         --file /etc/hosts \ | ||||||
|         --line 'filer.fs:/vol/home /home  nfs    defaults        0 0' \ |         --line '127.0.0.3 test.example.com' | ||||||
|         --state present |  | ||||||
| 
 | 
 | ||||||
|     # Removes the line specifiend in "include_www" from the file "lighttpd.conf" |     # Remove the line starting with TIMEZONE from the /etc/rc.conf file. | ||||||
|     __line legacy_timezone --file /etc/rc.conf --regex 'TIMEZONE=.*' --state absent |     __line legacy_timezone \ | ||||||
|  |        --file /etc/rc.conf \ | ||||||
|  |        --regex 'TIMEZONE=.*' \ | ||||||
|  |        --state absent | ||||||
|  | 
 | ||||||
|  |     # Insert a line before another one. | ||||||
|  |     __line password-auth-local:classify \ | ||||||
|  |         --file /etc/pam.d/password-auth-local \ | ||||||
|  |         --line '-session required pam_exec.so debug log=/tmp/classify.log /usr/local/libexec/classify' \ | ||||||
|  |         --before '^session[[:space:]]+include[[:space:]]+password-auth-ac$' | ||||||
|  | 
 | ||||||
|  |     # Insert a line after another one. | ||||||
|  |     __line password-auth-local:classify \ | ||||||
|  |         --file /etc/pam.d/password-auth-local \ | ||||||
|  |         --line '-session required pam_exec.so debug log=/tmp/classify.log /usr/local/libexec/classify' \ | ||||||
|  |         --after '^session[[:space:]]+include[[:space:]]+password-auth-ac$' | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| SEE ALSO | SEE ALSO | ||||||
| -------- | -------- | ||||||
| :strong:`grep`\ (1) | :strong:`cdist-type`\ (7) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| AUTHORS | AUTHORS | ||||||
| ------- | ------- | ||||||
| Nico Schottelius <nico-cdist--@--schottelius.org> | Steven Armstrong <steven-cdist--@--armstrong.cc> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| COPYING | COPYING | ||||||
| ------- | ------- | ||||||
| Copyright \(C) 2012-2013 Nico Schottelius. You can redistribute it | Copyright \(C) 2018 Steven Armstrong. Free use of this software is | ||||||
| and/or modify it under the terms of the GNU General Public License as | granted under the terms of the GNU General Public License version 3 (GPLv3). | ||||||
| published by the Free Software Foundation, either version 3 of the |  | ||||||
| License, or (at your option) any later version. |  | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								cdist/conf/type/__line/parameter/default/state
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								cdist/conf/type/__line/parameter/default/state
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | present | ||||||
|  | @ -1,4 +1,6 @@ | ||||||
| state | after | ||||||
| regex | before | ||||||
| file | file | ||||||
| line | line | ||||||
|  | regex | ||||||
|  | state | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue