Repository anatomy⚓
Record in sources.list may look like this:
deb http://ppa.launchpad.net/saltstack/salt/ubuntu trusty main
It consists of repository url, distribution and one or more components.
deb <repository> <distribution> <component(s)>
Distribution⚓
Distribution name consists of Linux distribution version codename (eg. trusty) and our internal stability identifier (stable or testing)
It can also have a prefix that can identify source.
- Examples
- trusty-stable
- trusty-testing
- upstream/trusty-testing
Component⚓
Components are subsets of each distribution repository. They can be used independently by choice or all together.
- Examples
- main - Ubuntu packages
- universe - Ubuntu universe packages
Local repository⚓
Custom packages build by us. Published as cloudlab component.
aptly repo create -component=cloudlab -architectures=amd64 cloudlab
Publishing⚓
Direct publish⚓
It’s possible to publish repository directly with no snapshots. Unfortunately this is not possible with mirrors so if it’s needed to combine components including custom repository and mirrorred ones, it has to be via snapshots. See Multi component publishing
- New publish
aptly publish repo -distribution=trusty-testing cloudlab
- Update publish
aptly publish update trusty-testing
Snapshot publish⚓
To create new snapshot and create or update existing publish.
- New publish
aptly snapshot create cloudlab-`date +%s` from repo cloudlab aptly publish snapshot -distribution=trusty-stable cloudlab-1433941328
- Update publish
aptly snapshot create cloudlab-`date +%s` from repo cloudlab aptly publish switch -component=cloudlab trusty-stable cloudlab-1433941328
Repository mirror⚓
First you need to import distributor’s GPG key to be able to verify packages.
gpg --no-tty --no-default-keyring --keyring trustedkeys.gpg --keyserver keys.gnupg.net --recv-keys 0E27C0A6
- Create mirror
aptly mirror create -architectures=amd64 salt http://ppa.launchpad.net/saltstack/salt/ubuntu/ trusty main
- Update mirror
This will fetch newest packages from upstream. Should be run periodically to be always up to date.
aptly mirror update salt
Real-world example⚓
Let’s try to design some real-world example.
Software sources⚓
- local repository with custom packages
- upstream mirror
- trusty main
- trusty-security main
- ubuntucloud (trusty-updates/juno main)
- salt repository mirror
- Example status
aptly@aptly01:/home/filip$ aptly repo list List of local repos: - [cloudlab]: TCP Cloud custom components (packages: 0) aptly@aptly01:/home/filip$ aptly mirror list List of mirrors: - [salt-trusty]: http://ppa.launchpad.net/saltstack/salt/ubuntu/ trusty - [trusty-main]: http://eu.archive.ubuntu.com/ubuntu/ trusty - [trusty-security]: http://eu.archive.ubuntu.com/ubuntu/ trusty-security - [trusty-updates-juno]: http://ubuntu-cloud.archive.canonical.com/ubuntu/ trusty-updates/juno
Sync mirrors⚓
It’s good idea to keep mirrors up to date and sync them periodically. You can use script bellow, place it eg. into /usr/local/bin/aptly_update_mirrors.sh and create crontab record for aptly user (suppose that aptly repo is not managed by root).
#!/bin/bash
SCRIPT=$(basename $0)
MAXTRIES=3
VERBOSE=0
SNAPSHOT=0
log_info() {
logger -p user.info -t ${SCRIPT} "$*"
[ $VERBOSE -eq 1 ] && echo "[INFO] $*"
}
log_error() {
logger -p user.error -t ${SCRIPT} "$*"
echo "[ERROR] $*" >&2
}
if [[ "$*" == *--verbose* || "$*" == *-v* ]]; then
VERBOSE=1
fi
if [[ "$*" == *--snapshot* || "$*" == *-s* ]]; then
SNAPSHOT=1
fi
MIRRORS=$(aptly mirror list --raw 2>&1)
if [[ $? -ne 0 ]]; then
log_error "$MIRRORS"
exit 1
fi
for mirror in $MIRRORS; do
try=0
retval=666
while [[ $retval -ne 0 && $try -lt $MAXTRIES ]]; do
log_info "Starting update of mirror ${mirror}"
if [[ $VERBOSE -eq 0 ]]; then
out=$(aptly mirror update -force=true ${mirror} >/dev/null 2>&1)
else
aptly mirror update -force=true ${mirror}
fi
retval=$?
if [[ $retval -ne 0 ]]; then
try=$[ $try + 1 ]
log_error "Failed to update mirror ${mirror}, try=${try}"
[ ! -z "$out" ] && log_error "${out}"
else
log_info "Synced mirror ${mirror}"
if [[ $SNAPSHOT -eq 1 ]]; then
snapshot_name="${mirror}-$(date +%s)"
if [[ $VERBOSE -eq 0 ]]; then
out=$(aptly snapshot create ${snapshot_name} from mirror ${mirror} >/dev/null 2>&1)
else
aptly snapshot create ${snapshot_name} from mirror ${mirror}
fi
retval=$?
if [[ $retval -ne 0 ]]; then
log_error "Failed to create snapshot ${snapshot_name} from mirror ${mirror}"
[ ! -z "$out" ] && log_error "$out"
else
log_info "Created snapshot ${snapshot_name} from mirror ${mirror}"
fi
fi
break
fi
done
done
$ crontab -e -u aptly
# Sync aptly mirrors every 6 hours
0 */6 * * * aptly_sync_mirrors.sh -v -s
First run may take a long time so it’s good idea to execute it in terminal multiplexer (Tmux, Screen, Byobu, etc.) in verbose mode. Parameter --snapshot will automatically create snapshots of newly synced mirrors.
aptly_sync_mirrors.sh --verbose --snapshot
Publishing⚓
First we need to decide how our published repository will look like.
Distribution⚓
- We want to support only Ubuntu Trusty in three branches:
- nightly with up-to-date packages with mirrors
- testing for stabilization
- stable for rock-solid distribution
So final distributions will be:
- trusty-nightly
- trusty-testing
- trusty-stable
Components⚓
Each OS distribution has different approach to components. For our workflow, component means:
- set of independently versioned packages (sub-distribution) where customer may choose - eg. Openstack juno, kilo - or Opencontrail 21, 22, etc.
- set of packages with different or faster release workflow - because you can update publish per-component - eg. security from trusty-security distribution - or custom packages in our repository
For our example, components will be:
- main - merged upstream + ppa repositories with newer software versions
- security - security updates
- because we want to push security updates between publishes separately
- cloudlab - our packages
- because our packages can have different release cycle than rest of the distribution
- juno - packages of Openstack Juno
- because this component is optional and one can use different openstack version than juno
Create publish⚓
This way you will create snapshots, merge some of them into one component snapshot (main) and publish using multi-component publishing.
- Create snapshots for each mirror and repo
STAMP=$(date +%s) aptly mirror list --raw|while read i;do aptly snapshot create $i-${STAMP} from mirror $i;done aptly repo list --raw|while read i;do aptly snapshot create $i-${STAMP} from repo $i;done
- Merge snapshots into desired final components
aptly snapshot merge main-${STAMP} salt-trusty-${STAMP} trusty-main-${STAMP}
- Create multi-component publish of snapshots
aptly publish snapshot -component=main,cloudlab,juno,security -distribution=trusty-nightly main-${STAMP} cloudlab-${STAMP} trusty-updates-juno-${STAMP} trusty-security-${STAMP}
- When new snapshots are created, use similar command as the one above to update publish:
aptly publish switch -component=main,cloudlab,juno,security trusty-nightly main-${STAMP} cloudlab-${STAMP} trusty-updates-juno-${STAMP} trusty-security-${STAMP}
For more information, read multi-component publishing
Naming conventions⚓
Mirrors⚓
There has to be one mirror per each repository distribution, so distribution has to be reflected in the name. Aptly doesn’t work with components on this level so each component has to be mirrored separately, otherwise it will be merged into main component.
Source repository can be ommited from mirror name if it’s upstream repository. Distribution name can me ommited if there’s only one distribution per mirrored repository and naming it explicitly would be confusing.
- Patterns and examples
- <source>-<distribution>(-<component>)
- salt-trusty (<salt_ppa> trusty main)
- trusty-main (<ubuntu_upstream> trusty main)
- trusty-updates-juno (<ubuntucloud_repository> trusty-updates/juno main)
Snapshots⚓
It’s always good idea to use Unix timestamp for snapshot versioning and same name as for source mirror or repository.
For snapshots created by merging other snapshots, naming by published distribution is a good idea.
- Patterns and examples
- cloudlab-$(date +%s)
- trusty-updates-juno-1434444631
- main-1434444631