summaryrefslogtreecommitdiff
blob: 60f7687243ad430c41cba52e3cbb8a950b0d1c31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/bin/bash -x

# On container start, run an rsync to get a good copy of the tree.
# Then execute rsyncd; we will start serving once the sync completes.
# Then keep syncing in the background every 30m.

# We keep the trees in a TMPFS, we need a million inodes and 1300MB of space.
mount -t tmpfs -o size=1300m,nr_inodes=1000000 tmpfs "${DEST_DIR}"
if [[ $? -ne 0 ]]; then
	logger -t rsync "Init: Failed to create tmpfs: ${DEST_DIR}"
	return 1
fi

# Maintain 2 'partitions' of the tree.
# "serving" - This copy is served to users and is not mutated.
# "updating" - This copy is a shadow copy used for updates.
# Create the two partitions on startup.

PARTITION1=$(mktemp -d -p "${DEST_DIR}" XXXXXX)
PARTITION2=$(mktemp -d -p "${DEST_DIR}" XXXXXX)

# Function sync syncs dest ("${2}") from source ("${1}")
function sync() {
	OPTS=(
	 --quiet
	 --recursive
	 --links
	 --perms
	 --times
	 --delete
	 --timeout=300
	 --progress
	 # NOTE(antarus): Checksum upsets some public mirror nodes; so don't use it for now.
   #	 --checksum
	)
	SRC="${2}"
	DST="${1}"
  
	logger -t rsync "Started update at: $(date)"
	logger -t rsync "re-rsyncing the gentoo-portage tree"
	/usr/bin/rsync ${OPTS[@]} "${SRC}" "${DST}" >> $0.log 2>&1
	err=$?
	if [[ $err -ne 0 ]]; then
		logger -t rsync "Failed to rsync tree: ${SRC}: $(date)"
		return 1
	fi
	logger -t rsync "End: $(date)"
	return 0
}

# Function init does a first sync, to populate the serving partition and
# setup symlinks, and begin serving data.
# "${1}" is the serving partition. "${2}" is the update partition
function init() {
	sync "${1}" "${SOURCE_MIRROR}" # this is synchronous.

	# We serve out of ${DEST_DIR}/serving
	ln -s "${1}" "${DEST_DIR}/serving"
	# Setup the update partition
	ln -s "${2}" "${DEST_DIR}/update"

	# Then launch rsyncd; it will detach into the background and serve from serving.
	rsync --daemon --config="/opt/rsync/rsyncd.conf"
	return 0
}

# Function update syncs the 'update' partition and, if successful, swaps the partitions.
function update() {
	update=$(readlink "${DEST_DIR}/update")
	# If we fail to sync, just return false and avoid modifying the serving state.
	if ! sync "${update}" "${SOURCE_MIRROR}"; then
		return 1
	fi

	# Save the previous serving partition
	old_serving=$(readlink "${DEST_DIR}/serving")
	# Point the serving symlink at the update partition; now freshly updated.
	mv -fT "${DEST_DIR}/update" "${DEST_DIR}/serving"
	# Point the update partition at the old serving partition.
	ln -sf "${old_serving}" "${DEST_DIR}/update"
}

function serve() {
	while true
	do
		# TODO(antarus): Add exponential backoff.
		sleep "${WAIT_TIME}"
		update
	done
}

# Partition1 starts as "serving", partition2 as "update"
init "${PARTITION1}" "${PARTITION2}"
if [[ $? -ne 0 ]]; then
	exit 1
fi
# Serve forever
serve