背景
我们正在使用 nagios 来监控我们的基础设施。目前,我们没有对 nagios 配置进行版本控制,我们中有两个人负责管理 nagios 配置。因此,我正在努力将我们的 nagios 配置放入中央 git 存储库,使用一些钩子进行语法检查,然后如果配置看起来不错,就让它们“活跃”。我正在使用这家伙的以此作为起点。
我尝试实现的一般工作流程是:
- 编辑 nagios 配置的本地 git repo。添加编辑的文件,本地提交。
git push origin master
到远程仓库。- 推送被预接收钩子拦截,它获取文件,将其移动到服务器上的临时目录,然后通过 nagios 语法检查器运行它们。
- 如果语法检查器通过,则接受推送,然后使用提交后挂钩将
git pull
新代码放入实时 nagios 配置目录中,然后重新启动 nagios。 - 如果语法检查器失败,则拒绝推送,并向用户显示 nagios 语法错误。
但是,当我由于 nagios 配置中的语法错误而拒绝 git push 时,我遇到了一种奇怪的行为。我期望发生的是,如果我拒绝钩子,则尝试的推送应该使存储库保持原样,不受影响。但事实似乎并非如此。以下是我所看到的详细信息:
问题
我在本地编辑 nagios 配置,故意包含一个语法错误,添加,然后在本地提交:
host:nagios erik$ vi nagios.cfg
host:nagios erik$ git add nagios.cfg
host:nagios erik$ git commit -m "syntax error"
[master da71aed] syntax error
1 files changed, 1 insertions(+), 0 deletions(-)
现在我把这些更改推送到主仓库。这会因语法错误而被拒绝:
host:nagios erik$ git push origin master
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 12.74 KiB, done.
Total 3 (delta 1), reused 2 (delta 1)
remote: Previous HEAD position was 3ddc880... removed syntax error
remote: HEAD is now at da71aed... syntax error
remote: Nagios Config Check Exit Status: 254
remote: Your configs did not parse correctly, there was an error. Output follows.
remote:
remote: Nagios Core 3.2.3
remote: Copyright (c) 2009-2010 Nagios Core Development Team and Community Contributors
remote: Copyright (c) 1999-2009 Ethan Galstad
remote: Last Modified: 10-03-2010
remote: License: GPL
remote:
remote: Website: http://www.nagios.org
remote: Reading configuration data...
remote: Error in configuration file '/tmp/nagiosworkdir/nagios.cfg' - Line 23 (NULL value)
remote: Error processing main config file!
remote:
remote:
remote:
remote: ***> One or more problems was encountered while processing the config files...
remote:
remote: Check your configuration file(s) to ensure that they contain valid
remote: directives and data defintions. If you are upgrading from a previous
remote: version of Nagios, you should be aware that some variables/definitions
remote: may have been removed or modified in this version. Make sure to read
remote: the HTML documentation regarding the config files, as well as the
remote: 'Whats New' section to find out what has changed.
remote:
To [email protected]:nagios
! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to '[email protected]:nagios'
这不应该已经触及远程存储库,但确实如此。如果我切换到另一个本地临时目录并尝试克隆存储库,我会得到:
host:temp erik$ git clone [email protected]:nagios
Cloning into nagios...
remote: Counting objects: 30, done.
remote: Compressing objects: 100% (29/29), done.
remote: Total 30 (delta 12), reused 0 (delta 0)
Receiving objects: 100% (30/30), 29.81 KiB, done.
Resolving deltas: 100% (12/12), done.
error: Trying to write ref HEAD with nonexistant object da71aedfde2e0469288acd9e45bb8b57a6e5a7b3
fatal: Cannot update the ref 'HEAD'.
现在我回到原来的工作目录,修复语法错误,添加、提交并推送:
host:nagios erik$ vi nagios.cfg
host:nagios erik$ git add nagios.cfg
host:nagios erik$ git commit -m "removing syntax error, push should succeed this time"
[master f147ded] removing syntax error, push should succeed this time
1 files changed, 0 insertions(+), 2 deletions(-)
host:nagios erik$ git push origin master
Counting objects: 6, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 487 bytes, done.
Total 4 (delta 2), reused 0 (delta 0)
remote: Previous HEAD position was 4c80d45... syntax error
remote: HEAD is now at f147ded... removing syntax error, push should succeed this time
remote: Nagios Config Check Exit Status: 0
remote: Your configs look good and parsed correctly.
To [email protected]:nagios
3ddc880..f147ded master -> master
此时,存储库没有问题,我可以切换到临时目录并再次克隆存储库:
host:temp erik$ git clone [email protected]:nagios
Cloning into nagios...
remote: Counting objects: 34, done.
remote: Compressing objects: 100% (33/33), done.
remote: Total 34 (delta 14), reused 0 (delta 0)
Receiving objects: 100% (34/34), 30.22 KiB, done.
Resolving deltas: 100% (14/14), done.
这里是预接收我正在使用的钩子。
我在客户端上使用 git v1.7.5.4,在服务器上使用 v1.7.2.3。
因此,对于问题:为什么当我拒绝推送时,存储库处于不一致状态?是我的 git hook 出了问题,还是我对 git 的理解不够?
答案1
你正在做的:
export GIT_WORK_TREE=/tmp/nagiosworkdir
/usr/bin/git checkout -f $NEW_SHA1
在你的钩子中。虽然它没有触及你通常的工作副本,但它是更新 git-dir 中的引用(特别是HEAD
引用),如您的错误所示:
...
remote: HEAD is now at da71aed... syntax error
...
您的钩子正在exit 1
拒绝更新,但是HEAD
在失败后它不会(重新)重置引用。
我认为您需要像这样更新钩子中的失败分支:
...
if [ "$NAGIOS_CHECK_STATUS" -ne 0 ]
then
echo "Your configs did not parse correctly, there was an error. Output follows."
cat $GIT_WORK_TREE/check.out
/usr/bin/git reset --hard $OLD_SHA1 # <-- Add This
exit 1
else
...
答案2
git checkout
钩子中的命令正在创建/更新存储库中的 HEAD 引用。
如果你的仓库是裸仓库,那么它可以在没有 HEAD 引用的情况下生存(新的克隆将默认检出其掌握分支(如果有的话);只需在退出之前删除 HEAD 引用(也许可以这样,trap
这样您就不必在每个引用之前exit
单独安排这样做)。在脚本中的“早期”任何地方:
trap 'git update-ref -m "removing HEAD after temporary checkout to alternate workdir" -d HEAD "$NEW_SHA1"' 0
如果您的存储库不是裸存储库,或者您想要维护 HEAD 引用(以便克隆默认会检出其他分支),那么您必须在退出之前保存 HEAD 引用并将其恢复。
首先,在服务器的存储库中,重置 HEAD 引用以指向您想要在新克隆中默认签出的分支:
git symbolic-ref -m 'setting default branch for new clones' HEAD refs/heads/master
然后,在您的挂钩脚本中(结帐之前的任何位置):
# Restore HEAD symref when exiting
saved_HEAD=$(git symbolic-ref HEAD)
trap 'git symbolic-ref -m "restoring HEAD after temporary checkout to alternate workdir" HEAD "$saved_HEAD"' 0
顺便说一句,pre-receive
钩子应该确保它们完全读取标准输入并处理它们输入的所有行。在处理完所有输入之前退出有时会在进程中触发 SIGPIPE git-receive-pack
;如果您一次只推送一个引用(因为您至少读取一行),则可能不会出现这种情况,但这是需要记住的事情。可能将此钩子作为一个update
钩子更容易,因为您一次只需要关注一个引用,并且可以单独拒绝每个引用的推送(也许您只关心保留提示)掌握“清理”;同时检查并报告其他分支的提示,但绝不会拒绝它们,以便它们可以用于未完成工作的协作。