SHIFT

--- Sjoerd Hooft's InFormation Technology ---

User Tools

Site Tools


Sidebar

Recently Changed Pages:

View All Pages


View All Tags


LinkedIn




WIKI Disclaimer: As with most other things on the Internet, the content on this wiki is not supported. It was contributed by me and is published “as is”. It has worked for me, and might work for you.
Also note that any view or statement expressed anywhere on this site are strictly mine and not the opinions or views of my employer.


Pages with comments

View All Comments

redhatmercurial

Red Hat Mercurial: Setup and Config

This is the documentation page about the setup from mercurial on Red Hat systems here. Mercurial is a versioning system which uses a master repository server. The usual setup is shown in this picture:

redhatmercurial01.jpg

Once you know how everything works, setting up Mercurial is quite easy, and consists of these steps:

  • Installation
    • Install Mercurial on master repository server
    • Install Mercurial on client server
  • Repositories
    • Create AD Service account for mercurial on master repository server
    • Create repository
  • Cloning
    • Create SSH trust between AD service account on master repository server
    • Create clone
    • Configure clone

The Red Hat Enterprise Linux 6.5 Management Server will function as the master repository server, while all other servers will function as a client, since we want to create a versioning system of all /etc directories.

Note that the element abbreviation for Mercurial is hg. Using mercurial will be done with the command hg.

Installation

The installation can be done through the install of a simple rpm. However, the version that is available in the Red Hat repository is 1.4-3, which is quite old and does not recognize the originating user when editing files using su(do), which is not really useful in our environment since we only use sudo or su. So we got version 2.2.2-1 from http://rpmfind.net//linux/RPM/dag/redhat/el6/x86_64/extras/mercurial-2.2.2-1.el6.rfx.x86_64.html which is also an old version but at least has the ability to recognize the originating user.

We will install the rpm using yum localinstall to keep the yum database intact: sudo yum localinstall mercurial-2.2.2-1.el6.rfx.x86_64.rpm:

[adminsjoerd@rhmgmtsrv repo-getshifting]$ sudo yum localinstall mercurial-2.2.2-1.el6.rfx.x86_64.rpm
Loaded plugins: product-id, refresh-packagekit, subscription-manager
'exceptions.ValueError' object has no attribute 'msg'
Setting up Local Package Process
Examining mercurial-2.2.2-1.el6.rfx.x86_64.rpm: mercurial-2.2.2-1.el6.rfx.x86_64
Marking mercurial-2.2.2-1.el6.rfx.x86_64.rpm to be installed
Resolving Dependencies
--> Running transaction check
---> Package mercurial.x86_64 0:2.2.2-1.el6.rfx will be installed
--> Finished Dependency Resolution

Dependencies Resolved

=================================================================================================================================================================================
Package                            Arch                            Version                                     Repository                                                  Size
=================================================================================================================================================================================
Installing:
mercurial                          x86_64                          2.2.2-1.el6.rfx                             /mercurial-2.2.2-1.el6.rfx.x86_64                           11 M

Transaction Summary
=================================================================================================================================================================================
Install       1 Package(s)

Total size: 11 M
Installed size: 11 M
Is this ok [y/N]: y
Downloading Packages:
Running rpm_check_debug
Running Transaction Test
Transaction Test Succeeded
Running Transaction
  Installing : mercurial-2.2.2-1.el6.rfx.x86_64                                                                                                                              1/1
  Verifying  : mercurial-2.2.2-1.el6.rfx.x86_64                                                                                                                              1/1

Installed:
  mercurial.x86_64 0:2.2.2-1.el6.rfx

Complete!

[adminsjoerd@rhmgmtsrv repo-getshifting]$ yum list installed | grep mercurial
mercurial.x86_64                 2.2.2-1.el6.rfx         @/mercurial-2.2.2-1.el6.rfx.x86_64

The installation is the same for the master repository server as for the clients, just get the rpm from the management server:

wget -O /tmp/mercurial.rpm http://rhmgmtsrv/getshiftinginstall/mercurial-2.2.2-1.el6.rfx.x86_64.rpm
sudo yum localinstall mercurial.rpm

Repositories

Create AD Account

In our setup all accounts that need to log on remotely have to use an AD account. Create one and configure the UNIX properties like this:

redhatmercurial02.jpg

Then setup the homedirectory as configured in the AD account:

sudo mkdir /data/hg 
sudo chown -R srv-rhmgmtsrv-hg:UNIX-Service-Accounts hg/

Then make sure the UNIX-Service-Accounts group can logon over ssh:

AllowGroups UNIX-Server-Admins UNIX-Service-Accounts

Restart service sshd restart to commit the changes.

Create Repository

We will create the repository for “applscripts” and we will use the following directory structure:

  • /data/hg
    • repositories
      • applscripts

So switch to the created application user and setup the directories:

$> mkdir -p /data/hg/repositories/applscripts

Now switch to the directory, make sure the srv-rhmgmtsrv-hg user is the owner and create the repository using the hg init command:

$> cd /data/hg/repositories/applscripts
$> ll

drwxr-xr-x. 2 srv-rhmgmtsrv-hg UNIX-Service-Accounts 4096 Aug  1 10:36 applscripts

$> hg init

$> ls -la

total 12
drwxr-xr-x. 3 srv-rhmgmtsrv-hg UNIX-Service-Accounts 4096 Aug  1 10:36 .
drwxr-xr-x. 3 srv-rhmgmtsrv-hg UNIX-Service-Accounts 4096 Aug  1 10:36 ..
drwxr-xr-x. 3 srv-rhmgmtsrv-hg UNIX-Service-Accounts 4096 Aug  1 10:36 .hg

Cloning

Create SSH Trust

We need to establish a SSH trust between the client and the master server, since we will configure Mercurial to use ssh to send over files.

Configure SSH Daemon

First we will setup SSH to allow for keys authorization using the vi /etc/ssh/sshd_config, uncomment the following lines:

RSAAuthentication yes
PubkeyAuthentication yes
AuthorizedKeysFile      .ssh/authorized_keys

Setup Key Exchange

As the application user on the client server:

[appluser@rhapplserver ~]$ ssh-keygen

and follow the prompts while keeping the passphrase empty.

Now copy it to the AD user on the master repository:

[appluser@rhapplserver ~]$ ssh-copy-id srv-rhmgmtsrv-hg@rhmgmtsrv

Now we should be able to start a ssh session from appluser to the rhmgmtsrv without a password. However, if you would try you would still be requested for your password. That is because SELinux is blocking the ssh daemon form accessing the ssh authorized_keys file. And that is because the home directory for the srv-rhmgmtsrv-hg is not on the default location. You can check this issue by checking the /var/log/messages file. If you have the setroubleshoot-server package installed you'll see a message like this:

Aug  1 11:06:20 rhmgmtsrv setroubleshoot: SELinux is preventing /usr/sbin/sshd from read access on the file authorized_keys. For complete SELinux messages. run sealert -l af6fe822-1e0a-4ec7-8707-c66c7eb2a88f

You could check the allowed locations for the ssh_home directories like this:

[adminsjoerd@rhmgmtsrv hg]$ sudo semanage fcontext -l | grep ssh_home_t
/root/\.shosts                                     all files          system_u:object_r:ssh_home_t:s0
/root/\.ssh(/.*)?                                  all files          system_u:object_r:ssh_home_t:s0
/var/lib/amanda/\.ssh(/.*)?                        all files          system_u:object_r:ssh_home_t:s0
/var/lib/gitolite(3)?/\.ssh(/.*)?                  all files          system_u:object_r:ssh_home_t:s0
/var/lib/openshift/[^/]+/\.ssh(/.*)?               all files          system_u:object_r:ssh_home_t:s0
/var/lib/pgsql/\.ssh(/.*)?                         all files          system_u:object_r:ssh_home_t:s0
/var/lib/stickshift/[^/]+/\.ssh(/.*)?              all files          system_u:object_r:ssh_home_t:s0

Now add the ssh home directory for the srv-rhmgmtsrv-hg user to this list:

[adminsjoerd@rhmgmtsrv hg]$ sudo semanage fcontext -a -t ssh_home_t '/data/hg/.ssh(/.*)?'
[adminsjoerd@rhmgmtsrv hg]$ sudo semanage fcontext -l | grep ssh_home_t
/data/hg/.ssh(/.*)?                                all files          system_u:object_r:ssh_home_t:s0
/root/\.shosts                                     all files          system_u:object_r:ssh_home_t:s0
/root/\.ssh(/.*)?                                  all files          system_u:object_r:ssh_home_t:s0
/var/lib/amanda/\.ssh(/.*)?                        all files          system_u:object_r:ssh_home_t:s0
/var/lib/gitolite(3)?/\.ssh(/.*)?                  all files          system_u:object_r:ssh_home_t:s0
/var/lib/openshift/[^/]+/\.ssh(/.*)?               all files          system_u:object_r:ssh_home_t:s0
/var/lib/pgsql/\.ssh(/.*)?                         all files          system_u:object_r:ssh_home_t:s0
/var/lib/stickshift/[^/]+/\.ssh(/.*)?              all files          system_u:object_r:ssh_home_t:s0

Now set the SElinux context correct:

[adminsjoerd@rhmgmtsrv hg]$ sudo restorecon -Rv /data/hg
restorecon reset /data/hg/.ssh context unconfined_u:object_r:sshd_key_t:s0->unconfined_u:object_r:ssh_home_t:s0
restorecon reset /data/hg/.ssh/authorized_keys context unconfined_u:object_r:sshd_key_t:s0->unconfined_u:object_r:ssh_home_t:s0
[adminsjoerd@rhmgmtsrv hg]$ sudo ls -lZ .ssh/
-rw-------. srv-rhmgmtsrv-hg UNIX-Service-Accounts unconfined_u:object_r:sshd_key_t:s0 authorized_keys

Now it works!

Create Clone

Now all we have to do to create the clone is to change to the directory that will function as the root of the repository on the client. So for example, if you want to create a repository of all files in the directory //appl/algorithmics/acp/markets (which by coincidence is the homedir of the application user) you do that like this:

[appluser@rhapplserver ~]$ hg clone ssh://srv-rhmgmtsrv-hg@rhmgmtsrv/repositories/markets .
[appluser@rhapplserver ~]$ ll
total 125M
drwx------. 20 appluser appluser 4.0K Aug  1 11:40 .
drwxr-xr-x.  3 appluser appluser 4.0K Jul  9 14:02 ..
...<cut>...
drwxrwxr-x.  3 appluser appluser 4.0K Aug  1 11:40 .hg
...<cut>...

As you can see the .hg directory is created meaning the connection for the repository between the master and the client is now established.

Configure Clone

Now we don't want to have all the files in the directory in the repository so we need to configure which files will be included and which will be excluded. For this, create in the same directory where the .hg directory is a file called .hgignore:

^(?!scripts|hg)
(\.|_)(out|log|orig|bak|rwb|scf|tar|gz|zip|csv|trm|bck|rws)$
~$
[0-9]{8}$
hgshelve.pyc$
backup

The topline will only include directories in the hg and scripts directory, while the other lines exclude files that match any of the patterns.

Basic Mercurial Usage and Commands

We will use Mercurial for two purposes, first is keeping an remote repository for each server's /etc directory. Second is keeping an adminscripts directory in sync over all servers with scripts that will have to be kept on all servers.

Creating Remote Repository for ETC Directory

On The Master Repository Server

sudo su - srv-rhmgmtsrv-hg
cd /data/hg/repositories/
mkdir etc-rhapplserver
cd etc-rhapplserver/
hg init
-bash-4.1$ ls -la
total 12
drwxr-xr-x. 3 srv-rhmgmtsrv-hg UNIX-Service-Accounts 4096 Sep  6 20:26 .
drwxr-xr-x. 4 srv-rhmgmtsrv-hg UNIX-Service-Accounts 4096 Sep  6 20:25 ..
drwxr-xr-x. 3 srv-rhmgmtsrv-hg UNIX-Service-Accounts 4096 Sep  6 20:26 .hg
-bash-4.1$ exit

On the Client

First set up the key exchange as the root server:

[adminsjoerd@rhapplserver ~]$ su - root
[root@rhapplserver ~]# ssh-keygen
[root@rhapplserver ~]# ssh-copy-id srv-rhmgmtsrv-hg@rhmgmtsrv

test the connection now:

[root@rhapplserver ~]# ssh srv-rhmgmtsrv-hg@rhmgmtsrv
Last login: Fri Aug  1 11:36:30 2014 from rhapplserver.prd.domain

-bash-4.1$ exit

Now use these commands to clone the repository, make an exclude file and update the repository with all the files:

[root@rhapplserver ~]# cd /etc
[root@rhapplserver etc]# hg clone ssh://srv-rhmgmtsrv-hg@rhmgmtsrv/repositories/etc-rhapplserver .

abort: destination '.' is not empty

This error is because /etc is not empty. Mercurial expects an empty directory so work around this by letting hg create an empty directory and then move the files:

[root@rhapplserver etc]# hg clone ssh://srv-rhmgmtsrv-hg@rhmgmtsrv/repositories/etc-rhapplserver
[root@rhapplserver etc]# cd etc-rhapplserver
[root@rhapplserver etc-rhapplserver]# mv .hg/ ../
[root@rhapplserver etc-rhapplserver]# cd ..
[root@rhapplserver etc]# rm -rf etc-rhapplserver/
[root@rhapplserver etc]# hg status

This will show you all files in /etc so you first need to create an excludefile:
[root@rhapplserver etc]# vi .hgignore
[root@rhapplserver etc]# cat .hgignore
(\.|_)(out|log|orig|bak|rwb|scf|tar|gz|zip|csv|trm|bck|rws)$
~$
backup
cache
mtab

Now we add and commit the files to the local repository:
[root@rhapplserver etc]# hg add
[root@rhapplserver etc]# hg commit -u "adminsjoerd" -m "initial import in 6 sep 14"

Now push the files to the remote repository:
[root@rhapplserver etc]# hg push

If you ever want some more files not kept in the repository or removed from it you can use the forget command:

hg forget <filename>

Do not forget to commit the changes:

hg commit -Am -u "adminsjoerd" "Removed some files from repository"

Creating An AdminScripts Directory

On The Master Repository Server

sudo su - srv-rhmgmtsrv-hg
cd /data/hg/repositories/
mkdir adminscripts
cd adminscripts/
hg init
-bash-4.1$ ls -la
total 12
drwxr-xr-x. 3 srv-rhmgmtsrv-hg UNIX-Service-Accounts 4096 Sep  6 20:26 .
drwxr-xr-x. 4 srv-rhmgmtsrv-hg UNIX-Service-Accounts 4096 Sep  6 20:25 ..
drwxr-xr-x. 3 srv-rhmgmtsrv-hg UNIX-Service-Accounts 4096 Sep  6 20:26 .hg
-bash-4.1$ exit

On the Client

[root@rhapplserver etc]# cd /
[root@rhapplserver /]# mkdir adminscripts
[root@rhapplserver /]# cd adminscripts
[root@rhapplserver adminscripts]# hg clone ssh://srv-rhmgmtsrv-hg@rhmgmtsrv/repositories/adminscripts .
no changes found
updating to branch default
0 files updated, 0 files merged, 0 files removed, 0 files unresolved

If you would like the adminscripts dir also on the local server you can also clone the dir locally:

sudo hg clone /data/hg/repositories/adminscripts .
[root@rhapplserver adminscripts]# vi testfile
[root@rhapplserver adminscripts]# ll
total 4
-rw-r--r--. 1 root root 9 Sep  6 21:57 testfile
[root@rhapplserver adminscripts]# hg st
? testfile
[root@rhapplserver adminscripts]# hg add
adding testfile
[root@rhapplserver adminscripts]# hg commit -u "adminsjoerd" -m "Testfile"
[root@rhapplserver adminscripts]# hg push

On the Second Client

[root@rhapplserver02 adminscripts]# hg pull
[root@rhapplserver02 adminscripts]# hg update

Restore Files

See Files on Master Repository

If you just want to look into the current file on the master repository log on the Master Repository Server and browse to the repository directory. Then change to the “.hg → store → data” directory and that's where your files will be.

Overview Off Commits

You can use the hg log command to see the different revisions. You can see why it's important to keep your comments nicely documented:

[root@rhapplserver etc]# hg log
changeset:   2:d22a2c80e17e
tag:         tip
user:        adminsjoerd
date:        Wed Sep 10 13:21:38 2014 +0200
summary:     removed some non-config files-2

changeset:   1:9daff51252cf
user:        adminsjoerd
date:        Wed Sep 10 13:20:47 2014 +0200
summary:     removed some non-config files

changeset:   0:dafda2b4790f
user:        adminsjoerd
date:        Sat Sep 06 21:27:00 2014 +0200
summary:     initial import in 6 sep 14

Recover From Accidental Commit

If you've committed a change but have not yet pushed it to the master repository use hg rollback to make the changeset go away:

[root@rhapplserver etc]# vi hg-testfile
[root@rhapplserver etc]# hg add
adding hg-testfile
[root@rhapplserver etc]# hg commit
abort: no username supplied (see "hg help config")
[root@rhapplserver etc]# hg commit -u adminsjoerd -m "Added hg-testfile for documentation purposes"
[root@rhapplserver etc]# hg tip
changeset:   3:121369f2f5f2
tag:         tip
user:        adminsjoerd
date:        Wed Sep 10 13:54:39 2014 +0200
summary:     Added hg-testfile for documentation purposes

[root@rhapplserver etc]# hg status
[root@rhapplserver etc]# hg rollback
repository tip rolled back to revision 2 (undo commit)
working directory now based on revision 2
[root@rhapplserver etc]# hg status
A hg-testfile

Above you see I've added a file and I want to roll that back. Rolling back the commit makes hg see the file again as added.

Note that rollback is useless after a push.

Restore Previous Version From Local Repository

In this example I commit and push the changes made to the remote repository. Then I make another change, commit and push these as well. Then I rollback to the previous version:

[root@rhapplserver etc]# hg status
A hg-testfile
[root@rhapplserver etc]# hg commit -u adminsjoerd -m "Added hg-testfile for documentation purposes"
[root@rhapplserver etc]# hg push
pushing to ssh://srv-rhmgmtsrv-hg@rhmgmtsrv/repositories/etc-rhapplserver
searching for changes
remote: adding changesets
remote: adding manifests
remote: adding file changes
remote: added 1 changesets with 1 changes to 1 files
[root@rhapplserver etc]# vi hg-testfile
[root@rhapplserver etc]# hg status
M hg-testfile
[root@rhapplserver etc]# hg commit -u adminsjoerd -m "Added hg-testfile for documentation purposes - version 2"
[root@rhapplserver etc]# hg push
pushing to ssh://srv-rhmgmtsrv-hg@rhmgmtsrv/repositories/etc-rhapplserver
searching for changes
remote: adding changesets
remote: adding manifests
remote: adding file changes
remote: added 1 changesets with 1 changes to 1 files
[root@rhapplserver etc]# cat hg-testfile
version 01
version 02
[root@rhapplserver etc]# hg log
changeset:   4:9649f4d260bb
tag:         tip
user:        adminsjoerd
date:        Wed Sep 10 13:59:00 2014 +0200
summary:     Added hg-testfile for documentation purposes - version 2

changeset:   3:8c84648c5677
user:        adminsjoerd
date:        Wed Sep 10 13:58:21 2014 +0200
summary:     Added hg-testfile for documentation purposes

changeset:   2:d22a2c80e17e
user:        adminsjoerd
date:        Wed Sep 10 13:21:38 2014 +0200
summary:     removed some non-config files-2

changeset:   1:9daff51252cf
user:        adminsjoerd
date:        Wed Sep 10 13:20:47 2014 +0200
summary:     removed some non-config files

changeset:   0:dafda2b4790f
user:        adminsjoerd
date:        Sat Sep 06 21:27:00 2014 +0200
summary:     initial import in 6 sep 14

[root@rhapplserver etc]# hg update -r 3
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
[root@rhapplserver etc]# cat hg-testfile
version 01

Restore File From Master Repository

This is an example on how to deal with deleted files. As long as the change is not committed there is no problem. After you delete the file, “hg delete” the file and commit the change that data is gone and you need other needs to restore the files (from tape):

[root@rhapplserver etc]# hg st
[root@rhapplserver etc]# rm hg-testfile
rm: remove regular file `hg-testfile'? y
[root@rhapplserver etc]# hg status
! hg-testfile
[root@rhapplserver etc]# hg revert hg-testfile
[root@rhapplserver etc]# hg status
[root@rhapplserver etc]# cat hg-testfile
version 01
[root@rhapplserver etc]# rm hg-testfile
rm: remove regular file `hg-testfile'? y
[root@rhapplserver etc]# hg st
! hg-testfile
[root@rhapplserver etc]# hg commit
nothing changed (1 missing files, see 'hg status')
[root@rhapplserver etc]# hg st
! hg-testfile
[root@rhapplserver etc]# hg remove hg-testfile
[root@rhapplserver etc]# hg st
R hg-testfile
[root@rhapplserver etc]# hg commit -u adminsjoerd -m "Removed testfile"
created new head
[root@rhapplserver etc]# hg st
[root@rhapplserver etc]# hg revert hg-testfile
hg-testfile: no such file in rev 2421b14f9b72

Working with Multiple Committers

Even though I'm not working with multiple committers I found that it is best to follow the guidelines for this as this prevents non trusting errors like:

remote: abort: Permission denied: <repository directory>/.hg/store/data/.<filename>

The guidelines for working with multiple editors are found here and come down to going to the mail repository, and make sure all files are owned by the service account and group, and then set the sticky bit so new files that are added to the repository get the same permissions:

cd /data/hg/repositories/adminscripts/
sudo chown srv-rhmgmtsrv-hg:UNIX-Service-Accounts -R .hg/
sudo chmod g+w .hg .hg/* .hg/store/*
sudo chmod g+s .hg .hg/store .hg/store/data

Script for Automatic Updates

I also wanted a script so I could create changes to configfiles without having to thing about committing these changes to the repository. Downside of this is of course that if you would have multiple edits on the same file between updates these updated would go missing. Do mitigate this you should run your script as often as possible, or only change files on one server. I created a script that can be scheduled using cron.

Script

# Script to update changes in the adminscripts directory and distribute them among all the servers.
# Author: Sjoerd Hooft
# Date: 27-11-2014
 
# Schedule time
# This script can be used manually or scheduled. To make sure changes are not pushed at the same time keep this schedule:
# 3:00 rhmgmtsrv01
# 3:05 rhmgmtsrv02
# 4:00 rhdbprd01
# 4:05 rhapprd01
# 5:00 rhdbacp01
# 5:05 rhapacp01
 
# Script Variables
TODAY=`date +%Y%m%d%H%M`
LOGFILE=/tmp/mercurialdailyupdate.log
HOST=`hostname -s`
REPO="/adminscripts/"
 
# Create new logfile
echo "Start daily mercurial update on $HOST on $TODAY" > $LOGFILE
 
# First: Push local changes to the repository
echo "Start push" >> $LOGFILE
/usr/bin/hg add -R $REPO >> $LOGFILE
/usr/bin/hg commit -u "DailyPush" -m "DailyPush" -R $REPO >> $LOGFILE
/usr/bin/hg push -R $REPO >> $LOGFILE
 
# Second: download changes from other servers
echo "Start pull" >> $LOGFILE
/usr/bin/hg pull -R $REPO >> $LOGFILE
/usr/bin/hg update -R $REPO >> $LOGFILE
 
# Close
echo "End of log" >> $LOGFILE
exit 0

Cron

Then schedule the script using sudo crontab -e (so the script gets executed as root):

# Run mercurial update every day (time in script)
0 3 * * * /adminscripts/mercurialupdate > /tmp/mercurialupdate.log 2>&1

Resources

You could leave a comment if you were logged in.
redhatmercurial.txt · Last modified: 2021/09/24 00:25 (external edit)