Merge branch 'master' into feature_init_process

Conflicts:
	doc/changelog
	doc/dev/todo/niconext
	doc/man/cdist-reference.text.sh
	lib/cdist/core/explorer.py

doc/man/cdist-reference.text.sh documents better reachability of
variables - also suitable for master?

Signed-off-by: Nico Schottelius <nico@brief.schottelius.org>
This commit is contained in:
Nico Schottelius 2012-02-14 13:53:16 +01:00
commit 2b380b7dc1
55 changed files with 921 additions and 360 deletions

17
README
View file

@ -50,10 +50,11 @@ UNIX, simplicity, familar environment | cdist is configured in POSIX shell
### Documentation ### Documentation
The cdist documentation is included as manpages in the distribution. The cdist documentation is included as manpages in the distribution.
You can browse the documentation online as well:
* You can [browse the documentation of the latest version online](man) as well. * [latest version](man/latest)
* Have a look at the [given speeches](speeches) * [all versions (>= 2.0.4)](man)
### OS support ### OS support
@ -88,7 +89,7 @@ cdist was tested or is know to run on at least
## Installation ## Installation
### Preperation ### Preparation
Ensure you have Python 3.2 installed on the machine you use to **deploy to the targets** Ensure you have Python 3.2 installed on the machine you use to **deploy to the targets**
(the ***source host***). (the ***source host***).
@ -135,7 +136,13 @@ If you want to ensure nothing breaks you must set back the python version to wha
#### Max OS X #### Max OS X
Ensure you have port installed and configured (http://www.macports.org/install.php). You can choose between Homebrew and Macports, either way works:
[Homebrew](http://mxcl.github.com/homebrew/) variant:
brew install python3
[Macports](http://www.macports.org/install.php) variant:
port install python32 port install python32
ln -s /opt/local/bin/python3.2 /opt/local/bin/python3 ln -s /opt/local/bin/python3.2 /opt/local/bin/python3

9
build
View file

@ -85,7 +85,7 @@ case "$1" in
;; ;;
release) release)
"$0" clean && "$0" man && "$0" web ./doc/dev/releasechecklist
;; ;;
speeches) speeches)
@ -99,7 +99,7 @@ case "$1" in
webmain) webmain)
cp README ${WEBPAGE} cp README ${WEBPAGE}
cd ${WEBDIR} && git commit -m "cdist update" ${WEBPAGE} cd ${WEBDIR} && git commit -m "cdist main update" ${WEBPAGE}
cd ${WEBDIR} && make pub cd ${WEBDIR} && make pub
;; ;;
@ -119,6 +119,11 @@ case "$1" in
cd ${WEBDIR} && git add ${WEBBASE} cd ${WEBDIR} && git add ${WEBBASE}
cd ${WEBDIR} && git commit -m "cdist update" ${WEBBASE} ${WEBPAGE} cd ${WEBDIR} && git commit -m "cdist update" ${WEBBASE} ${WEBPAGE}
cd ${WEBDIR} && make pub cd ${WEBDIR} && make pub
# Fix ikiwiki, which does not like symlinks for pseudo security
ssh tee.schottelius.org \
"cd /home/services/www/nico/www.nico.schottelius.org/www/software/cdist/man &&
rm -f latest && ln -sf "$version" latest"
;; ;;
p|pu|pub) p|pu|pub)

View file

@ -18,13 +18,14 @@
# along with cdist. If not, see <http://www.gnu.org/licenses/>. # along with cdist. If not, see <http://www.gnu.org/licenses/>.
# #
# #
# All os variables are lower case # All os variables are lower case. Keep this file in alphabetical
# # order by os variable except in cases where order otherwise matters,
# in which case keep the primary os and its derivatives together in
# a block (see Debian and Redhat examples below).
# #
# Ubuntu is also Debian, thus return if Ubuntu was found if grep -q ^Amazon /etc/system-release 2>/dev/null; then
if grep -q ^DISTRIB_ID=Ubuntu /etc/lsb-release 2>/dev/null; then echo amazon
echo ubuntu
exit 0 exit 0
fi fi
@ -33,45 +34,52 @@ if [ -f /etc/arch-release ]; then
exit 0 exit 0
fi fi
if [ -f /etc/cdist-preos ]; then
echo cdist-preos
exit 0
fi
### Debian and derivatives
if grep -q ^DISTRIB_ID=Ubuntu /etc/lsb-release 2>/dev/null; then
echo ubuntu
exit 0
fi
if [ -f /etc/debian_version ]; then if [ -f /etc/debian_version ]; then
echo debian echo debian
exit 0 exit 0
fi fi
###
if [ -f /etc/gentoo-release ]; then if [ -f /etc/gentoo-release ]; then
echo gentoo echo gentoo
exit 0 exit 0
fi fi
# Fedora is also Redhat, thus return before redhat!
if grep -q ^Fedora /etc/redhat-release 2>/dev/null; then
echo fedora
exit 0
fi
# CentOS is also based on Redhat, thus return before redhat!
if grep -q ^CentOS /etc/redhat-release 2>/dev/null; then
echo centos
exit 0
fi
if [ -f /etc/redhat-release ]; then
echo redhat
exit 0
fi
if [ -f /etc/SuSE-release ]; then
echo suse
exit 0
fi
if [ -f /etc/owl-release ]; then if [ -f /etc/owl-release ]; then
echo owl echo owl
exit 0 exit 0
fi fi
if [ -f /etc/cdist-preos ]; then ### Redhat and derivatives
echo cdist-preos if grep -q ^CentOS /etc/redhat-release 2>/dev/null; then
echo centos
exit 0
fi
if grep -q ^Fedora /etc/redhat-release 2>/dev/null; then
echo fedora
exit 0
fi
if [ -f /etc/redhat-release ]; then
echo redhat
exit 0
fi
###
if [ -f /etc/SuSE-release ]; then
echo suse
exit 0 exit 0
fi fi

View file

@ -23,6 +23,9 @@
# #
case "$($__explorer/os)" in case "$($__explorer/os)" in
amazon)
cat /etc/system-release
;;
archlinux) archlinux)
# empty, but well... # empty, but well...
cat /etc/arch-release cat /etc/arch-release

View file

@ -34,11 +34,6 @@ case "$state_should" in
fi fi
fi fi
# Mode settings
if [ -f "$__object/parameter/mode" ]; then
echo chmod \"$(cat "$__object/parameter/mode")\" \"$destination\"
fi
# Group # Group
if [ -f "$__object/parameter/group" ]; then if [ -f "$__object/parameter/group" ]; then
echo chgrp \"$(cat "$__object/parameter/group")\" \"$destination\" echo chgrp \"$(cat "$__object/parameter/group")\" \"$destination\"
@ -48,6 +43,12 @@ case "$state_should" in
if [ -f "$__object/parameter/owner" ]; then if [ -f "$__object/parameter/owner" ]; then
echo chown \"$(cat "$__object/parameter/owner")\" \"$destination\" echo chown \"$(cat "$__object/parameter/owner")\" \"$destination\"
fi fi
# Mode - needs to happen last as a chown/chgrp can alter mode by
# clearing S_ISUID and S_ISGID bits (see chown(2))
if [ -f "$__object/parameter/mode" ]; then
echo chmod \"$(cat "$__object/parameter/mode")\" \"$destination\"
fi
;; ;;
absent) absent)

View file

@ -29,7 +29,7 @@ if grep -q "^${name}:" "$__object/explorer/group"; then
for property in $(ls .); do for property in $(ls .); do
new_value="$(cat "$property")" new_value="$(cat "$property")"
case "$key" in case "$property" in
password) password)
current_value="$(awk -F: '{ print $2 }' < "$__object/explorer/gshadow")" current_value="$(awk -F: '{ print $2 }' < "$__object/explorer/gshadow")"
;; ;;

View file

@ -0,0 +1,53 @@
#!/bin/sh
#
# 2011 Steven Armstrong (steven-cdist at armstrong.cc)
#
# This file is part of cdist.
#
# cdist is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# cdist is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see <http://www.gnu.org/licenses/>.
#
key="$(cat "$__object/parameter/key" 2>/dev/null \
|| echo "$__object_id")"
state="$(cat "$__object/parameter/state" 2>/dev/null \
|| echo "present")"
file="$(cat "$__object/parameter/file")"
delimiter="$(cat "$__object/parameter/delimiter")"
value="$(cat "$__object/parameter/value" 2>/dev/null \
|| echo "__CDIST_NOTSET__")"
case "$state" in
absent)
if grep -q -E "^$key$delimiter+" "$file"; then
# if the key exists, with whatever value, we will have to remove it
# so report it as present
echo present
else
# key does not exist
echo absent
fi
;;
present)
if grep -q -E "^$key$delimiter+$value$" "$file"; then
# key exists and value is same
echo present
elif grep -q -E "^$key$delimiter+" "$file"; then
# key exists, but value is empty or different
echo wrongvalue
else
# key does not exist
echo absent
fi
;;
esac

View file

@ -18,35 +18,40 @@
# along with cdist. If not, see <http://www.gnu.org/licenses/>. # along with cdist. If not, see <http://www.gnu.org/licenses/>.
# #
value_is="$(cat "$__object/explorer/value")"
value_should="$(cat "$__object/parameter/value")"
key="$(cat "$__object/parameter/key")" key="$(cat "$__object/parameter/key")"
file="$(cat "$__object/parameter/file")" file="$(cat "$__object/parameter/file")"
delimiter="$(cat "$__object/parameter/delimiter")" delimiter="$(cat "$__object/parameter/delimiter")"
value="$(cat "$__object/parameter/value")"
if [ "$value_is" != "$value_should" ]; then state_is="$(cat "$__object/explorer/state")"
case "$value_is" in state_should="$(cat "$__object/parameter/state")"
__NOTSET__)
# add key and value
echo "echo \"${key}${delimiter}${value_should}\" >> \"$file\""
;;
*)
if [ "$value_should" = '__NOTSET__' ]; then
# remove key and value
cat << DONE
sed -i '/^${key}/d' "$file"
DONE
else
# change value
cat << DONE
awk -F "$delimiter" '
/${key}${delimiter}*/{gsub("$value_is", "$value_should")};{print}' "$file" > "${file}+" \
&& mv "${file}+" "$file"
DONE if [ "$state_is" = "$state_should" ]; then
fi # nothing to do
;; exit 0
esac
fi fi
case "$state_should" in
absent)
# remove lines starting with key
echo "sed -i '/^$key\($delimiter\+\)/d' \"$file\""
;;
present)
case "$state_is" in
absent)
# add new key and value
echo "echo \"${key}${delimiter}${value}\" >> \"$file\""
;;
wrongvalue)
# change exisiting value
echo "sed -i \"s|^$key\($delimiter\+\).*|$key\1$value|\" \"$file\""
;;
*)
echo "Unknown explorer state: $state_is" >&2
exit 1
esac
;;
*)
echo "Unknown state: $state_should" >&2
exit 1
esac

View file

@ -16,9 +16,6 @@ file.
REQUIRED PARAMETERS REQUIRED PARAMETERS
------------------- -------------------
value::
The value for the key. Setting the value to `__NOTSET__` will remove the key
from the file.
file:: file::
The file to operate on. The file to operate on.
delimiter:: delimiter::
@ -27,8 +24,13 @@ delimiter::
OPTIONAL PARAMETERS OPTIONAL PARAMETERS
------------------- -------------------
state::
present or absent, defaults to present. If present, sets the key to value,
if absent, removes the key from the file.
key:: key::
The key to change. Defaults to object_id. The key to change. Defaults to object_id.
value::
The value for the key. Optional if state=absent, required otherwise.
EXAMPLES EXAMPLES
@ -45,6 +47,9 @@ __key_value my-fancy-id --file /etc/login.defs --key SYS_UID_MAX --value 666 \
# Enable packet forwarding # Enable packet forwarding
__key_value net.ipv4.ip_forward --file /etc/sysctl.conf --value 1 \ __key_value net.ipv4.ip_forward --file /etc/sysctl.conf --value 1 \
--delimiter '=' --delimiter '='
# Remove existing key/value
__key_value LEGACY_KEY --file /etc/somefile --state absent --delimiter '='
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View file

@ -18,9 +18,13 @@
# along with cdist. If not, see <http://www.gnu.org/licenses/>. # along with cdist. If not, see <http://www.gnu.org/licenses/>.
# #
if [ -f "$__object/parameter/key" ]; then # set defaults
key="$(cat "$__object/parameter/key")" key="$(cat "$__object/parameter/key" 2>/dev/null \
else || echo "$__object_id" | tee "$__object/parameter/key")"
echo "$__object_id" > "$__object/parameter/key" state="$(cat "$__object/parameter/state" 2>/dev/null \
fi || echo "present" | tee "$__object/parameter/state")"
if [ "$state" = "present" -a ! -f "$__object/parameter/value" ]; then
echo "Missing required parameter 'value'" >&2
exit 1
fi

View file

@ -1 +1,3 @@
key key
value
state

View file

@ -1,3 +1,2 @@
value
file file
delimiter delimiter

62
conf/type/__link/explorer/state Executable file
View file

@ -0,0 +1,62 @@
#!/bin/sh
#
# 2012 Steven Armstrong (steven-cdist at armstrong.cc)
#
# This file is part of cdist.
#
# cdist is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# cdist is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see <http://www.gnu.org/licenses/>.
#
destination="/$__object_id"
type="$(cat "$__object/parameter/type")"
source="$(cat "$__object/parameter/source")"
# no destination? -> state is absent
if [ ! -e "$destination" ]; then
echo absent
exit 0
fi
destination_dir="${destination%/*}"
case "$type" in
symbolic)
cd "$destination_dir"
source_is=$(ls -l "$destination" | sed 's/.*-> //g')
if [ -h "$destination" -a "$source_is" = "$source" ]; then
echo present
else
echo absent
fi
;;
hard)
cd "$destination_dir"
# check source relative to destination_dir
if [ ! -e "$source" ]; then
echo sourcemissing
exit 0
fi
destination_inode=$(ls -i "$destination" | awk '{print $1}')
source_inode=$(ls -i "$source" | awk '{print $1}')
if [ "$destination_inode" -eq "$source_inode" ]; then
echo present
else
echo absent
fi
;;
*)
echo "Unknown type: $type" >&2
exit 1
;;
esac

View file

@ -39,7 +39,14 @@ case "$type" in
;; ;;
esac esac
state_is="$(cat "$__object/explorer/state")"
state_should="$(cat "$__object/parameter/state")" state_should="$(cat "$__object/parameter/state")"
if [ "$state_should" = "$state_is" ]; then
# nothing to do
exit 0
fi
case "$state_should" in case "$state_should" in
present) present)
echo ln ${lnopt} -f \"$source\" \"$destination\" echo ln ${lnopt} -f \"$source\" \"$destination\"

View file

@ -33,7 +33,7 @@ else
archlinux) type="pacman" ;; archlinux) type="pacman" ;;
debian|ubuntu) type="apt" ;; debian|ubuntu) type="apt" ;;
gentoo) type="emerge" ;; gentoo) type="emerge" ;;
fedora|redhat|centos) type="yum" ;; amazon|centos|fedora|redhat) type="yum" ;;
*) *)
echo "Don't know how to manage packages on: $os" >&2 echo "Don't know how to manage packages on: $os" >&2
exit 1 exit 1

View file

@ -0,0 +1,49 @@
#!/bin/sh
#
# 2012 Nico Schottelius (nico-cdist at schottelius.org)
#
# This file is part of cdist.
#
# cdist is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# cdist is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see <http://www.gnu.org/licenses/>.
#
#
# Development supported by Local AG (www.local.ch)
#
nameparam="$__object/parameter/name"
if [ -f "$nameparam" ]; then
name=$(cat "$nameparam")
else
name="$__object_id"
fi
pipparam="$__object/parameter/pip"
if [ -f "$pipparam" ]; then
pip=$(cat "$pipparam")
else
pip="pip"
fi
# If there is no pip, it may get created from somebody else.
# If it will be created, there is probably no package installed.
if ! command -v "$pip" >/dev/null 2>&1; then
echo absent
else
if "$pip" freeze | grep -i -q "^$name=="; then
echo present
else
echo absent
fi
fi

View file

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
# #
# 2011 Steven Armstrong (steven-cdist at armstrong.cc) # 2012 Nico Schottelius (nico-cdist at schottelius.org)
# #
# This file is part of cdist. # This file is part of cdist.
# #
@ -18,20 +18,33 @@
# along with cdist. If not, see <http://www.gnu.org/licenses/>. # along with cdist. If not, see <http://www.gnu.org/licenses/>.
# #
# #
# Get the current value of key or __NOTSET__ if the key doesn't exist. # Development supported by Local AG (www.local.ch)
# #
if [ -f "$__object/parameter/key" ]; then state_is=$(cat "$__object/explorer/state")
key="$(cat "$__object/parameter/key")" state_should=$(cat "$__object/parameter/state")
[ "$state_is" = "$state_should" ] && exit 0
nameparam="$__object/parameter/name"
if [ -f "$nameparam" ]; then
name=$(cat "$nameparam")
else else
key="$__object_id" name="$__object_id"
fi fi
file="$(cat "$__object/parameter/file")"
delimiter="$(cat "$__object/parameter/delimiter")"
awk -F "$delimiter" ' pipparam="$__object/parameter/pip"
BEGIN { found=0 } if [ -f "$pipparam" ]; then
/^'$key'/ { print $2; found=1 } pip=$(cat "$pipparam")
END { if (found) exit 0; else exit 1 }' "$file" \ else
|| echo "__NOTSET__" pip="pip"
fi
case "$state_should" in
present)
echo $pip install -q pyro
;;
absent)
echo $pip uninstall -q -y pyro
;;
esac

View file

@ -0,0 +1,53 @@
cdist-type__package_pip(7)
==========================
Nico Schottelius <nico-cdist--@--schottelius.org>
NAME
----
cdist-type__package_pip - Manage packages with pip
DESCRIPTION
-----------
Pip is used in Python environments to install packages.
It is also included in the python virtualenv environment.
REQUIRED PARAMETERS
-------------------
state::
Either "present" or "absent".
OPTIONAL PARAMETERS
-------------------
name::
If supplied, use the name and not the object id as the package name.
pip::
Instead of using pip from PATH, use the specific pip path.
EXAMPLES
--------
--------------------------------------------------------------------------------
# Install a package
__package_pip pyro --state present
# Use pip in a virtualenv located at /root/shinken_virtualenv
__package_pip pyro --state present --pip /root/shinken_virtualenv/bin/pip
--------------------------------------------------------------------------------
SEE ALSO
--------
- cdist-type(7)
- cdist-type__package(7)
COPYING
-------
Copyright \(C) 2012 Nico Schottelius. Free use of this software is
granted under the terms of the GNU General Public License version 3 (GPLv3).

View file

@ -0,0 +1 @@
pip

View file

@ -0,0 +1 @@
state

View file

@ -29,7 +29,7 @@ fi
state="$(cat "$__object/parameter/state")" state="$(cat "$__object/parameter/state")"
if grep -q -E "(centos|redhat)" "$__global/explorer/os"; then if grep -q -E "(centos|redhat|amazon)" "$__global/explorer/os"; then
opts="-y --quiet" opts="-y --quiet"
else else
opts="--assumeyes --quiet" opts="--assumeyes --quiet"

View file

@ -28,6 +28,7 @@ cd "$__object/parameter"
if grep -q "^${name}:" "$__object/explorer/passwd"; then if grep -q "^${name}:" "$__object/explorer/passwd"; then
for property in $(ls .); do for property in $(ls .); do
new_value="$(cat "$property")" new_value="$(cat "$property")"
unset current_value
file="$__object/explorer/passwd" file="$__object/explorer/passwd"
@ -36,13 +37,20 @@ if grep -q "^${name}:" "$__object/explorer/passwd"; then
if $(echo "$new_value" | grep -q '^[0-9][0-9]*$'); then if $(echo "$new_value" | grep -q '^[0-9][0-9]*$'); then
field=4 field=4
else else
# group name # We were passed a group name. Compare the gid in
file="$__object/explorer/group" # the user's /etc/passwd entry with the gid of the
field=1 # group returned by the group explorer.
gid_from_group=$(awk -F: '{ print $3 }' "$__object/explorer/group")
gid_from_passwd=$(awk -F: '{ print $4 }' "$file")
if [ "$gid_from_group" != "$gid_from_passwd" ]; then
current_value="$gid_from_passwd"
else
current_value="$new_value"
fi
fi fi
;; ;;
password) password)
field=3 field=2
file="$__object/explorer/shadow" file="$__object/explorer/shadow"
;; ;;
comment) field=5 ;; comment) field=5 ;;
@ -51,8 +59,12 @@ if grep -q "^${name}:" "$__object/explorer/passwd"; then
uid) field=3 ;; uid) field=3 ;;
esac esac
export field # If we haven't already set $current_value above, pull it from the
current_value="$(awk -F: '{ print $ENVIRON["field"] }' < "$file")" # appropriate file/field.
if [ -z "$current_value" ]; then
export field
current_value="$(awk -F: '{ print $ENVIRON["field"] }' < "$file")"
fi
if [ "$new_value" != "$current_value" ]; then if [ "$new_value" != "$current_value" ]; then
set -- "$@" "--$property" \'$new_value\' set -- "$@" "--$property" \'$new_value\'

View file

@ -1,15 +1,37 @@
2.0.6: Changelog
---------
* Changes are always commented with their author in (braces)
* Exception: No braces means author == Nico Schottelius
2.0.8:
* Cleanup: Better hint to source of error
* Cleanup: Remove support for __debug variable in manifests (Type != Core
debugging)
2.0.7: 2012-02-13
* Bugfix __file: Use chmod after chown/chgrp (Matt Coddington)
* Bugfix __user: Correct shadow field in explorer (Matt Coddington)
* Bugfix __link: Properly handle existing links (Steven Armstrong)
* Bugfix __key_value: More robust implementation (Steven Armstrong)
* Bugfix __user: Fix for changing a user's group by name (Matt Coddington)
* New Type: __package_pip
* Bugfix/Cleanup: Correctly allow Object ID to start and end with /, but
not contain //.
2.0.6: 2012-01-28
* Bugfix __apt_ppa: * Bugfix __apt_ppa:
Also remove the [ppa-name].list file, if empty. (Tim Kersten) Also remove the [ppa-name].list file, if empty. (Tim Kersten)
* Bugfix __group:
Referenced wrong variable name (Matt Coddington)
* Feature __package_apt: * Feature __package_apt:
Initial support for virtual packages (Evax Software) Initial support for virtual packages (Evax Software)
* Feature Core: Added new dependency resolver (Steven Armstrong)
* Feature Explorer, __package_yum: Support Amazon Linux (Matt Coddington)
* New Type: __rvm (Evax Software) * New Type: __rvm (Evax Software)
* New Type: __rvm_gem (Evax Software) * New Type: __rvm_gem (Evax Software)
* New Type: __rvm_gemset (Evax Software) * New Type: __rvm_gemset (Evax Software)
* New Type: __rvm_ruby (Evax Software) * New Type: __rvm_ruby (Evax Software)
* New Explorer: runlevel
* Documentation: Update of reference (environment variables)
* Feature core: Added new dependency resolver (Steven Armstrong)
2.0.5: 2012-01-18 2.0.5: 2012-01-18
* Bugfix __key_value: Use correct delimiters * Bugfix __key_value: Use correct delimiters

View file

@ -0,0 +1,36 @@
If a type explorer depends on a command that will be generated by another type,
the operation fails, as can be seen below.
This may be a corner case, but is hapenning with __package_pip and
__python_virtualenv.
[19:10] brief:cdist% ./bin/cdist config -v loch
INFO: loch: Running global explorers
INFO: loch: Running initial manifest /home/users/nico/privat/firmen/local.ch/vcs/cdist/conf/manifest
INFO: loch: Running object manifests and type explorers
INFO: loch: Running manifest and explorers for __git/root/shinken
INFO: loch: Running manifest and explorers for __package_pip/pyro
/var/lib/cdist/conf/type/__package_pip/explorer/state: line 38: /root/shinken_virtualenv/bin/pip: No such file or directory
INFO: loch: Running manifest and explorers for __python_virtualenv/root/shinken_virtualenv
INFO: loch: Running manifest and explorers for __directory/pyro
INFO: loch: Running manifest and explorers for __directory/root/shinken
INFO: loch: Running manifest and explorers for __directory/root/shinken_virtualenv
INFO: loch: Running manifest and explorers for __package/git
INFO: loch: Running manifest and explorers for __package/python-virtualenv
INFO: loch: Running manifest and explorers for __package_pacman/git
INFO: loch: Running manifest and explorers for __package_pacman/python-virtualenv
INFO: loch: Generating and executing code
INFO: loch: Generating and executing code for __package_pacman/git
INFO: loch: Generating and executing code for __package/git
INFO: loch: Generating and executing code for __directory/root/shinken
INFO: loch: Generating and executing code for __git/root/shinken
fatal: write error: No space left on device
fatal: index-pack failed
ERROR: loch: Code that raised the error:
git clone --quiet "git://github.com/naparuba/shinken.git" "/root/shinken"
ERROR: Remote script execution failed: ssh -o User=root -q loch /bin/sh -e /var/lib/cdist/object/__git/root/shinken/.cdist/code-remote
WARNING: Failed to deploy to the following hosts: loch
INFO: Total processing time for 1 host(s): 340.62370681762695
[19:17] brief:cdist% ./bin/cdist config -v loch

View file

@ -0,0 +1,18 @@
__typename /foo/bar # possible, usual use case
require="__a//b" __typename /foo/bar # possible and happens often for __a/$id in loops
__typename /foo/bar/ # trailing slash will be stripped, can be documented
__typename /foo//bar//baz # // will be converted to / implicitly through fs; error prone; disallow
require="__a//b//c" __typename # // will be converted to / implicitly through fs; error prone; disallow
Solution:
1) allow require __a//b: type __a, object id /b
=> strip first slash of object id, as we do in non-dep-mode
2) allow _one_ trailing /: __type /foo/bar/ and require="__foo/abc/"
=> strip one leading slash of object id
3) disallow // within object id
4) disallow starting or ending / after 1) and 2)

View file

@ -0,0 +1,23 @@
possible dependencies:
- unix pattern __foo/*
- object: __foo//bar, __foo/bar
- singleton with object_id: __foo/singleton
- singleton without object_id: __foo/
solving dependencies:
solve_dep(object, run_list):
- list = [me]
- if status == IN_DEPENDENCY:
fail: circular dependency
- status = IN_DEPENDENCY
- create_list_of_deps(object)
- try pattern expansion
- for each dependency:
if object does not exist:
fail
else:
list.append(solve_dep(object, run_list)):
- status == IN_LIST
- return [me, dependencies [, dependencies of dependencies]]

View file

@ -11,15 +11,25 @@ echo "Testing documentation..."
./build clean && ./build man || exit 1 ./build clean && ./build man || exit 1
# get version # get version
changelog_version=$(head -n1 doc/changelog | sed 's/:.*//') changelog_version=$(grep '^[[:digit:]]' doc/changelog | head -n1 | sed 's/:.*//')
#git_version=$(git describe) #git_version=$(git describe)
lib_version=$(grep ^VERSION lib/cdist/__init__.py | sed -e 's/.*= //' -e 's/"//g') lib_version=$(grep ^VERSION lib/cdist/__init__.py | sed -e 's/.*= //' -e 's/"//g')
# get date
date_today="$(date +%Y-%m-%d)"
date_changelog=$(grep '^[[:digit:]]' doc/changelog | head -n1 | sed 's/.*: //')
echo "Ensure you fixed/prepared version files: $files" echo "Ensure you fixed/prepared version files: $files"
echo "changelog: $changelog_version" echo "changelog: $changelog_version"
#echo "git: $git_version" #echo "git: $git_version"
echo "lib: $lib_version" echo "lib: $lib_version"
if [ "$date_today" != "$date_changelog" ]; then
echo "Messed up date, not releasing:"
echo "Changelog: $date_changelog"
exit 1
fi
if [ "$lib_version" != "$changelog_version" ]; then if [ "$lib_version" != "$changelog_version" ]; then
echo "Messed up versions, not releasing" echo "Messed up versions, not releasing"
exit 1 exit 1

View file

@ -34,7 +34,6 @@ USER INTERFACE
TYPES TYPES
------ ------
- Add testing framework (proposed by Evax Software)
- __user - __user
add option to include --create-home add option to include --create-home
- ensure that all types, which support --state support
present and absent (consistent look and feel)

View file

@ -1,9 +1,17 @@
- __init_script? 2.0.8 features / cleanups:
to enable/disable startup of init stuff
http://linuxhelp.blogspot.com/2006/04/enabling-and-disabling-services-during_01.html - cleanup object_id handling
- have a look at singletons
- remove useless
ERROR: monitoring02: Code that raised the error:
- ensure that all types, which support --state support
present and absent (consistent look and feel)
--------------------------------------------------------------------------------
- update/create docs - update/create docs
- document __remote_copy and __remote_exec
- cdist-cache:: - cdist-cache::
How to get use information about the hosts we have been working on [advanced] How to get use information about the hosts we have been working on [advanced]
- cdist-scaling-tuning:: - cdist-scaling-tuning::
@ -17,3 +25,4 @@
- exec flag is not true for manifest anymore - exec flag is not true for manifest anymore
- SSH HINTS - ssh agent - SSH HINTS - ssh agent

View file

@ -49,10 +49,10 @@ The following global explorers are available:
eof eof
( (
cd ../../conf/explorer cd ../../conf/explorer
for explorer in *; do for explorer in *; do
echo "- $explorer" echo "- $explorer"
done done
) )
cat << eof cat << eof
@ -62,77 +62,77 @@ PATHS
If not specified otherwise, all paths are relative to the checkout directory. If not specified otherwise, all paths are relative to the checkout directory.
conf/:: conf/::
Contains the (static) configuration like manifests, types and explorers. Contains the (static) configuration like manifests, types and explorers.
conf/manifest/init:: conf/manifest/init::
This is the central entry point used by cdist-manifest-init(1). This is the central entry point used by cdist-manifest-init(1).
It is an executable (+x bit set) shell script that can use It is an executable (+x bit set) shell script that can use
values from the explorers to decide which configuration to create values from the explorers to decide which configuration to create
for the specified target host. for the specified target host.
It should be primary used to define mapping from configurations to hosts. It should be primary used to define mapping from configurations to hosts.
conf/manifest/*:: conf/manifest/*::
All other files in this directory are not directly used by cdist, but you All other files in this directory are not directly used by cdist, but you
can seperate configuration mappings, if you have a lot of code in the can seperate configuration mappings, if you have a lot of code in the
manifest/init file. This may also be helpful to have different admins manifest/init file. This may also be helpful to have different admins
maintain different groups of hosts. maintain different groups of hosts.
conf/explorer/<name>:: conf/explorer/<name>::
Contains explorers to be run on the target hosts, see cdist-explorer(7). Contains explorers to be run on the target hosts, see cdist-explorer(7).
conf/type/:: conf/type/::
Contains all available types, which are used to provide Contains all available types, which are used to provide
some kind of functionality. See cdist-type(7). some kind of functionality. See cdist-type(7).
conf/type/<name>/:: conf/type/<name>/::
Home of the type <name>. Home of the type <name>.
This directory is referenced by the variable __type (see below). This directory is referenced by the variable __type (see below).
conf/type/<name>/man.text:: conf/type/<name>/man.text::
Manpage in Asciidoc format (required for inclusion into upstream) Manpage in Asciidoc format (required for inclusion into upstream)
conf/type/<name>/manifest:: conf/type/<name>/manifest::
Used to generate additional objects from a type. Used to generate additional objects from a type.
conf/type/<name>/gencode-local:: conf/type/<name>/gencode-local::
Used to generate code to be executed on the server. Used to generate code to be executed on the server.
conf/type/<name>/gencode-remote:: conf/type/<name>/gencode-remote::
Used to generate code to be executed on the client. Used to generate code to be executed on the client.
conf/type/<name>/parameters/required:: conf/type/<name>/parameters/required::
Parameters required by type, \n seperated list. Parameters required by type, \n seperated list.
conf/type/<name>/parameters/optional:: conf/type/<name>/parameters/optional::
Parameters optionally accepted by type, \n seperated list. Parameters optionally accepted by type, \n seperated list.
conf/type/<name>/explorer:: conf/type/<name>/explorer::
Location of the type specific explorers. Location of the type specific explorers.
This directory is referenced by the variable __type_explorer (see below). This directory is referenced by the variable __type_explorer (see below).
See cdist-explorer(7). See cdist-explorer(7).
out/:: out/::
This directory contains output of cdist and is usually located This directory contains output of cdist and is usually located
in a temporary directory and thus will be removed after the run. in a temporary directory and thus will be removed after the run.
This directory is referenced by the variable __global (see below). This directory is referenced by the variable __global (see below).
out/explorer:: out/explorer::
Output of general explorers. Output of general explorers.
out/object:: out/object::
Objects created for the host. Objects created for the host.
out/object/<object>:: out/object/<object>::
Contains all object specific information. Contains all object specific information.
This directory is referenced by the variable __object (see below). This directory is referenced by the variable __object (see below).
out/object/<object>/explorers:: out/object/<object>/explorers::
Output of type specific explorers, per object. Output of type specific explorers, per object.
tmp_dir:: tmp_dir::
A tempdir and a tempfile is used by cdist internally, A tempdir and a tempfile is used by cdist internally,
which will be removed when the scripts end automatically. which will be removed when the scripts end automatically.
TYPES TYPES
----- -----
@ -141,13 +141,13 @@ The following types are available:
eof eof
for type in man7/cdist-type__*.text; do for type in man7/cdist-type__*.text; do
no_dir="${type#man7/}"; no_dir="${type#man7/}";
no_type="${no_dir#cdist-type}"; no_type="${no_dir#cdist-type}";
name="${no_type%.text}"; name="${no_type%.text}";
name_no_underline="$(echo $name | sed 's/^__/\\__/g')" name_no_underline="$(echo $name | sed 's/^__/\\__/g')"
man="${no_dir%.text}(7)" man="${no_dir%.text}(7)"
echo "- $name_no_underline" "($man)" echo "- $name_no_underline" "($man)"
done done
cat << eof cat << eof
@ -159,8 +159,8 @@ For object to object communication and tests, the following paths are
usable within a object directory: usable within a object directory:
changed:: changed::
This empty file exists in an object directory, if the object has This empty file exists in an object directory, if the object has
code to be excuted (either remote or local) code to be excuted (either remote or local)
ENVIRONMENT VARIABLES ENVIRONMENT VARIABLES
@ -169,33 +169,37 @@ __explorer::
Directory that contains all global explorers. Directory that contains all global explorers.
Available for: explorer, type explorer Available for: explorer, type explorer
__manifest:: __manifest::
Directory that contains the initial manifest. Directory that contains the initial manifest.
Available for: initial manifest Available for: initial manifest
__global:: __global::
Directory that contains generic output like explorer. Directory that contains generic output like explorer.
Available for: initial manifest, type manifest, type gencode Available for: initial manifest, type manifest, type gencode
__object:: __object::
Directory that contains the current object. Directory that contains the current object.
Available for: type manifest, type explorer, type gencode Available for: type manifest, type explorer, type gencode
__object_id:: __object_id::
The type unique object id. The type unique object id.
Available for: type manifest, type explorer, type gencode Available for: type manifest, type explorer, type gencode
Note: The leading "/" will always be stripped.
Note: The leading and the trailing "/" will always be stripped (caused by
the filesystem database and ensured by the core).
Note: Double slashes ("//") will not be fixed and result in an error.
__self:: __self::
DEPRECATED: Same as __object_name, do not use anymore, use __object_name instead. DEPRECATED: Same as __object_name, do not use anymore, use __object_name instead.
Will be removed in cdist 3.x. Will be removed in cdist 3.x.
__object_name:: __object_name::
The full qualified name of the current object. The full qualified name of the current object.
Available for: type manifest, type explorer, type gencode Available for: type manifest, type explorer, type gencode
__target_host:: __target_host::
The host we are deploying to. The host we are deploying to.
Available for: explorer, initial manifest, type explorer, type manifest, type gencode Available for: explorer, initial manifest, type explorer, type manifest, type gencode
__type:: __type::
Path to the current type. Path to the current type.
Available for: type manifest, type gencode Available for: type manifest, type gencode
__type_explorer:: __type_explorer::
Directory that contains the type explorers. Directory that contains the type explorers.
Available for: type explorer Available for: type explorer
SEE ALSO SEE ALSO

View file

@ -61,12 +61,19 @@ including it.
HOW TO SUBMIT A NEW TYPE HOW TO SUBMIT A NEW TYPE
------------------------ ------------------------
For detailled information about types, see cdist-type(7).
Submitting a type works as described above, with the additional requirement Submitting a type works as described above, with the additional requirement
that a corresponding manpage named man.text in asciidoc format with that a corresponding manpage named man.text in asciidoc format with
the manpage-name "cdist-type__NAME" is included in the type directory the manpage-name "cdist-type__NAME" is included in the type directory
AND asciidoc is able to compile it (i.e. do NOT have to many "=" in the second AND asciidoc is able to compile it (i.e. do NOT have to many "=" in the second
line). line).
Warning: Submitting "exec" or "run" types that simply echo their parameter in
gencode* will not be accepted, because they are of no use. Every type can output
code and thus such a type introduces redundant functionality that is given by
core cdist already.
SEE ALSO SEE ALSO
-------- --------

View file

@ -19,6 +19,8 @@
# #
# #
VERSION = "2.0.7"
BANNER = """ BANNER = """
.. . .x+=:. s .. . .x+=:. s
dF @88> z` ^% :8 dF @88> z` ^% :8
@ -34,7 +36,6 @@ BANNER = """
"P' "" "" "P' "" ""
""" """
DOT_CDIST = ".cdist" DOT_CDIST = ".cdist"
VERSION = "2.0.5"
import os import os
@ -43,15 +44,17 @@ class Error(Exception):
"""Base exception class for this project""" """Base exception class for this project"""
pass pass
class CdistObjectError(Error):
"""Something went wrong with an object"""
def __init__(self, cdist_object, message):
self.name = cdist_object.name
self.source = " ".join(cdist_object.source)
self.message = message
class MissingEnvironmentVariableError(Error):
"""Raised when a required environment variable is not set."""
def __init__(self, name):
self.name = name
def __str__(self): def __str__(self):
return 'Missing required environment variable: ' + str(self.name) return '%s: %s (defined at %s)' % (self.name, self.message, self.source)
def file_to_list(filename): def file_to_list(filename):
"""Return list from \n seperated file""" """Return list from \n seperated file"""

View file

@ -89,9 +89,9 @@ class ConfigInstall(object):
new_objects_created = True new_objects_created = True
while new_objects_created: while new_objects_created:
new_objects_created = False new_objects_created = False
for cdist_object in core.Object.list_objects(self.local.object_path, for cdist_object in core.CdistObject.list_objects(self.local.object_path,
self.local.type_path): self.local.type_path):
if cdist_object.state == core.Object.STATE_PREPARED: if cdist_object.state == core.CdistObject.STATE_PREPARED:
self.log.debug("Skipping re-prepare of object %s", cdist_object) self.log.debug("Skipping re-prepare of object %s", cdist_object)
continue continue
else: else:
@ -103,16 +103,16 @@ class ConfigInstall(object):
self.log.info("Running manifest and explorers for " + cdist_object.name) self.log.info("Running manifest and explorers for " + cdist_object.name)
self.explorer.run_type_explorers(cdist_object) self.explorer.run_type_explorers(cdist_object)
self.manifest.run_type_manifest(cdist_object) self.manifest.run_type_manifest(cdist_object)
cdist_object.state = core.Object.STATE_PREPARED cdist_object.state = core.CdistObject.STATE_PREPARED
def object_run(self, cdist_object): def object_run(self, cdist_object):
"""Run gencode and code for an object""" """Run gencode and code for an object"""
self.log.debug("Trying to run object " + cdist_object.name) self.log.debug("Trying to run object " + cdist_object.name)
if cdist_object.state == core.Object.STATE_DONE: if cdist_object.state == core.CdistObject.STATE_DONE:
# TODO: remove once we are sure that this really never happens. # TODO: remove once we are sure that this really never happens.
raise cdist.Error("Attempting to run an already finished object: %s", cdist_object) raise cdist.Error("Attempting to run an already finished object: %s", cdist_object)
cdist_type = cdist_object.type cdist_type = cdist_object.cdist_type
# Generate # Generate
self.log.info("Generating and executing code for " + cdist_object.name) self.log.info("Generating and executing code for " + cdist_object.name)
@ -130,13 +130,13 @@ class ConfigInstall(object):
# Mark this object as done # Mark this object as done
self.log.debug("Finishing run of " + cdist_object.name) self.log.debug("Finishing run of " + cdist_object.name)
cdist_object.state = core.Object.STATE_DONE cdist_object.state = core.CdistObject.STATE_DONE
def stage_run(self): def stage_run(self):
"""The final (and real) step of deployment""" """The final (and real) step of deployment"""
self.log.info("Generating and executing code") self.log.info("Generating and executing code")
objects = core.Object.list_objects( objects = core.CdistObject.list_objects(
self.local.object_path, self.local.object_path,
self.local.type_path) self.local.type_path)

View file

@ -19,11 +19,11 @@
# #
# #
from cdist.core.type import Type from cdist.core.cdist_type import CdistType
from cdist.core.type import NoSuchTypeError from cdist.core.cdist_type import NoSuchTypeError
from cdist.core.object import Object from cdist.core.cdist_object import CdistObject
from cdist.core.object import IllegalObjectIdError from cdist.core.cdist_object import IllegalObjectIdError
from cdist.core.object import OBJECT_MARKER from cdist.core.cdist_object import OBJECT_MARKER
from cdist.core.explorer import Explorer from cdist.core.explorer import Explorer
from cdist.core.manifest import Manifest from cdist.core.manifest import Manifest
from cdist.core.code import Code from cdist.core.code import Code

View file

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# 2011 Steven Armstrong (steven-cdist at armstrong.cc) # 2011 Steven Armstrong (steven-cdist at armstrong.cc)
# 2011 Nico Schottelius (nico-cdist at schottelius.org) # 2011-2012 Nico Schottelius (nico-cdist at schottelius.org)
# #
# This file is part of cdist. # This file is part of cdist.
# #
@ -42,7 +42,7 @@ class IllegalObjectIdError(cdist.Error):
return '%s: %s' % (self.message, self.object_id) return '%s: %s' % (self.message, self.object_id)
class Object(object): class CdistObject(object):
"""Represents a cdist object. """Represents a cdist object.
All interaction with objects in cdist should be done through this class. All interaction with objects in cdist should be done through this class.
@ -61,7 +61,7 @@ class Object(object):
"""Return a list of object instances""" """Return a list of object instances"""
for object_name in cls.list_object_names(object_base_path): for object_name in cls.list_object_names(object_base_path):
type_name, object_id = cls.split_name(object_name) type_name, object_id = cls.split_name(object_name)
yield cls(cdist.core.Type(type_base_path, type_name), object_base_path, object_id=object_id) yield cls(cdist.core.CdistType(type_base_path, type_name), object_base_path, object_id=object_id)
@classmethod @classmethod
def list_type_names(cls, object_base_path): def list_type_names(cls, object_base_path):
@ -96,30 +96,58 @@ class Object(object):
""" """
return os.path.join(type_name, object_id) return os.path.join(type_name, object_id)
@staticmethod def validate_object_id(self):
def validate_object_id(object_id): # FIXME: also check that there is no object ID when type is singleton?
"""Validate the given object_id and raise IllegalObjectIdError if it's not valid. """Validate the given object_id and raise IllegalObjectIdError if it's not valid.
""" """
if object_id: if self.object_id:
if object_id.startswith('/'): if OBJECT_MARKER in self.object_id.split(os.sep):
raise IllegalObjectIdError(object_id, 'object_id may not start with /') raise IllegalObjectIdError(self.object_id, 'object_id may not contain \'%s\'' % OBJECT_MARKER)
if OBJECT_MARKER in object_id.split(os.sep): if '//' in self.object_id:
raise IllegalObjectIdError(object_id, 'object_id may not contain \'%s\'' % OBJECT_MARKER) raise IllegalObjectIdError(self.object_id, 'object_id may not contain //')
# If no object_id and type is not singleton => error out
if not self.object_id and not self.cdist_type.is_singleton:
raise IllegalObjectIdError(self.object_id,
"Missing object_id and type is not a singleton.")
def __init__(self, cdist_type, base_path, object_id=None): def __init__(self, cdist_type, base_path, object_id=None):
self.validate_object_id(object_id) self.cdist_type = cdist_type # instance of Type
self.type = cdist_type # instance of Type
self.base_path = base_path self.base_path = base_path
self.object_id = object_id self.object_id = object_id
self.name = self.join_name(self.type.name, self.object_id)
self.path = os.path.join(self.type.path, self.object_id, OBJECT_MARKER) self.validate_object_id()
self.sanitise_object_id()
self.name = self.join_name(self.cdist_type.name, self.object_id)
self.path = os.path.join(self.cdist_type.path, self.object_id, OBJECT_MARKER)
self.absolute_path = os.path.join(self.base_path, self.path) self.absolute_path = os.path.join(self.base_path, self.path)
self.code_local_path = os.path.join(self.path, "code-local") self.code_local_path = os.path.join(self.path, "code-local")
self.code_remote_path = os.path.join(self.path, "code-remote") self.code_remote_path = os.path.join(self.path, "code-remote")
self.parameter_path = os.path.join(self.path, "parameter") self.parameter_path = os.path.join(self.path, "parameter")
def object_from_name(self, object_name):
"""Convenience method for creating an object instance from an object name.
Mainly intended to create objects when resolving requirements.
e.g:
<CdistObject __foo/bar>.object_from_name('__other/object') -> <CdistObject __other/object>
"""
base_path = self.base_path
type_path = self.cdist_type.base_path
type_name, object_id = self.split_name(object_name)
cdist_type = self.cdist_type.__class__(type_path, type_name)
return self.__class__(cdist_type, base_path, object_id=object_id)
def __repr__(self): def __repr__(self):
return '<Object %s>' % self.name return '<CdistObject %s>' % self.name
def __eq__(self, other): def __eq__(self, other):
"""define equality as 'name is the same'""" """define equality as 'name is the same'"""
@ -128,23 +156,23 @@ class Object(object):
def __hash__(self): def __hash__(self):
return hash(self.name) return hash(self.name)
def __lt__(self, other): def __lt__(self, other):
return isinstance(other, self.__class__) and self.name < other.name return isinstance(other, self.__class__) and self.name < other.name
def object_from_name(self, object_name): def sanitise_object_id(self):
"""Convenience method for creating an object instance from an object name.
Mainly intended to create objects when resolving requirements.
e.g:
<Object __foo/bar>.object_from_name('__other/object') -> <Object __other/object>
""" """
type_path = self.type.base_path Remove leading and trailing slash (one only)
base_path = self.base_path """
type_name, object_id = self.split_name(object_name)
return self.__class__(self.type.__class__(type_path, type_name), base_path, object_id=object_id) # Allow empty object id for singletons
if self.object_id:
# Remove leading slash
if self.object_id[0] == '/':
self.object_id = self.object_id[1:]
# Remove trailing slash
if self.object_id[-1] == '/':
self.object_id = self.object_id[:-1]
# FIXME: still needed? # FIXME: still needed?
@property @property

View file

@ -34,7 +34,7 @@ class NoSuchTypeError(cdist.Error):
return "Type '%s' does not exist at %s" % (self.type_path, self.type_absolute_path) return "Type '%s' does not exist at %s" % (self.type_path, self.type_absolute_path)
class Type(object): class CdistType(object):
"""Represents a cdist type. """Represents a cdist type.
All interaction with types in cdist should be done through this class. All interaction with types in cdist should be done through this class.
@ -61,7 +61,7 @@ class Type(object):
# name is second argument # name is second argument
name = args[1] name = args[1]
if not name in cls._instances: if not name in cls._instances:
instance = super(Type, cls).__new__(cls) instance = super(CdistType, cls).__new__(cls)
cls._instances[name] = instance cls._instances[name] = instance
# return instance so __init__ is called # return instance so __init__ is called
return cls._instances[name] return cls._instances[name]
@ -84,7 +84,7 @@ class Type(object):
self.__optional_parameters = None self.__optional_parameters = None
def __repr__(self): def __repr__(self):
return '<Type %s>' % self.name return '<CdistType %s>' % self.name
def __eq__(self, other): def __eq__(self, other):
return isinstance(other, self.__class__) and self.name == other.name return isinstance(other, self.__class__) and self.name == other.name

View file

@ -92,17 +92,14 @@ class Code(object):
'__global': self.local.out_path, '__global': self.local.out_path,
} }
if log.getEffectiveLevel() == logging.DEBUG:
self.env.update({'__debug': "yes" })
def _run_gencode(self, cdist_object, which): def _run_gencode(self, cdist_object, which):
cdist_type = cdist_object.type cdist_type = cdist_object.cdist_type
script = os.path.join(self.local.type_path, getattr(cdist_type, 'gencode_%s_path' % which)) script = os.path.join(self.local.type_path, getattr(cdist_type, 'gencode_%s_path' % which))
if os.path.isfile(script): if os.path.isfile(script):
env = os.environ.copy() env = os.environ.copy()
env.update(self.env) env.update(self.env)
env.update({ env.update({
'__type': cdist_object.type.absolute_path, '__type': cdist_object.cdist_type.absolute_path,
'__object': cdist_object.absolute_path, '__object': cdist_object.absolute_path,
'__object_id': cdist_object.object_id, '__object_id': cdist_object.object_id,
'__object_name': cdist_object.name, '__object_name': cdist_object.name,

View file

@ -73,9 +73,6 @@ class Explorer(object):
'__target_host': self.target_host, '__target_host': self.target_host,
'__explorer': self.remote.global_explorer_path, '__explorer': self.remote.global_explorer_path,
} }
# FIXME: remove soon with new logging infrastructure
if self.log.getEffectiveLevel() == logging.DEBUG:
self.env.update({'__debug': "yes" })
self._type_explorers_transferred = [] self._type_explorers_transferred = []
### global ### global
@ -122,15 +119,28 @@ class Explorer(object):
in the object. in the object.
""" """
self.log.debug("Transfering type explorers for type: %s", cdist_object.type) self.log.debug("Transfering type explorers for type: %s", cdist_object.cdist_type)
self.transfer_type_explorers(cdist_object.type) self.transfer_type_explorers(cdist_object.cdist_type)
self.log.debug("Transfering object parameters for object: %s", cdist_object.name) self.log.debug("Transfering object parameters for object: %s", cdist_object.name)
self.transfer_object_parameters(cdist_object) self.transfer_object_parameters(cdist_object)
for explorer in self.list_type_explorer_names(cdist_object.type): for explorer in self.list_type_explorer_names(cdist_object.cdist_type):
output = self.run_type_explorer(explorer, cdist_object) output = self.run_type_explorer(explorer, cdist_object)
self.log.debug("Running type explorer '%s' for object '%s'", explorer, cdist_object.name) self.log.debug("Running type explorer '%s' for object '%s'", explorer, cdist_object.name)
cdist_object.explorers[explorer] = output cdist_object.explorers[explorer] = output
def run_type_explorer(self, explorer, cdist_object):
"""Run the given type explorer for the given object and return it's output."""
cdist_type = cdist_object.cdist_type
env = self.env.copy()
env.update({
'__object': os.path.join(self.remote.object_path, cdist_object.path),
'__object_id': cdist_object.object_id,
'__object_fq': cdist_object.path,
'__type_explorer': os.path.join(self.remote.type_path, cdist_type.explorer_path)
})
script = os.path.join(self.remote.type_path, cdist_type.explorer_path, explorer)
return self.remote.run_script(script, env=env, return_output=True)
def transfer_type_explorers(self, cdist_type): def transfer_type_explorers(self, cdist_type):
"""Transfer the type explorers for the given type to the remote side.""" """Transfer the type explorers for the given type to the remote side."""
if cdist_type.explorers: if cdist_type.explorers:
@ -150,16 +160,3 @@ class Explorer(object):
destination = os.path.join(self.remote.object_path, cdist_object.parameter_path) destination = os.path.join(self.remote.object_path, cdist_object.parameter_path)
self.remote.mkdir(destination) self.remote.mkdir(destination)
self.remote.transfer(source, destination) self.remote.transfer(source, destination)
def run_type_explorer(self, explorer, cdist_object):
"""Run the given type explorer for the given object and return it's output."""
cdist_type = cdist_object.type
env = self.env.copy()
env.update({
'__object': os.path.join(self.remote.object_path, cdist_object.path),
'__object_id': cdist_object.object_id,
'__object_fq': cdist_object.path,
'__type_explorer': os.path.join(self.remote.type_path, cdist_type.explorer_path)
})
script = os.path.join(self.remote.type_path, cdist_type.explorer_path, explorer)
return self.remote.run_script(script, env=env, return_output=True)

View file

@ -87,7 +87,7 @@ class Manifest(object):
self.local.run_script(script, env=env) self.local.run_script(script, env=env)
def run_type_manifest(self, cdist_object): def run_type_manifest(self, cdist_object):
script = os.path.join(self.local.type_path, cdist_object.type.manifest_path) script = os.path.join(self.local.type_path, cdist_object.cdist_type.manifest_path)
if os.path.isfile(script): if os.path.isfile(script):
env = os.environ.copy() env = os.environ.copy()
env.update(self.env) env.update(self.env)
@ -96,7 +96,7 @@ class Manifest(object):
'__object_id': cdist_object.object_id, '__object_id': cdist_object.object_id,
'__object_name': cdist_object.name, '__object_name': cdist_object.name,
'__self': cdist_object.name, '__self': cdist_object.name,
'__type': cdist_object.type.absolute_path, '__type': cdist_object.cdist_type.absolute_path,
'__cdist_manifest': script, '__cdist_manifest': script,
}) })
self.local.run_script(script, env=env) self.local.run_script(script, env=env)

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
# 2011 Nico Schottelius (nico-cdist at schottelius.org) # 2011-2012 Nico Schottelius (nico-cdist at schottelius.org)
# #
# This file is part of cdist. # This file is part of cdist.
# #
@ -26,29 +26,22 @@ import os
import cdist import cdist
from cdist import core from cdist import core
class IllegalRequirementError(cdist.Error):
def __init__(self, requirement, message=None):
self.requirement = requirement
self.message = message or 'Illegal requirement'
def __str__(self):
return '%s: %s' % (self.message, self.requirement)
class Emulator(object): class Emulator(object):
def __init__(self, argv): def __init__(self, argv):
self.argv = argv self.argv = argv
self.object_id = False self.object_id = False
self.global_path = os.environ['__global'] self.global_path = os.environ['__global']
self.object_source = os.environ['__cdist_manifest']
self.target_host = os.environ['__target_host'] self.target_host = os.environ['__target_host']
# Internally only
self.object_source = os.environ['__cdist_manifest']
self.type_base_path = os.environ['__cdist_type_base_path'] self.type_base_path = os.environ['__cdist_type_base_path']
self.object_base_path = os.path.join(self.global_path, "object") self.object_base_path = os.path.join(self.global_path, "object")
self.type_name = os.path.basename(argv[0]) self.type_name = os.path.basename(argv[0])
self.cdist_type = core.Type(self.type_base_path, self.type_name) self.cdist_type = core.CdistType(self.type_base_path, self.type_name)
self.__init_log() self.__init_log()
@ -113,20 +106,15 @@ class Emulator(object):
def setup_object(self): def setup_object(self):
# FIXME: verify object id # Setup object_id - FIXME: unset / do not setup anymore!
# Setup object_id
if self.cdist_type.is_singleton: if self.cdist_type.is_singleton:
self.object_id = "singleton" self.object_id = "singleton"
else: else:
self.object_id = self.args.object_id[0] self.object_id = self.args.object_id[0]
del self.args.object_id del self.args.object_id
# strip leading slash from object_id
self.object_id = self.object_id.lstrip('/')
# Instantiate the cdist object we are defining # Instantiate the cdist object we are defining
self.cdist_object = core.Object(self.cdist_type, self.object_base_path, self.object_id) self.cdist_object = core.CdistObject(self.cdist_type, self.object_base_path, self.object_id)
# Create object with given parameters # Create object with given parameters
self.parameters = {} self.parameters = {}
@ -143,6 +131,9 @@ class Emulator(object):
self.cdist_object.create() self.cdist_object.create()
self.cdist_object.parameters = self.parameters self.cdist_object.parameters = self.parameters
# Record / Append source
self.cdist_object.source.append(self.object_source)
def record_requirements(self): def record_requirements(self):
"""record requirements""" """record requirements"""
@ -151,25 +142,17 @@ class Emulator(object):
self.log.debug("reqs = " + requirements) self.log.debug("reqs = " + requirements)
for requirement in requirements.split(" "): for requirement in requirements.split(" "):
# Ignore empty fields - probably the only field anyway # Ignore empty fields - probably the only field anyway
if len(requirement) == 0: if len(requirement) == 0: continue
continue
requirement_type_name, requirement_object_id = core.Object.split_name(requirement) # Raises an error, if object cannot be created
# Instantiate type which fails if type does not exist cdist_object = self.cdist_object.object_from_name(requirement)
requirement_type = core.Type(self.type_base_path, requirement_type_name)
if requirement_object_id:
# Validate object_id if any
core.Object.validate_object_id(requirement_object_id)
elif not requirement_type.is_singleton:
# Only singeltons have no object_id
raise IllegalRequirementError(requirement, "Missing object_id and type is not a singleton.")
self.log.debug("Recording requirement: " + requirement) self.log.debug("Recording requirement: " + requirement)
self.cdist_object.requirements.append(requirement)
# Record / Append source # Save the sanitised version, not the user supplied one
self.cdist_object.source.append(self.object_source) # (__file//bar => __file/bar)
# This ensures pattern matching is done against sanitised list
self.cdist_object.requirements.append(cdist_object.name)
def record_auto_requirements(self): def record_auto_requirements(self):
"""An object shall automatically depend on all objects that it defined in it's type manifest. """An object shall automatically depend on all objects that it defined in it's type manifest.

View file

@ -143,7 +143,7 @@ class Local(object):
def link_emulator(self, exec_path): def link_emulator(self, exec_path):
"""Link emulator to types""" """Link emulator to types"""
src = os.path.abspath(exec_path) src = os.path.abspath(exec_path)
for cdist_type in core.Type.list_types(self.type_path): for cdist_type in core.CdistType.list_types(self.type_path):
dst = os.path.join(self.bin_path, cdist_type.name) dst = os.path.join(self.bin_path, cdist_type.name)
self.log.debug("Linking emulator: %s to %s", src, dst) self.log.debug("Linking emulator: %s to %s", src, dst)

View file

@ -20,8 +20,6 @@
# #
# #
# FIXME: common base class with Local?
import io import io
import os import os
import sys import sys
@ -148,18 +146,17 @@ class Remote(object):
os_environ = os.environ.copy() os_environ = os.environ.copy()
os_environ['__target_host'] = self.target_host os_environ['__target_host'] = self.target_host
self.log.debug("Remote run script: %s", command)
# can't pass environment to remote side, so prepend command with # can't pass environment to remote side, so prepend command with
# variable declarations # variable declarations
if env: if env:
self.log.debug("Remote run script env: %s", env)
command.extend(["%s=%s" % item for item in env.items()]) command.extend(["%s=%s" % item for item in env.items()])
command.extend(["/bin/sh", "-e"]) command.extend(["/bin/sh", "-e"])
command.append(script) command.append(script)
self.log.debug("Remote run script: %s", command)
if env:
self.log.debug("Remote run script env: %s", env)
try: try:
if return_output: if return_output:
return subprocess.check_output(command, env=os_environ).decode() return subprocess.check_output(command, env=os_environ).decode()

View file

@ -125,7 +125,7 @@ class DependencyResolver(object):
resolved.append(cdist_object) resolved.append(cdist_object)
unresolved.remove(cdist_object) unresolved.remove(cdist_object)
except RequirementNotFoundError as e: except RequirementNotFoundError as e:
raise cdist.Error(cdist_object.name + " requires non-existing " + e.requirement) raise cdist.CdistObjectError(cdist_object, "requires non-existing " + e.requirement)
def __iter__(self): def __iter__(self):
"""Iterate over all unique objects while resolving dependencies. """Iterate over all unique objects while resolving dependencies.

View file

@ -54,8 +54,8 @@ class CodeTestCase(test.CdistTestCase):
self.code = code.Code(self.target_host, self.local, self.remote) self.code = code.Code(self.target_host, self.local, self.remote)
self.cdist_type = core.Type(self.local.type_path, '__dump_environment') self.cdist_type = core.CdistType(self.local.type_path, '__dump_environment')
self.cdist_object = core.Object(self.cdist_type, self.local.object_path, 'whatever') self.cdist_object = core.CdistObject(self.cdist_type, self.local.object_path, 'whatever')
self.cdist_object.create() self.cdist_object.create()
self.log = logging.getLogger("cdist") self.log = logging.getLogger("cdist")

View file

@ -79,7 +79,7 @@ class EmulatorTestCase(test.CdistTestCase):
os.environ.update(self.env) os.environ.update(self.env)
os.environ['require'] = '__file' os.environ['require'] = '__file'
emu = emulator.Emulator(argv) emu = emulator.Emulator(argv)
self.assertRaises(emulator.IllegalRequirementError, emu.run) self.assertRaises(core.IllegalObjectIdError, emu.run)
def test_singleton_object_requirement(self): def test_singleton_object_requirement(self):
argv = ['__file', '/tmp/foobar'] argv = ['__file', '/tmp/foobar']
@ -119,8 +119,8 @@ class AutoRequireEmulatorTestCase(test.CdistTestCase):
def test_autorequire(self): def test_autorequire(self):
initial_manifest = os.path.join(self.local.manifest_path, "init") initial_manifest = os.path.join(self.local.manifest_path, "init")
self.manifest.run_initial_manifest(initial_manifest) self.manifest.run_initial_manifest(initial_manifest)
cdist_type = core.Type(self.local.type_path, '__saturn') cdist_type = core.CdistType(self.local.type_path, '__saturn')
cdist_object = core.Object(cdist_type, self.local.object_path, 'singleton') cdist_object = core.CdistObject(cdist_type, self.local.object_path, 'singleton')
self.manifest.run_type_manifest(cdist_object) self.manifest.run_type_manifest(cdist_object)
expected = ['__planet/Saturn', '__moon/Prometheus'] expected = ['__planet/Saturn', '__moon/Prometheus']
self.assertEqual(sorted(cdist_object.requirements), sorted(expected)) self.assertEqual(sorted(cdist_object.requirements), sorted(expected))
@ -156,6 +156,6 @@ class ArgumentsWithDashesTestCase(test.CdistTestCase):
emu = emulator.Emulator(argv) emu = emulator.Emulator(argv)
emu.run() emu.run()
cdist_type = core.Type(self.local.type_path, '__arguments_with_dashes') cdist_type = core.CdistType(self.local.type_path, '__arguments_with_dashes')
cdist_object = core.Object(cdist_type, self.local.object_path, 'some-id') cdist_object = core.CdistObject(cdist_type, self.local.object_path, 'some-id')
self.assertTrue('with-dash' in cdist_object.parameters) self.assertTrue('with-dash' in cdist_object.parameters)

View file

@ -83,19 +83,19 @@ class ExplorerClassTestCase(test.CdistTestCase):
shutil.rmtree(out_path) shutil.rmtree(out_path)
def test_list_type_explorer_names(self): def test_list_type_explorer_names(self):
cdist_type = core.Type(self.local.type_path, '__test_type') cdist_type = core.CdistType(self.local.type_path, '__test_type')
expected = cdist_type.explorers expected = cdist_type.explorers
self.assertEqual(self.explorer.list_type_explorer_names(cdist_type), expected) self.assertEqual(self.explorer.list_type_explorer_names(cdist_type), expected)
def test_transfer_type_explorers(self): def test_transfer_type_explorers(self):
cdist_type = core.Type(self.local.type_path, '__test_type') cdist_type = core.CdistType(self.local.type_path, '__test_type')
self.explorer.transfer_type_explorers(cdist_type) self.explorer.transfer_type_explorers(cdist_type)
source = os.path.join(self.local.type_path, cdist_type.explorer_path) source = os.path.join(self.local.type_path, cdist_type.explorer_path)
destination = os.path.join(self.remote.type_path, cdist_type.explorer_path) destination = os.path.join(self.remote.type_path, cdist_type.explorer_path)
self.assertEqual(os.listdir(source), os.listdir(destination)) self.assertEqual(os.listdir(source), os.listdir(destination))
def test_transfer_type_explorers_only_once(self): def test_transfer_type_explorers_only_once(self):
cdist_type = core.Type(self.local.type_path, '__test_type') cdist_type = core.CdistType(self.local.type_path, '__test_type')
# first transfer # first transfer
self.explorer.transfer_type_explorers(cdist_type) self.explorer.transfer_type_explorers(cdist_type)
source = os.path.join(self.local.type_path, cdist_type.explorer_path) source = os.path.join(self.local.type_path, cdist_type.explorer_path)
@ -109,8 +109,8 @@ class ExplorerClassTestCase(test.CdistTestCase):
self.assertFalse(os.listdir(destination)) self.assertFalse(os.listdir(destination))
def test_transfer_object_parameters(self): def test_transfer_object_parameters(self):
cdist_type = core.Type(self.local.type_path, '__test_type') cdist_type = core.CdistType(self.local.type_path, '__test_type')
cdist_object = core.Object(cdist_type, self.local.object_path, 'whatever') cdist_object = core.CdistObject(cdist_type, self.local.object_path, 'whatever')
cdist_object.create() cdist_object.create()
cdist_object.parameters = {'first': 'first value', 'second': 'second value'} cdist_object.parameters = {'first': 'first value', 'second': 'second value'}
self.explorer.transfer_object_parameters(cdist_object) self.explorer.transfer_object_parameters(cdist_object)
@ -119,15 +119,15 @@ class ExplorerClassTestCase(test.CdistTestCase):
self.assertEqual(sorted(os.listdir(source)), sorted(os.listdir(destination))) self.assertEqual(sorted(os.listdir(source)), sorted(os.listdir(destination)))
def test_run_type_explorer(self): def test_run_type_explorer(self):
cdist_type = core.Type(self.local.type_path, '__test_type') cdist_type = core.CdistType(self.local.type_path, '__test_type')
cdist_object = core.Object(cdist_type, self.local.object_path, 'whatever') cdist_object = core.CdistObject(cdist_type, self.local.object_path, 'whatever')
self.explorer.transfer_type_explorers(cdist_type) self.explorer.transfer_type_explorers(cdist_type)
output = self.explorer.run_type_explorer('world', cdist_object) output = self.explorer.run_type_explorer('world', cdist_object)
self.assertEqual(output, 'hello\n') self.assertEqual(output, 'hello\n')
def test_run_type_explorers(self): def test_run_type_explorers(self):
cdist_type = core.Type(self.local.type_path, '__test_type') cdist_type = core.CdistType(self.local.type_path, '__test_type')
cdist_object = core.Object(cdist_type, self.local.object_path, 'whatever') cdist_object = core.CdistObject(cdist_type, self.local.object_path, 'whatever')
cdist_object.create() cdist_object.create()
self.explorer.run_type_explorers(cdist_object) self.explorer.run_type_explorers(cdist_object)
self.assertEqual(cdist_object.explorers, {'world': 'hello'}) self.assertEqual(cdist_object.explorers, {'world': 'hello'})

View file

@ -79,8 +79,8 @@ class ManifestTestCase(test.CdistTestCase):
self.assertEqual(output_dict['__manifest'], self.local.manifest_path) self.assertEqual(output_dict['__manifest'], self.local.manifest_path)
def test_type_manifest_environment(self): def test_type_manifest_environment(self):
cdist_type = core.Type(self.local.type_path, '__dump_environment') cdist_type = core.CdistType(self.local.type_path, '__dump_environment')
cdist_object = core.Object(cdist_type, self.local.object_path, 'whatever') cdist_object = core.CdistObject(cdist_type, self.local.object_path, 'whatever')
handle, output_file = self.mkstemp(dir=self.temp_dir) handle, output_file = self.mkstemp(dir=self.temp_dir)
os.close(handle) os.close(handle)
os.environ['__cdist_test_out'] = output_file os.environ['__cdist_test_out'] = output_file

View file

@ -25,6 +25,8 @@ import shutil
from cdist import test from cdist import test
from cdist import core from cdist import core
import cdist
import os.path as op import os.path as op
my_dir = op.abspath(op.dirname(__file__)) my_dir = op.abspath(op.dirname(__file__))
fixtures = op.join(my_dir, 'fixtures') fixtures = op.join(my_dir, 'fixtures')
@ -34,48 +36,48 @@ type_base_path = op.join(fixtures, 'type')
class ObjectClassTestCase(test.CdistTestCase): class ObjectClassTestCase(test.CdistTestCase):
def test_list_object_names(self): def test_list_object_names(self):
object_names = list(core.Object.list_object_names(object_base_path)) object_names = list(core.CdistObject.list_object_names(object_base_path))
self.assertEqual(object_names, ['__first/man', '__second/on-the', '__third/moon']) self.assertEqual(object_names, ['__first/man', '__second/on-the', '__third/moon'])
def test_list_type_names(self): def test_list_type_names(self):
type_names = list(core.Object.list_type_names(object_base_path)) type_names = list(cdist.core.CdistObject.list_type_names(object_base_path))
self.assertEqual(type_names, ['__first', '__second', '__third']) self.assertEqual(type_names, ['__first', '__second', '__third'])
def test_list_objects(self): def test_list_objects(self):
objects = list(core.Object.list_objects(object_base_path, type_base_path)) objects = list(core.CdistObject.list_objects(object_base_path, type_base_path))
objects_expected = [ objects_expected = [
core.Object(core.Type(type_base_path, '__first'), object_base_path, 'man'), core.CdistObject(core.CdistType(type_base_path, '__first'), object_base_path, 'man'),
core.Object(core.Type(type_base_path, '__second'), object_base_path, 'on-the'), core.CdistObject(core.CdistType(type_base_path, '__second'), object_base_path, 'on-the'),
core.Object(core.Type(type_base_path, '__third'), object_base_path, 'moon'), core.CdistObject(core.CdistType(type_base_path, '__third'), object_base_path, 'moon'),
] ]
self.assertEqual(objects, objects_expected) self.assertEqual(objects, objects_expected)
class ObjectIdTestCase(test.CdistTestCase): class ObjectIdTestCase(test.CdistTestCase):
def test_object_id_starts_with_slash(self): def test_object_id_contains_double_slash(self):
cdist_type = core.Type(type_base_path, '__third') cdist_type = core.CdistType(type_base_path, '__third')
illegal_object_id = '/object_id/may/not/start/with/slash' illegal_object_id = '/object_id//may/not/contain/double/slash'
with self.assertRaises(core.IllegalObjectIdError): with self.assertRaises(core.IllegalObjectIdError):
core.Object(cdist_type, object_base_path, illegal_object_id) core.CdistObject(cdist_type, object_base_path, illegal_object_id)
def test_object_id_contains_object_marker(self): def test_object_id_contains_object_marker(self):
cdist_type = core.Type(type_base_path, '__third') cdist_type = core.CdistType(type_base_path, '__third')
illegal_object_id = 'object_id/may/not/contain/%s/anywhere' % core.OBJECT_MARKER illegal_object_id = 'object_id/may/not/contain/%s/anywhere' % core.OBJECT_MARKER
with self.assertRaises(core.IllegalObjectIdError): with self.assertRaises(core.IllegalObjectIdError):
core.Object(cdist_type, object_base_path, illegal_object_id) core.CdistObject(cdist_type, object_base_path, illegal_object_id)
def test_object_id_contains_object_marker_string(self): def test_object_id_contains_object_marker_string(self):
cdist_type = core.Type(type_base_path, '__third') cdist_type = core.CdistType(type_base_path, '__third')
illegal_object_id = 'object_id/may/contain_%s_in_filename' % core.OBJECT_MARKER illegal_object_id = 'object_id/may/contain_%s_in_filename' % core.OBJECT_MARKER
core.Object(cdist_type, object_base_path, illegal_object_id) core.CdistObject(cdist_type, object_base_path, illegal_object_id)
# if we get here, the test passed # if we get here, the test passed
class ObjectTestCase(test.CdistTestCase): class ObjectTestCase(test.CdistTestCase):
def setUp(self): def setUp(self):
self.cdist_type = core.Type(type_base_path, '__third') self.cdist_type = core.CdistType(type_base_path, '__third')
self.cdist_object = core.Object(self.cdist_type, object_base_path, 'moon') self.cdist_object = core.CdistObject(self.cdist_type, object_base_path, 'moon')
def tearDown(self): def tearDown(self):
self.cdist_object.changed = False self.cdist_object.changed = False
@ -159,16 +161,16 @@ class ObjectTestCase(test.CdistTestCase):
self.assertEqual(self.cdist_object.state, '') self.assertEqual(self.cdist_object.state, '')
def test_state_prepared(self): def test_state_prepared(self):
self.cdist_object.state = core.Object.STATE_PREPARED self.cdist_object.state = core.CdistObject.STATE_PREPARED
self.assertEqual(self.cdist_object.state, core.Object.STATE_PREPARED) self.assertEqual(self.cdist_object.state, core.CdistObject.STATE_PREPARED)
def test_state_running(self): def test_state_running(self):
self.cdist_object.state = core.Object.STATE_RUNNING self.cdist_object.state = core.CdistObject.STATE_RUNNING
self.assertEqual(self.cdist_object.state, core.Object.STATE_RUNNING) self.assertEqual(self.cdist_object.state, core.CdistObject.STATE_RUNNING)
def test_state_done(self): def test_state_done(self):
self.cdist_object.state = core.Object.STATE_DONE self.cdist_object.state = core.CdistObject.STATE_DONE
self.assertEqual(self.cdist_object.state, core.Object.STATE_DONE) self.assertEqual(self.cdist_object.state, core.CdistObject.STATE_DONE)
def test_source(self): def test_source(self):
self.assertEqual(list(self.cdist_object.source), []) self.assertEqual(list(self.cdist_object.source), [])
@ -195,6 +197,6 @@ class ObjectTestCase(test.CdistTestCase):
self.cdist_object.code_remote = 'Hello World' self.cdist_object.code_remote = 'Hello World'
other_name = '__first/man' other_name = '__first/man'
other_object = self.cdist_object.object_from_name(other_name) other_object = self.cdist_object.object_from_name(other_name)
self.assertTrue(isinstance(other_object, core.Object)) self.assertTrue(isinstance(other_object, core.CdistObject))
self.assertEqual(other_object.type.name, '__first') self.assertEqual(other_object.cdist_type.name, '__first')
self.assertEqual(other_object.object_id, 'man') self.assertEqual(other_object.object_id, 'man')

View file

@ -37,7 +37,7 @@ type_base_path = op.join(fixtures, 'type')
class ResolverTestCase(test.CdistTestCase): class ResolverTestCase(test.CdistTestCase):
def setUp(self): def setUp(self):
self.objects = list(core.Object.list_objects(object_base_path, type_base_path)) self.objects = list(core.CdistObject.list_objects(object_base_path, type_base_path))
self.object_index = dict((o.name, o) for o in self.objects) self.object_index = dict((o.name, o) for o in self.objects)
self.dependency_resolver = resolver.DependencyResolver(self.objects) self.dependency_resolver = resolver.DependencyResolver(self.objects)

View file

@ -33,115 +33,115 @@ class TypeTestCase(test.CdistTestCase):
def test_list_type_names(self): def test_list_type_names(self):
base_path = op.join(fixtures, 'list_types') base_path = op.join(fixtures, 'list_types')
type_names = core.Type.list_type_names(base_path) type_names = core.CdistType.list_type_names(base_path)
self.assertEqual(type_names, ['__first', '__second', '__third']) self.assertEqual(type_names, ['__first', '__second', '__third'])
def test_list_types(self): def test_list_types(self):
base_path = op.join(fixtures, 'list_types') base_path = op.join(fixtures, 'list_types')
types = list(core.Type.list_types(base_path)) types = list(core.CdistType.list_types(base_path))
types_expected = [ types_expected = [
core.Type(base_path, '__first'), core.CdistType(base_path, '__first'),
core.Type(base_path, '__second'), core.CdistType(base_path, '__second'),
core.Type(base_path, '__third'), core.CdistType(base_path, '__third'),
] ]
self.assertEqual(types, types_expected) self.assertEqual(types, types_expected)
def test_only_one_instance(self): def test_only_one_instance(self):
base_path = fixtures base_path = fixtures
cdist_type1 = core.Type(base_path, '__name_path') cdist_type1 = core.CdistType(base_path, '__name_path')
cdist_type2 = core.Type(base_path, '__name_path') cdist_type2 = core.CdistType(base_path, '__name_path')
self.assertEqual(id(cdist_type1), id(cdist_type2)) self.assertEqual(id(cdist_type1), id(cdist_type2))
def test_nonexistent_type(self): def test_nonexistent_type(self):
base_path = fixtures base_path = fixtures
self.assertRaises(core.NoSuchTypeError, core.Type, base_path, '__i-dont-exist') self.assertRaises(core.NoSuchTypeError, core.CdistType, base_path, '__i-dont-exist')
def test_name(self): def test_name(self):
base_path = fixtures base_path = fixtures
cdist_type = core.Type(base_path, '__name_path') cdist_type = core.CdistType(base_path, '__name_path')
self.assertEqual(cdist_type.name, '__name_path') self.assertEqual(cdist_type.name, '__name_path')
def test_path(self): def test_path(self):
base_path = fixtures base_path = fixtures
cdist_type = core.Type(base_path, '__name_path') cdist_type = core.CdistType(base_path, '__name_path')
self.assertEqual(cdist_type.path, '__name_path') self.assertEqual(cdist_type.path, '__name_path')
def test_base_path(self): def test_base_path(self):
base_path = fixtures base_path = fixtures
cdist_type = core.Type(base_path, '__name_path') cdist_type = core.CdistType(base_path, '__name_path')
self.assertEqual(cdist_type.base_path, base_path) self.assertEqual(cdist_type.base_path, base_path)
def test_absolute_path(self): def test_absolute_path(self):
base_path = fixtures base_path = fixtures
cdist_type = core.Type(base_path, '__name_path') cdist_type = core.CdistType(base_path, '__name_path')
self.assertEqual(cdist_type.absolute_path, os.path.join(base_path, '__name_path')) self.assertEqual(cdist_type.absolute_path, os.path.join(base_path, '__name_path'))
def test_manifest_path(self): def test_manifest_path(self):
base_path = fixtures base_path = fixtures
cdist_type = core.Type(base_path, '__name_path') cdist_type = core.CdistType(base_path, '__name_path')
self.assertEqual(cdist_type.manifest_path, os.path.join('__name_path', 'manifest')) self.assertEqual(cdist_type.manifest_path, os.path.join('__name_path', 'manifest'))
def test_explorer_path(self): def test_explorer_path(self):
base_path = fixtures base_path = fixtures
cdist_type = core.Type(base_path, '__name_path') cdist_type = core.CdistType(base_path, '__name_path')
self.assertEqual(cdist_type.explorer_path, os.path.join('__name_path', 'explorer')) self.assertEqual(cdist_type.explorer_path, os.path.join('__name_path', 'explorer'))
def test_gencode_local_path(self): def test_gencode_local_path(self):
base_path = fixtures base_path = fixtures
cdist_type = core.Type(base_path, '__name_path') cdist_type = core.CdistType(base_path, '__name_path')
self.assertEqual(cdist_type.gencode_local_path, os.path.join('__name_path', 'gencode-local')) self.assertEqual(cdist_type.gencode_local_path, os.path.join('__name_path', 'gencode-local'))
def test_gencode_remote_path(self): def test_gencode_remote_path(self):
base_path = fixtures base_path = fixtures
cdist_type = core.Type(base_path, '__name_path') cdist_type = core.CdistType(base_path, '__name_path')
self.assertEqual(cdist_type.gencode_remote_path, os.path.join('__name_path', 'gencode-remote')) self.assertEqual(cdist_type.gencode_remote_path, os.path.join('__name_path', 'gencode-remote'))
def test_singleton_is_singleton(self): def test_singleton_is_singleton(self):
base_path = fixtures base_path = fixtures
cdist_type = core.Type(base_path, '__singleton') cdist_type = core.CdistType(base_path, '__singleton')
self.assertTrue(cdist_type.is_singleton) self.assertTrue(cdist_type.is_singleton)
def test_not_singleton_is_singleton(self): def test_not_singleton_is_singleton(self):
base_path = fixtures base_path = fixtures
cdist_type = core.Type(base_path, '__not_singleton') cdist_type = core.CdistType(base_path, '__not_singleton')
self.assertFalse(cdist_type.is_singleton) self.assertFalse(cdist_type.is_singleton)
def test_install_is_install(self): def test_install_is_install(self):
base_path = fixtures base_path = fixtures
cdist_type = core.Type(base_path, '__install') cdist_type = core.CdistType(base_path, '__install')
self.assertTrue(cdist_type.is_install) self.assertTrue(cdist_type.is_install)
def test_not_install_is_install(self): def test_not_install_is_install(self):
base_path = fixtures base_path = fixtures
cdist_type = core.Type(base_path, '__not_install') cdist_type = core.CdistType(base_path, '__not_install')
self.assertFalse(cdist_type.is_install) self.assertFalse(cdist_type.is_install)
def test_with_explorers(self): def test_with_explorers(self):
base_path = fixtures base_path = fixtures
cdist_type = core.Type(base_path, '__with_explorers') cdist_type = core.CdistType(base_path, '__with_explorers')
self.assertEqual(cdist_type.explorers, ['whatever']) self.assertEqual(cdist_type.explorers, ['whatever'])
def test_without_explorers(self): def test_without_explorers(self):
base_path = fixtures base_path = fixtures
cdist_type = core.Type(base_path, '__without_explorers') cdist_type = core.CdistType(base_path, '__without_explorers')
self.assertEqual(cdist_type.explorers, []) self.assertEqual(cdist_type.explorers, [])
def test_with_required_parameters(self): def test_with_required_parameters(self):
base_path = fixtures base_path = fixtures
cdist_type = core.Type(base_path, '__with_required_parameters') cdist_type = core.CdistType(base_path, '__with_required_parameters')
self.assertEqual(cdist_type.required_parameters, ['required1', 'required2']) self.assertEqual(cdist_type.required_parameters, ['required1', 'required2'])
def test_without_required_parameters(self): def test_without_required_parameters(self):
base_path = fixtures base_path = fixtures
cdist_type = core.Type(base_path, '__without_required_parameters') cdist_type = core.CdistType(base_path, '__without_required_parameters')
self.assertEqual(cdist_type.required_parameters, []) self.assertEqual(cdist_type.required_parameters, [])
def test_with_optional_parameters(self): def test_with_optional_parameters(self):
base_path = fixtures base_path = fixtures
cdist_type = core.Type(base_path, '__with_optional_parameters') cdist_type = core.CdistType(base_path, '__with_optional_parameters')
self.assertEqual(cdist_type.optional_parameters, ['optional1', 'optional2']) self.assertEqual(cdist_type.optional_parameters, ['optional1', 'optional2'])
def test_without_optional_parameters(self): def test_without_optional_parameters(self):
base_path = fixtures base_path = fixtures
cdist_type = core.Type(base_path, '__without_optional_parameters') cdist_type = core.CdistType(base_path, '__without_optional_parameters')
self.assertEqual(cdist_type.optional_parameters, []) self.assertEqual(cdist_type.optional_parameters, [])

View file

@ -0,0 +1,22 @@
Description:
Type that supports transfer of huge data, which is a general problem in
configuration management systems.
Good solution using standardised rsync approach.
Problem:
Uses root@$__target_host:$destination notation for transfer.
This breaks the concept of being able to replace __remote_exec and
__remote_copy and then doing chroot or different stuff.
This breaks for instance, if __remote_copy = cp and the destination is
a local chroot.
Solutions:
- Have cdist provide support for rsync syntax?
- Integrate __rsyncer more in line with philosohpy of other components
- Think about the general way of __rsyncer and what cdist would need
to provide for general solution.

View file

@ -0,0 +1,48 @@
#!/bin/sh
#
# Copyright (C) 2011 Daniel Maher (phrawzty+cdist at gmail.com)
#
# This file is part of cdist (https://github.com/telmich/cdist/).
#
# cdist is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# cdist is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see <http://www.gnu.org/licenses/>.
#
source="$(cat "$__object/parameter/source")"
if [ -f "$__object/parameter/destination" ]; then
destination="$(cat "$__object/parameter/destination")"
else
destination="/$__object_id"
fi
# The system binary is probably ok, but if not...
if [ -f "$__object/parameter/rsyncbin" ]; then
rsyncbin="$(cat "$__object/parameter/rsyncbin")"
else
rsyncbin=`which rsync`
fi
args='-a'
# If the --delete argument should be passed to rsync.
if [ -f "$__object/parameter/delete" ]; then
args="$args --delete"
fi
# FIXME:
# - using root@ may break - find a good way to avoid this
# - align with __remote_{exec,copy} variables?
# Run rsync (locally).
echo "$rsyncbin $args $source root@$__target_host:$destination"

View file

@ -0,0 +1,62 @@
cdist-type__rsyncer(7)
======================
Daniel Maher <phrawzty+cdist at gmail.com>
NAME
----
cdist-type__rsyncer - Use rsync to copy files.
DESCRIPTION
-----------
This type is used to trigger rsync to copy files from the machine running cdist
(source) to the target machine in question (destination). The likely usage is
the rapid deployment of full directory trees, the cohorency of which can be
guarunteed with the optional --delete argument, which will remove any files
from the destination which are not present on the source.
REQUIRED PARAMETERS
-------------------
source::
The full path of the source from which to copy. This is passed directly
to rsync.
OPTIONAL PARAMETERS
-------------------
destination::
The full path of the destination. This is passed directly to rsync.
Default: object_id
delete::
If true, remove files from destination which are not in source. This is
effectively the --delete argument of rsync.
Default: false
rsyncbin::
Specify the full path to the rsync binary.
Default: `which rsync`
EXAMPLES
--------
--------------------------------------------------------------------------------
# Basic example
__rsyncer '/home/foo' --source '/opt/dist/foo'
# Fancier example
__rsyncer FOO --source '/opt/dist/foo' --destination '/home/foo/' --delete true
--------------------------------------------------------------------------------
SEE ALSO
--------
- cdist-type(7)
COPYING
-------
Copyright \(C) 2011 Daniel Maher. Free use of this software is granted under
the terms of the GNU General Public License version 3 (GPLv3).

View file

@ -0,0 +1,3 @@
destination
delete
rsyncbin

View file

@ -0,0 +1 @@
source