diff --git a/conf/type/__process/explorer/runs b/conf/type/__process/explorer/runs index 2bfa8f84..240ebef9 100755 --- a/conf/type/__process/explorer/runs +++ b/conf/type/__process/explorer/runs @@ -24,7 +24,7 @@ if [ -f "$__object/parameter/name" ]; then name="$(cat "$__object/parameter/name")" else - name="/$__object_id" + name="$__object_id" fi pgrep -x -f "$name" || true diff --git a/conf/type/__process/man.text b/conf/type/__process/man.text index fd3bcf49..065beeef 100644 --- a/conf/type/__process/man.text +++ b/conf/type/__process/man.text @@ -50,6 +50,10 @@ __process /usr/sbin/sshd --state stopped --stop "/etc/rc.d/sshd stop" # Ensure cups is running, which runs with -C ...: __process cups --start "/etc/rc.d/cups start" --state running \ --name "/usr/sbin/cupsd -C /etc/cups/cupsd.conf" + +# Ensure rpc.statd is running (which usually runs with -L) using a regexp +__process rpcstatd --state running --start "/etc/init.d/statd start" \ + --name "rpc.statd.*" -------------------------------------------------------------------------------- diff --git a/doc/changelog b/doc/changelog index aad067b7..7217a6f2 100644 --- a/doc/changelog +++ b/doc/changelog @@ -3,6 +3,7 @@ * Cleanup: __object_fq variable removed (never used) * Cleanup: Environment variable __self DEPRECATED, use __object_name instead * Cleanup: Environment variable __self scheduled for removal in cdist 2.1 + * New Type: __cron (Steven Armstrong) 2.0.3: 2011-10-18 * Improved logging, added --verbose, by more quiet by default diff --git a/lib/cdist/config_install.py b/lib/cdist/config_install.py index 75b07e78..2d2ab949 100644 --- a/lib/cdist/config_install.py +++ b/lib/cdist/config_install.py @@ -71,7 +71,7 @@ class ConfigInstall(object): start_time = time.time() self.deploy_to() self.cleanup() - self.log.info("Finished run in %s seconds", + self.log.info("Finished successful run in %s seconds", time.time() - start_time) def stage_prepare(self): @@ -106,7 +106,7 @@ class ConfigInstall(object): """Run gencode and code for an object""" self.log.debug("Trying to run object " + cdist_object.name) if cdist_object.state == core.Object.STATE_RUNNING: - # FIXME: resolve dependency circle + # FIXME: resolve dependency circle / show problem source raise cdist.Error("Detected circular dependency in " + cdist_object.name) elif cdist_object.state == core.Object.STATE_DONE: self.log.debug("Ignoring run of already finished object %s", cdist_object) @@ -119,6 +119,13 @@ class ConfigInstall(object): for requirement in cdist_object.requirements: self.log.debug("Object %s requires %s", cdist_object, requirement) required_object = cdist_object.object_from_name(requirement) + + # The user may have created dependencies without satisfying them + if not required_object.exists: + raise cdist.Error(cdist_object.name + " requires non-existing " + required_object.name) + else: + self.log.debug("Required object %s exists", required_object.name) + self.object_run(required_object) # Generate diff --git a/lib/cdist/core/__init__.py b/lib/cdist/core/__init__.py index ac5bbf2f..c61c659b 100644 --- a/lib/cdist/core/__init__.py +++ b/lib/cdist/core/__init__.py @@ -23,6 +23,7 @@ from cdist.core.type import Type from cdist.core.type import NoSuchTypeError from cdist.core.object import Object from cdist.core.object import IllegalObjectIdError +from cdist.core.object import OBJECT_MARKER from cdist.core.explorer import Explorer from cdist.core.manifest import Manifest from cdist.core.code import Code diff --git a/lib/cdist/core/object.py b/lib/cdist/core/object.py index 778bebe8..9abb11eb 100644 --- a/lib/cdist/core/object.py +++ b/lib/cdist/core/object.py @@ -30,7 +30,7 @@ from cdist.util import fsproperty log = logging.getLogger(__name__) -DOT_CDIST = '.cdist' +OBJECT_MARKER = '.cdist' class IllegalObjectIdError(cdist.Error): @@ -72,7 +72,7 @@ class Object(object): def list_object_names(cls, object_base_path): """Return a list of object names""" for path, dirs, files in os.walk(object_base_path): - if DOT_CDIST in dirs: + if OBJECT_MARKER in dirs: yield os.path.relpath(path, object_base_path) @staticmethod @@ -100,13 +100,13 @@ class Object(object): if object_id: if object_id.startswith('/'): raise IllegalObjectIdError(object_id, 'object_id may not start with /') - if '.cdist' in object_id: - raise IllegalObjectIdError(object_id, 'object_id may not contain \'.cdist\'') + if OBJECT_MARKER in object_id.split(os.sep): + raise IllegalObjectIdError(object_id, 'object_id may not contain \'%s\'' % OBJECT_MARKER) self.type = cdist_type # instance of Type self.base_path = base_path 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, DOT_CDIST) + self.path = os.path.join(self.type.path, self.object_id, OBJECT_MARKER) self.absolute_path = os.path.join(self.base_path, self.path) self.code_local_path = os.path.join(self.path, "code-local") self.code_remote_path = os.path.join(self.path, "code-remote") @@ -132,9 +132,9 @@ class Object(object): """ type_path = self.type.base_path - object_path = self.base_path + base_path = self.base_path type_name, object_id = self.split_name(object_name) - return self.__class__(self.type.__class__(type_path, type_name), object_path, object_id=object_id) + return self.__class__(self.type.__class__(type_path, type_name), base_path, object_id=object_id) # FIXME: still needed? @property diff --git a/lib/cdist/emulator.py b/lib/cdist/emulator.py index 481e734a..340acec3 100644 --- a/lib/cdist/emulator.py +++ b/lib/cdist/emulator.py @@ -157,19 +157,22 @@ class Emulator(object): self.log.debug("Recording requirement: " + requirement) requirement_parts = requirement.split(os.sep, 1) requirement_type_name = requirement_parts[0] - requirement_object_id = requirement_parts[1] - - # FIXME: Add support for omitted object id == singleton - #if len(requirement_parts) == 1: - #except IndexError: - # # no object id, must be singleton - # requirement_object_id = 'singleton' - - # Remove / if existent in object id + try: + requirement_object_id = requirement_parts[1] + except IndexError: + # no object id, assume singleton + requirement_object_id = 'singleton' + + # Remove leading / from object id requirement_object_id = requirement_object_id.lstrip('/') # Instantiate type which fails if type does not exist requirement_type = core.Type(self.type_base_path, requirement_type_name) + + if requirement_object_id == 'singleton' \ + and not requirement_type.is_singleton: + raise IllegalRequirementError(requirement, "Missing object_id and type is not a singleton.") + # Instantiate object which fails if the object_id is illegal requirement_object = core.Object(requirement_type, self.object_base_path, requirement_object_id) diff --git a/lib/cdist/test/emulator/__init__.py b/lib/cdist/test/emulator/__init__.py index 7b30fba1..0859ffea 100644 --- a/lib/cdist/test/emulator/__init__.py +++ b/lib/cdist/test/emulator/__init__.py @@ -74,6 +74,21 @@ class EmulatorTestCase(test.CdistTestCase): emu = emulator.Emulator(argv) self.assertRaises(core.IllegalObjectIdError, emu.run) + def test_missing_object_id_requirement(self): + argv = ['__file', '/tmp/foobar'] + os.environ.update(self.env) + os.environ['require'] = '__file' + emu = emulator.Emulator(argv) + self.assertRaises(emulator.IllegalRequirementError, emu.run) + + def test_singleton_object_requirement(self): + argv = ['__file', '/tmp/foobar'] + os.environ.update(self.env) + os.environ['require'] = '__issue' + emu = emulator.Emulator(argv) + emu.run() + # if we get here all is fine + import os.path as op my_dir = op.abspath(op.dirname(__file__)) diff --git a/lib/cdist/test/object/__init__.py b/lib/cdist/test/object/__init__.py index 6681c916..f199ffb5 100644 --- a/lib/cdist/test/object/__init__.py +++ b/lib/cdist/test/object/__init__.py @@ -58,12 +58,18 @@ class ObjectIdTestCase(test.CdistTestCase): with self.assertRaises(core.IllegalObjectIdError): core.Object(cdist_type, object_base_path, illegal_object_id) - def test_object_id_contains_dotcdist(self): + def test_object_id_contains_object_marker(self): cdist_type = core.Type(type_base_path, '__third') - illegal_object_id = 'object_id/may/not/contain/.cdist/anywhere' + illegal_object_id = 'object_id/may/not/contain/%s/anywhere' % core.OBJECT_MARKER with self.assertRaises(core.IllegalObjectIdError): core.Object(cdist_type, object_base_path, illegal_object_id) + def test_object_id_contains_object_marker_string(self): + cdist_type = core.Type(type_base_path, '__third') + illegal_object_id = 'object_id/may/contain_%s_in_filename' % core.OBJECT_MARKER + core.Object(cdist_type, object_base_path, illegal_object_id) + # if we get here, the test passed + class ObjectTestCase(test.CdistTestCase): diff --git a/other/examples/remote/README b/other/examples/remote/README new file mode 100644 index 00000000..288fc293 --- /dev/null +++ b/other/examples/remote/README @@ -0,0 +1,2 @@ +Some examples of using alternative __remote_copy and __remote_exec prefixes. +This allows you to change how cdist interacts with the target host (or directory, or whatever :-) diff --git a/other/examples/remote/chroot/copy b/other/examples/remote/chroot/copy new file mode 100755 index 00000000..528a5faf --- /dev/null +++ b/other/examples/remote/chroot/copy @@ -0,0 +1,47 @@ +#!/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 . +# +# +# __remote_copy script to run cdist against a local chroot instead of via ssh +# to a remote target host. +# +# Usage: +# __remote_copy="/path/to/this/script /path/to/your/chroot" cdist config target-id +# + +log() { + #echo "$@" | logger -t "cdist-chroot-copy" + : +} + +chroot="$1"; shift +target_host="$__target_host" + +# replace target_host with chroot location +code="$(echo "$@" | sed "s|$target_host:|$chroot|g")" + +log "target_host: $target_host" +log "chroot: $chroot" +log "$@" +log "$code" + +# copy files into chroot +cp $code + +log "-----" diff --git a/other/examples/remote/chroot/exec b/other/examples/remote/chroot/exec new file mode 100755 index 00000000..19e76b0e --- /dev/null +++ b/other/examples/remote/chroot/exec @@ -0,0 +1,55 @@ +#!/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 . +# +# +# __remote_exec script to run cdist against a local chroot instead of via ssh +# on a remote target host. +# +# Usage: +# __remote_exec="/path/to/this/script /path/to/your/chroot" cdist config target-id +# + +log() { + #echo "$@" | logger -t "cdist-chroot-exec" + : +} + +chroot="$1"; shift +target_host="$1"; shift + +script=$(mktemp "${chroot}/tmp/chroot-${0##*/}.XXXXXXXXXX") +trap cleanup INT TERM EXIT +cleanup() { + [ $__cdist_debug ] || rm "$script" +} + +log "target_host: $target_host" +log "script: $script" +log "@: $@" +echo "#!/bin/sh -l" > "$script" +echo "$@" >> "$script" +chmod +x "$script" + +relative_script="${script#$chroot}" +log "relative_script: $relative_script" + +# run in chroot +chroot "$chroot" "$relative_script" + +log "-----" diff --git a/other/examples/remote/schroot-uri b/other/examples/remote/schroot-uri new file mode 100755 index 00000000..06dce369 --- /dev/null +++ b/other/examples/remote/schroot-uri @@ -0,0 +1,132 @@ +#!/bin/sh -e +# +# 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 . +# +# +# __remote_{exec,copy} script to run cdist against a schroot target uri +# +# Usage: +# __remote_exec="/path/to/this/script exec" cdist config target_uri +# __remote_copy="/path/to/this/script copy" cdist config target_uri +# +# # target_uri examples: +# schroot:///chroot-name +# schroot://foo.ethz.ch/chroot-name +# schroot://user-name@foo.ethz.ch/chroot-name +# +# # and how to match them in .../manifest/init +# case "$target_host" in +# schroot://*) +# # any schroot +# ;; +# schroot://foo.ethz.ch/*) +# # any schroot on specific host +# ;; +# schroot://foo.ethz.ch/chroot-name) +# # specific schroot on specific host +# ;; +# schroot:///chroot-name) +# # specific schroot on localhost +# ;; +# esac + +my_name="${0##*/}" +mode="$1"; shift + +log() { + #echo "$@" | logger -t "cdist-$my_name-$mode" + : +} + +die() { + echo "$@" >&2 + exit 1 +} + + +uri="$__target_host" + +scheme="${uri%%:*}"; rest="${uri#$scheme:}"; rest="${rest#//}" +authority="${rest%%/*}"; rest="${rest#$authority}" +path="${rest%\?*}"; rest="${rest#$path}" +schroot_name="${path#/}" + +[ "$scheme" = "schroot" ] || die "Failed to parse scheme from __target_host ($__target_host). Expected 'schroot', got '$scheme'" +[ -n "$schroot_name" ] || die "Failed to parse schroot name from __target_host: $__target_host" + +case "$authority" in + '') + # authority is empty, neither user nor host given + user="" + host="" + ;; + *@*) + # authority contains @, take user from authority + user="${authority%@*}" + host="${authority#*@}" + ;; + *) + # no user in authority, default to root + user="root" + host="$authority" + ;; +esac + +log "mode: $mode" +log "@: $@" +log "uri: $uri" +log "scheme: $scheme" +log "authority: $authority" +log "user: $user" +log "host: $host" +log "path: $path" +log "schroot_name: $schroot_name" + +exec_prefix="" +copy_prefix="" +if [ -n "$host" ]; then + # we are working on a remote host + exec_prefix="ssh -o User=$user -q $host" + copy_prefix="scp -o User=$user -q" + copy_destination_prefix="$host:" +else + # working on local machine + copy_prefix="cp" + copy_destination_prefix="" +fi + +case "$mode" in + exec) + code="$exec_prefix schroot -c $schroot_name -- $@" + ;; + copy) + # get directory for given chroot_name + schroot_directory="$($exec_prefix schroot $chroot_name --config | awk -F = '/directory=/ {print $2}')" + [ -n "$schroot_directory" ] || die "Failed to retreive schroot directory for schroot: $schroot_name" + # prefix destination with chroot + code="$copy_prefix $(echo "$@" | sed "s|$uri:|${copy_destination_prefix}${schroot_directory}|g")" + ;; + *) die "Unknown mode: $mode";; +esac + +log "code: $code" + +# Run the code +$code + +log "-----" diff --git a/other/examples/remote/schroot/copy b/other/examples/remote/schroot/copy new file mode 100755 index 00000000..3587a4f2 --- /dev/null +++ b/other/examples/remote/schroot/copy @@ -0,0 +1,50 @@ +#!/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 . +# +# +# __remote_copy script to run cdist against a chroot on the target host over ssh. +# +# Usage: +# __remote_copy="/path/to/this/script schroot-chroot-name" cdist config target_host +# + +log() { + #echo "$@" | logger -t "cdist-schroot-copy" + : +} + +chroot_name="$1"; shift +target_host="$__target_host" + +# get directory for given chroot_name +chroot="$(ssh -o User=root -q $target_host schroot $chroot_name --config | awk -F = '/directory=/ {print $2}')" + +# prefix destination with chroot +code="$(echo "$@" | sed "s|$target_host:|$target_host:$chroot|g")" + +log "target_host: $target_host" +log "chroot_name: $chroot_name" +log "chroot: $chroot" +log "@: $@" +log "code: $code" + +# copy files into remote chroot +scp -o User=root -q $code + +log "-----" diff --git a/other/examples/remote/schroot/exec b/other/examples/remote/schroot/exec new file mode 100755 index 00000000..5b561de0 --- /dev/null +++ b/other/examples/remote/schroot/exec @@ -0,0 +1,45 @@ +#!/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 . +# +# +# __remote_exec script to run cdist against a chroot on the target host over ssh. +# +# Usage: +# __remote_exec="/path/to/this/script schroot-chroot-name" cdist config target_host +# + +log() { + #echo "$@" | logger -t "cdist-schroot-exec" + : +} + +chroot_name="$1"; shift +target_host="$1"; shift + +code="ssh -o User=root -q $target_host schroot -c $chroot_name -- $@" + +log "target_host: $target_host" +log "chroot_name: $chroot_name" +log "@: $@" +log "code: $code" + +# run in remote chroot +$code + +log "-----" diff --git a/other/examples/remote/ssh/copy b/other/examples/remote/ssh/copy new file mode 100755 index 00000000..0ecd8c52 --- /dev/null +++ b/other/examples/remote/ssh/copy @@ -0,0 +1,28 @@ +#!/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 . +# +# +# same as cdist default +# +# Usage: +# __remote_copy="/path/to/this/script" cdist config target_host +# + +#echo "$@" | logger -t "cdist-ssh-copy" +scp -o User=root -q $@ diff --git a/other/examples/remote/ssh/exec b/other/examples/remote/ssh/exec new file mode 100755 index 00000000..b597a47f --- /dev/null +++ b/other/examples/remote/ssh/exec @@ -0,0 +1,28 @@ +#!/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 . +# +# +# same as cdist default +# +# Usage: +# __remote_exec="/path/to/this/script" cdist config target_host +# + +#echo "$@" | logger -t "cdist-ssh-exec" +ssh -o User=root -q $@