Merge branch 'master' into __package_absent_present

This commit is contained in:
Nico Schottelius 2012-02-16 16:35:47 +01:00
commit 605eaeb039
51 changed files with 1002 additions and 427 deletions

2
README
View file

@ -89,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***).

8
build
View file

@ -85,7 +85,7 @@ case "$1" in
;; ;;
release) release)
"$0" clean && "$0" man && "$0" web ./doc/dev/releasechecklist
;; ;;
speeches) speeches)
@ -113,8 +113,8 @@ case "$1" in
# cp ${SPEECHESDIR}/*.pdf ${WEBDIR}/${WEBBASE}/speeches # cp ${SPEECHESDIR}/*.pdf ${WEBDIR}/${WEBBASE}/speeches
# git describe > ${WEBDIR}/${WEBBASE}/man/VERSION # git describe > ${WEBDIR}/${WEBBASE}/man/VERSION
cp ${MAN1DSTDIR}/*.html ${WEBMAN}/man1 cp ${MAN1DSTDIR}/*.html ${MAN1DSTDIR}/*.css ${WEBMAN}/man1
cp ${MAN7DSTDIR}/*.html ${WEBMAN}/man7 cp ${MAN7DSTDIR}/*.html ${MAN7DSTDIR}/*.css ${WEBMAN}/man7
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}
@ -123,7 +123,7 @@ case "$1" in
# Fix ikiwiki, which does not like symlinks for pseudo security # Fix ikiwiki, which does not like symlinks for pseudo security
ssh tee.schottelius.org \ ssh tee.schottelius.org \
"cd /home/services/www/nico/www.nico.schottelius.org/www/software/cdist/man && "cd /home/services/www/nico/www.nico.schottelius.org/www/software/cdist/man &&
ln -sf "$version" latest" rm -f latest && ln -sf "$version" latest"
;; ;;
p|pu|pub) p|pu|pub)

26
conf/explorer/runlevel Executable file
View file

@ -0,0 +1,26 @@
#!/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/>.
#
#
set +e
executable=$(which runlevel 2>/dev/null)
if [ -x "$executable" ]; then
"$executable" | awk '{ print $2 }'
fi

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

View file

@ -35,14 +35,15 @@ else
pip="pip" pip="pip"
fi fi
# which is not posix, but command is :-) # 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 if ! command -v "$pip" >/dev/null 2>&1; then
echo "No usable pip found at path \"$pip\"" >&2
exit 1
fi
if "$pip" freeze | grep -i -q "^$name=="; then
echo present
else
echo absent echo absent
else
if "$pip" freeze | grep -i -q "^$name=="; then
echo present
else
echo absent
fi
fi fi

View file

@ -1,5 +1,5 @@
cdist-type__package_pip(7) cdist-type__package_pip(7)
============================= ==========================
Nico Schottelius <nico-cdist--@--schottelius.org> Nico Schottelius <nico-cdist--@--schottelius.org>

View file

@ -0,0 +1,64 @@
#!/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/>.
#
#
# Check whether the given name will be started on boot or not
#
os=$("$__explorer/os")
runlevel=$("$__explorer/runlevel")
name="$__object_id"
case "$os" in
archlinux)
# convert bash array to shell
daemons=$(grep ^DAEMONS /etc/rc.conf | sed -e 's/^.*=(//' -e 's/)$//')
# absent, as long as not found
state="absent"
# iterate, last one wins.
for daemon in $daemons; do
if [ "$daemon" = "$name" -o "$daemon" = "@${name}" ]; then
state="present"
elif [ "$daemon" = "!${name}" ]; then
state="absent"
fi
done
;;
debian|ubuntu)
state="present"
[ -f "/etc/rc$runlevel.d/S"??"$name" ] || state="absent"
;;
centos|fedora|owl|redhat)
state="present"
state=$(chkconfig --level "$runlevel" \"$name\" || echo absent)
[ "$state" ] || state="present"
;;
*)
echo "Unsupported os: $os" >&2
exit 1
;;
esac
echo $state

View file

@ -0,0 +1,89 @@
#!/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/>.
#
#
state_should="$(cat "$__object/parameter/state")"
state_is=$(cat "$__object/explorer/state")
# Nothing todo, go away
[ "$state_should" = "$state_is" ] && exit 0
os=$(cat "$__global/explorer/os")
name="$__object_id"
case "$state_should" in
present)
case "$os" in
archlinux)
echo "sed -i /etc/rc.conf \'s/^\\(DAEMONS=.*\\))/\\1 $name)/\'"
;;
debian|ubuntu)
echo "update-rc.d \"$name\" defaults >/dev/null"
;;
# FIXME: Disabled until the explorer is checked
# gentoo)
# echo rc-update add \"$name\" default
# ;;
centos|fedora|owl|redhat)
echo chkconfig \"$name\" on
;;
*)
echo "Unsupported os: $os" >&2
exit 1
;;
esac
;;
absent)
case "$os" in
archlinux)
# Replace a) at the beginning b) in the middle c) end d) only
# Support @name as well...makes it more ugly, but well...
echo "sed -i /etc/rc.conf -e 's/^\\(DAEMONS=(\\)@\\{0,1\\}$name /\\1/' -e 's/^\\(DAEMONS=(.* \\)@\\{0,1\\}$name \\(.*\\)/\\1\\2/' -e 's/^\\(DAEMONS=(.*\\) @\\{0,1\\}$name)/\\1)/' -e 's/^\\(DAEMONS=(\\)@\\{0,1\\}$name)/\\1)/'"
;;
debian|ubuntu)
echo update-rc.d -f \"$name\" remove
;;
# FIXME: Disabled until the explorer is checked
# gentoo)
# echo rc-update del \"$name\"
# ;;
centos|fedora|owl|redhat)
echo chkconfig \"$name\" off
;;
*)
echo "Unsupported os: $os" >&2
exit 1
;;
esac
;;
*)
echo "Unknown state: $state_should" >&2
exit 1
;;
esac

View file

@ -0,0 +1,53 @@
cdist-type__start_on_boot(7)
============================
Nico Schottelius <nico-cdist--@--schottelius.org>
NAME
----
cdist-type__start_on_boot - Manage stuff to be started at boot
DESCRIPTION
-----------
This cdist type allows you to enable or disable stuff to be started
at boot of your operating system.
Warning: This type has not been tested intensively and is not fully
supported (i.e. gentoo and *bsd are not implemented).
REQUIRED PARAMETERS
-------------------
None.
OPTIONAL PARAMETERS
-------------------
state::
'present' or 'absent', defaults to 'present'
EXAMPLES
--------
--------------------------------------------------------------------------------
# Ensure snmpd is started at boot
__start_on_boot snmpd
# Same, but more explicit
__start_on_boot snmpd --state present
# Ensure legacy configuration management will not be started
__start_on_boot puppet --state absent
--------------------------------------------------------------------------------
SEE ALSO
--------
- cdist-type(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

@ -1,6 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# 2011 Steven Armstrong (steven-cdist at armstrong.cc) # 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.
# #
@ -17,21 +18,7 @@
# 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/>.
# #
#
# Get the current value of key or __NOTSET__ if the key doesn't exist.
#
if [ -f "$__object/parameter/key" ]; then
key="$(cat "$__object/parameter/key")"
else
key="$__object_id"
fi
file="$(cat "$__object/parameter/file")"
delimiter="$(cat "$__object/parameter/delimiter")"
awk -F "$delimiter" '
BEGIN { found=0 }
/^'$key'/ { print $2; found=1 }
END { if (found) exit 0; else exit 1 }' "$file" \
|| echo "__NOTSET__"
# set default: present, if not setup
statefile="$__object/parameter/state"
[ -f "$statefile" ] || echo present > "$statefile"

View file

@ -0,0 +1 @@
state

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,9 +37,16 @@ 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)
@ -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

@ -4,11 +4,22 @@ Changelog
* Changes are always commented with their author in (braces) * Changes are always commented with their author in (braces)
* Exception: No braces means author == Nico Schottelius * Exception: No braces means author == Nico Schottelius
2.0.7: 2.0.8:
* Cleanup: Better hint to source of error
* Cleanup: Do not output failing script, but path to script only
* Cleanup: Remove support for __debug variable in manifests (Type != Core
debugging)
* Feature Core: Support boolean parameters (Steven Armstrong)
2.0.7: 2012-02-13
* Bugfix __file: Use chmod after chown/chgrp (Matt Coddington) * Bugfix __file: Use chmod after chown/chgrp (Matt Coddington)
* Bugfix __user: Correct shadow field in explorer (Matt Coddington) * Bugfix __user: Correct shadow field in explorer (Matt Coddington)
* Bugfix __link: Properly handle existing links (Steven Armstrong) * 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 * New Type: __package_pip
* Bugfix/Cleanup: Correctly allow Object ID to start and end with /, but
not contain //.
2.0.6: 2012-01-28 2.0.6: 2012-01-28
* Bugfix __apt_ppa: * Bugfix __apt_ppa:

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

@ -0,0 +1,132 @@
- parameter/setting default from manifest
==> BRANCH[feature_default_parameters],
==> PERSON[Steven or Nico]
==> PROPOSAL(1)
- current bug
- proposal 1: parameter/default/$name (for optional ones)
- new way
- catches --state absent|present
- needs changes of types
- also possible for explorer
- support for it in core?
- handling of ${o} $o "$o" ?
- handling which variables?
- introduction of "templating language"
- aka macros
- possible problems:
- inconsistency
- redoing shell functionality
- raising expectations for more templating from users
- possible benefit
- no need for eval
- once in core, not everytime in type
- OTOH: one extra word.
- a=$(cat $__object/parameter/name) vs. $(eval $(cat $__object/parameter/name))
- only possible for static defaults
- --name overrides name not possible vs. object_id
- Is this the only case????
- if yes: don't care.
- possible solution:
- echo '/$__object_id' > typename/parameter/default/name
- eval $(cat $__object/parameter/name)
- probably allows code injection
- is possible anyway???
- $(cat /etc/shadow)
- other eval side effects???
- none: go for it
- some: have headache
- many: don't do
- proposal 2: 2 dbs (user input vs. stuff changable by type)
- explicit 2nd db [parameter_user and parameter/]
- not very clean (both agreed)
- proposal 3: parameter are read-only
- breaks current types (in core probably elsewhere)
- can't enforce, but user is on his own => breaks, her problem
+ clean seperation between core and type (nico)
- parameter belongs to type not core (steven)
- proposal 4: core ignores changes in parameter/* of object
- implicit 2nd db [see automagic below]
- steven+++
- does not work with divergent emulator not being in core
- because emulators primary db __is__ fs.
1 manifest:
__foo bar == emulator
echo present > $__global/object/__foo/bar/parameter/state
# fails
__foo bar == emulator
! automagic / filesystem
! fsproperty:
- kill, write explicitly to disk
==> BRANCH[cleanup_fsproperty]
==> PERSON[Steven]
==> PROPOSAL(just cleanup)
- implicit/automatic writes/read to fs
- explicit interfaces are better then implicit
- same problems as in cdist 1.x to 2.x move! (environment!)
- format on disk should not change/dictate code flow
- degrade python to shell (nico++! steven--)
- user should not care about python, ruby, .net or ASM implementation (steven++ nico++)
? proposal 1: diverge emulator / core
- emulator verifies input
- emulator writes to fs
- core reads/syncs from/to fs before passing control to user
? proposal 2: emulator is dumb and passes data to core
- core creates objects
- no fs involved
- core reads/syncs from/to fs before passing control to user
- passing:
- full objects via pickle
- parameters only
- how???
- unix socket?
- not everywhere possible?
- tcp / ip
- not everywhere possible
- chroot / local only
- rfc 1149
- not everywhere possible
- missing avian carriers
- 0mq
- not everywhere possible
- not installed
- shm (ipcs and friends)
- not everywhere possible
- no /dev/shm, different libraries? cleanups needed...
- what speaks against FS?
- emulator_input/.../
- nico: to fancy probably
! boolean implementation
==> BRANCH[feature_boolean_parameter]
==> PERSON[Steven]
- nico:
- parameters/boolean: document
- argparse changes (consider parameters/boolean)
- create
- can be implemented with changes in emulator
- use store_true, del if false => never seen by core
- INDEPENDENT INDEPENDT OF FS.PROPERTIES!!111111!
- emulator:
- how much integrated into core
- also: using CdistObject????
- dependency on filesystem: good (nico) | bad (steven)
- singleton / support without object_id
- not discussed
- __apt_ppa:
==> BRANCH[bugfix_do_not_change_state_in_manifest]
==> PERSON[Nico]
- logging divergent between emulator / core
- no problem (nico)
- may be helpful (steven)

View file

@ -15,11 +15,21 @@ 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

@ -35,3 +35,5 @@ USER INTERFACE
TYPES TYPES
------ ------
- Add testing framework (proposed by Evax Software) - Add testing framework (proposed by Evax Software)
- __user
add option to include --create-home

View file

@ -1,11 +1,13 @@
- __file/foo/bar//bar - introduce default parameters
- fails later
- cleanup object_id handling
- have a look at singletons
- __user
add option to include --create-home
- ensure that all types, which support --state support - ensure that all types, which support --state support
present and absent (consistent look and feel) present and absent (consistent look and feel)
--------------------------------------------------------------------------------
- update/create docs - update/create docs
- 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]

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,80 @@ 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>/parameter/required::
Parameters required by type, \n seperated list. Parameters required by type, \n seperated list.
conf/type/<name>/parameters/optional:: conf/type/<name>/parameter/optional::
Parameters optionally accepted by type, \n seperated list. Parameters optionally accepted by type, \n seperated list.
conf/type/<name>/parameter/boolean::
Boolean parameters 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 +144,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,43 +162,47 @@ 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
--------------------- ---------------------
__explorer:: __explorer::
Directory that contains all global explorers. Directory that contains all global explorers.
Available for: 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: initial manifest, 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

@ -72,15 +72,42 @@ To begin a new type, just create the directory **conf/type/__NAME**.
DEFINING PARAMETERS DEFINING PARAMETERS
------------------- -------------------
Every type consists of optional and required parameters, which must Every type consists of required, optional and boolean parameters, which must
be created in a newline seperated file in ***parameters/required*** and be created in a newline seperated file in ***parameter/required***,
***parameters/optional***. If either or both missing, the type will have ***parameter/optional*** and ***parameter/boolean***. If either is missing,
no required, no optional or no parameters at all. the type will have no required, no optional, no boolean or no parameters at
all.
Example: Example:
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
echo servername >> conf/type/__nginx_vhost/parameter/required echo servername >> conf/type/__nginx_vhost/parameter/required
echo logdirectory >> conf/type/__nginx_vhost/parameter/optional echo logdirectory >> conf/type/__nginx_vhost/parameter/optional
echo use_ssl >> conf/type/__nginx_vhost/parameter/boolean
--------------------------------------------------------------------------------
USING PARAMETERS
----------------
The parameters given to a type can be accessed and used in all type scripts
(e.g manifest, gencode-*, explorer/*). Note that boolean parameters are
represented by file existence. File exists -> True,
file does not exist -> False
Example: (e.g. in conf/type/__nginx_vhost/manifest)
--------------------------------------------------------------------------------
# required parameter
servername="$(cat "$__object/parameter/servername")"
# optional parameter
if [ -f "$__object/parameter/logdirectory" ]; then
logdirectory="$(cat "$__object/parameter/logdirectory")"
fi
# boolean parameter
if [ -f "$__object/parameter/use_ssl" ]; then
# file exists -> True
# do some fancy ssl stuff
fi
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------

View file

@ -19,7 +19,7 @@
# #
# #
VERSION = "2.0.6" VERSION = "2.0.7"
BANNER = """ BANNER = """
.. . .x+=:. s .. . .x+=:. s
@ -44,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"""
class MissingEnvironmentVariableError(Error): def __init__(self, cdist_object, message):
"""Raised when a required environment variable is not set.""" self.name = cdist_object.name
self.source = " ".join(cdist_object.source)
self.message = message
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]
@ -82,9 +82,10 @@ class Type(object):
self.__explorers = None self.__explorers = None
self.__required_parameters = None self.__required_parameters = None
self.__optional_parameters = None self.__optional_parameters = None
self.__boolean_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
@ -144,3 +145,19 @@ class Type(object):
finally: finally:
self.__optional_parameters = parameters self.__optional_parameters = parameters
return self.__optional_parameters return self.__optional_parameters
@property
def boolean_parameters(self):
"""Return a list of boolean parameters"""
if not self.__boolean_parameters:
parameters = []
try:
with open(os.path.join(self.absolute_path, "parameter", "boolean")) as fd:
for line in fd:
parameters.append(line.strip())
except EnvironmentError:
# error ignored
pass
finally:
self.__boolean_parameters = parameters
return self.__boolean_parameters

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,8 +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,
} }
if self.log.getEffectiveLevel() == logging.DEBUG:
self.env.update({'__debug': "yes" })
self._type_explorers_transferred = [] self._type_explorers_transferred = []
### global ### global
@ -121,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:
@ -149,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()
@ -94,7 +87,7 @@ class Emulator(object):
def commandline(self): def commandline(self):
"""Parse command line""" """Parse command line"""
parser = argparse.ArgumentParser(add_help=False) parser = argparse.ArgumentParser(add_help=False, argument_default=argparse.SUPPRESS)
for parameter in self.cdist_type.optional_parameters: for parameter in self.cdist_type.optional_parameters:
argument = "--" + parameter argument = "--" + parameter
@ -102,6 +95,9 @@ class Emulator(object):
for parameter in self.cdist_type.required_parameters: for parameter in self.cdist_type.required_parameters:
argument = "--" + parameter argument = "--" + parameter
parser.add_argument(argument, dest=parameter, action='store', required=True) parser.add_argument(argument, dest=parameter, action='store', required=True)
for parameter in self.cdist_type.boolean_parameters:
argument = "--" + parameter
parser.add_argument(argument, dest=parameter, action='store_const', const='')
# If not singleton support one positional parameter # If not singleton support one positional parameter
if not self.cdist_type.is_singleton: if not self.cdist_type.is_singleton:
@ -113,20 +109,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 = {}
@ -137,12 +128,15 @@ class Emulator(object):
if self.cdist_object.exists: if self.cdist_object.exists:
if self.cdist_object.parameters != self.parameters: if self.cdist_object.parameters != self.parameters:
raise cdist.Error("Object %s already exists with conflicting parameters:\n%s: %s\n%s: %s" raise cdist.Error("Object %s already exists with conflicting parameters:\n%s: %s\n%s: %s"
% (self.cdist_object, " ".join(self.cdist_object.source), self.cdist_object.parameters, self.object_source, self.parameters) % (self.cdist_object.name, " ".join(self.cdist_object.source), self.cdist_object.parameters, self.object_source, self.parameters)
) )
else: else:
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 +145,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

@ -32,18 +32,6 @@ import logging
import cdist import cdist
from cdist import core from cdist import core
class LocalScriptError(cdist.Error):
def __init__(self, script, command, script_content):
self.script = script
self.command = command
self.script_content = script_content
def __str__(self):
plain_command = " ".join(self.command)
return "Local script execution failed: %s" % plain_command
class Local(object): class Local(object):
"""Execute commands locally. """Execute commands locally.
@ -119,35 +107,16 @@ class Local(object):
command = ["/bin/sh", "-e"] command = ["/bin/sh", "-e"]
command.append(script) command.append(script)
self.log.debug("Local run script: %s", command) return self.run(command, env, return_output)
if env is None:
env = os.environ.copy()
# Export __target_host for use in __remote_{copy,exec} scripts
env['__target_host'] = self.target_host
self.log.debug("Local run script env: %s", env)
try:
if return_output:
return subprocess.check_output(command, env=env).decode()
else:
subprocess.check_call(command, env=env)
except subprocess.CalledProcessError as error:
script_content = self.run(["cat", script], return_output=True)
self.log.error("Code that raised the error:\n%s", script_content)
raise LocalScriptError(script, command, script_content)
except EnvironmentError as error:
raise cdist.Error(" ".join(command) + ": " + error.args[1])
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)
try: try:
os.symlink(src, dst) os.symlink(src, dst)
except OSError as e: except OSError as e:
raise cdist.Error("Linking emulator from " + src + " to " + dst + " failed: " + e.__str__()) raise cdist.Error("Linking emulator from %s to %s failed: %s" % (src, dst, e.__str__()))

View file

@ -20,8 +20,6 @@
# #
# #
# FIXME: common base class with Local?
import io import io
import os import os
import sys import sys
@ -30,17 +28,6 @@ import logging
import cdist import cdist
class RemoteScriptError(cdist.Error):
def __init__(self, script, command, script_content):
self.script = script
self.command = command
self.script_content = script_content
def __str__(self):
plain_command = " ".join(self.command)
return "Remote script execution failed: %s" % plain_command
class DecodeError(cdist.Error): class DecodeError(cdist.Error):
def __init__(self, command): def __init__(self, command):
self.command = command self.command = command
@ -93,6 +80,17 @@ class Remote(object):
command.extend(["-r", source, self.target_host + ":" + destination]) command.extend(["-r", source, self.target_host + ":" + destination])
self._run_command(command) self._run_command(command)
def run_script(self, script, env=None, return_output=False):
"""Run the given script with the given environment on the remote side.
Return the output as a string.
"""
command = ["/bin/sh", "-e"]
command.append(script)
return self.run(command, env, return_output)
def run(self, command, env=None, return_output=False): def run(self, command, env=None, return_output=False):
"""Run the given command with the given environment on the remote side. """Run the given command with the given environment on the remote side.
Return the output as a string. Return the output as a string.
@ -101,7 +99,15 @@ class Remote(object):
# prefix given command with remote_exec # prefix given command with remote_exec
cmd = self._exec.split() cmd = self._exec.split()
cmd.append(self.target_host) cmd.append(self.target_host)
# can't pass environment to remote side, so prepend command with
# variable declarations
if env:
remote_env = ["%s=%s" % item for item in env.items()]
cmd.extend(remote_env)
cmd.extend(command) cmd.extend(command)
return self._run_command(cmd, env=env, return_output=return_output) return self._run_command(cmd, env=env, return_output=return_output)
def _run_command(self, command, env=None, return_output=False): def _run_command(self, command, env=None, return_output=False):
@ -115,14 +121,6 @@ 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
# can't pass environment to remote side, so prepend command with
# variable declarations
if env:
cmd = ["%s=%s" % item for item in env.items()]
cmd.extend(command)
else:
cmd = command
self.log.debug("Remote run: %s", command) self.log.debug("Remote run: %s", command)
try: try:
if return_output: if return_output:
@ -135,39 +133,3 @@ class Remote(object):
raise cdist.Error(" ".join(*args) + ": " + error.args[1]) raise cdist.Error(" ".join(*args) + ": " + error.args[1])
except UnicodeDecodeError: except UnicodeDecodeError:
raise DecodeError(command) raise DecodeError(command)
def run_script(self, script, env=None, return_output=False):
"""Run the given script with the given environment on the remote side.
Return the output as a string.
"""
command = self._exec.split()
command.append(self.target_host)
# export target_host for use in __remote_{exec,copy} scripts
os_environ = os.environ.copy()
os_environ['__target_host'] = self.target_host
# can't pass environment to remote side, so prepend command with
# variable declarations
if env:
command.extend(["%s=%s" % item for item in env.items()])
command.extend(["/bin/sh", "-e"])
command.append(script)
self.log.debug("Remote run script: %s", command)
if env:
self.log.debug("Remote run script env: %s", env)
try:
if return_output:
return subprocess.check_output(command, env=os_environ).decode()
else:
subprocess.check_call(command, env=os_environ)
except subprocess.CalledProcessError as error:
script_content = self.run(["cat", script], return_output=True)
self.log.error("Code that raised the error:\n%s", script_content)
raise RemoteScriptError(script, command, script_content)
except EnvironmentError as error:
raise cdist.Error(" ".join(command) + ": " + error.args[1])

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,14 +119,14 @@ 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))
class ArgumentsWithDashesTestCase(test.CdistTestCase): class ArgumentsTestCase(test.CdistTestCase):
def setUp(self): def setUp(self):
self.temp_dir = self.mkdtemp() self.temp_dir = self.mkdtemp()
@ -156,6 +156,62 @@ 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)
def test_boolean(self):
type_name = '__arguments_boolean'
object_id = 'some-id'
argv = [type_name, object_id, '--boolean1']
os.environ.update(self.env)
emu = emulator.Emulator(argv)
emu.run()
cdist_type = core.CdistType(self.local.type_path, type_name)
cdist_object = core.CdistObject(cdist_type, self.local.object_path, object_id)
self.assertTrue('boolean1' in cdist_object.parameters)
self.assertFalse('boolean2' in cdist_object.parameters)
# empty file -> True
self.assertTrue(cdist_object.parameters['boolean1'] == '')
def test_required(self):
type_name = '__arguments_required'
object_id = 'some-id'
value = 'some value'
argv = [type_name, object_id, '--required1', value, '--required2', value]
os.environ.update(self.env)
emu = emulator.Emulator(argv)
emu.run()
cdist_type = core.CdistType(self.local.type_path, type_name)
cdist_object = core.CdistObject(cdist_type, self.local.object_path, object_id)
self.assertTrue('required1' in cdist_object.parameters)
self.assertTrue('required2' in cdist_object.parameters)
self.assertEqual(cdist_object.parameters['required1'], value)
self.assertEqual(cdist_object.parameters['required2'], value)
# def test_required_missing(self):
# type_name = '__arguments_required'
# object_id = 'some-id'
# value = 'some value'
# argv = [type_name, object_id, '--required1', value]
# os.environ.update(self.env)
# emu = emulator.Emulator(argv)
#
# self.assertRaises(SystemExit, emu.run)
def test_optional(self):
type_name = '__arguments_optional'
object_id = 'some-id'
value = 'some value'
argv = [type_name, object_id, '--optional1', value]
os.environ.update(self.env)
emu = emulator.Emulator(argv)
emu.run()
cdist_type = core.CdistType(self.local.type_path, type_name)
cdist_object = core.CdistObject(cdist_type, self.local.object_path, object_id)
self.assertTrue('optional1' in cdist_object.parameters)
self.assertFalse('optional2' in cdist_object.parameters)
self.assertEqual(cdist_object.parameters['optional1'], value)

View file

@ -0,0 +1,2 @@
boolean1
boolean2

View file

@ -0,0 +1,2 @@
required1
required2

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,126 @@ 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, [])
def test_with_boolean_parameters(self):
base_path = fixtures
cdist_type = core.CdistType(base_path, '__with_boolean_parameters')
self.assertEqual(cdist_type.boolean_parameters, ['boolean1', 'boolean2'])
def test_without_boolean_parameters(self):
base_path = fixtures
cdist_type = core.CdistType(base_path, '__without_boolean_parameters')
self.assertEqual(cdist_type.boolean_parameters, [])

View file

@ -0,0 +1,2 @@
boolean1
boolean2