diff --git a/.github/workflows/pkg.yml b/.github/workflows/pkg.yml index 31f73328..e95ed69b 100644 --- a/.github/workflows/pkg.yml +++ b/.github/workflows/pkg.yml @@ -1,19 +1,15 @@ -# GitHub Actions workflow for building and testing Krill O/S packages. -# Uses GitHub Actions caching to avoid rebuilding Rust cargo-deb and -# Krill dependencies on every run. +# GitHub Actions workflow for building and testing Krill O/S packages. Uses GitHub Actions caching to avoid rebuilding +# Rust cargo-deb , cargo generate-rpm and Krill compiled dependencies on every run. # -# Note: at the time of writing the GH cache contents expire after a -# week if not used so the next build may be much slower as it will -# have to re-download/build/install lots of Rust crates. +# Note: at the time of writing the GH cache contents expire after a week if not used so the next build may be much +# slower as it will have to re-download/build/install lots of Rust crates. # -# Packages are built inside Docker containers as GH Runners have extra libraries -# and packages installed which can cause package building to succeed but package -# installation on a real target O/S to fail, due to being built against too +# Packages are built inside Docker containers as GH Runners have extra libraries and packages installed which can cause +# package building to succeed but package installation on a real target O/S to fail, due to being built against too # recent version of a package such as libssl or glibc. # -# Packages are tested inside LXC/LXD containers because Docker containers don't -# by default support init managers such as systemd but we want to test systemd -# service unit installation and activation. +# Packages are tested inside LXC/LXD containers because Docker containers don't by default support init managers such as +# systemd but we want to test systemd service unit installation and activation. name: Packaging on: @@ -51,9 +47,12 @@ defaults: shell: bash --noprofile --norc -eo pipefail -x {0} jobs: - # Use the cargo-deb Rust crate to build a Debian package for installing - # Krill. See: https://github.com/mmstick/cargo-deb - deb-pkg: + # Use the cargo-deb and cargo-generate-rpm Rust crates to build Debian and RPM packages respectively for installing + # Krill. + # See: + # - https://github.com/mmstick/cargo-deb + # - https://github.com/cat-in-136/cargo-generate-rpm + pkg: strategy: matrix: image: # can't use complex values here, only primitive values are allowed @@ -63,16 +62,21 @@ jobs: - 'debian:stretch' # debian/9 - 'debian:buster' # debian/10 - 'debian:bullseye' # debian/11 + - 'centos:7' + - 'centos:8' + include: + - image: 'centos:7' + extra_build_args: '--features static-openssl' env: CARGO_DEB_VER: 1.28.0 - # A Krill version of the form 'x.y.z-plus' denotes a dev build that is - # newer than the released x.y.z version but is not yet a new release. + CARGO_GENERATE_RPM_VER: 0.4.0 + # A Krill version of the form 'x.y.z-bis' denotes a dev build that is newer than the released x.y.z version but is + # not yet a new release. NEXT_VER_LABEL: bis - name: deb-pkg + name: pkg runs-on: ubuntu-latest - # Build on the oldest platform we are targeting in order to avoid - # https://github.com/rust-lang/rust/issues/57497. Specifying container - # causes all of the steps in this job to run inside a Docker container. + # Build on the oldest platform we are targeting in order to avoid https://github.com/rust-lang/rust/issues/57497. + # Specifying container causes all of the steps in this job to run inside a Docker container. container: ${{ matrix.image }} steps: @@ -80,9 +84,8 @@ jobs: id: setvars shell: bash run: | - # Get the operating system and release name (e.g. ubuntu and xenial) from - # the image name (e.g. ubuntu:xenial) by extracting only the parts before - # and after but not including the colon: + # Get the operating system and release name (e.g. ubuntu and xenial) from the image name (e.g. ubuntu:xenial) by + # extracting only the parts before and after but not including the colon: echo "OS_NAME=${MATRIX_IMAGE%:*}" >> $GITHUB_ENV echo "OS_REL=${MATRIX_IMAGE#*:}" >> $GITHUB_ENV env: @@ -92,12 +95,19 @@ jobs: - name: Checkout repository uses: actions/checkout@v1 - # Install Rust the hard way rather than using a GH Action because the action - # doesn't work inside a Docker container. + # Install Rust the hard way rather than using a GH Action because the action doesn't work inside a Docker container. - name: Install Rust run: | - apt-get update - apt-get install -y curl + case ${OS_NAME} in + debian|ubuntu) + apt-get update + apt-get install -y curl + ;; + centos) + yum update -y + ;; + esac + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --profile minimal -y echo "$HOME/.cargo/bin" >> $GITHUB_PATH env: @@ -105,7 +115,17 @@ jobs: - name: Install compilation and other dependencies run: | - apt-get install -y build-essential jq libssl-dev lintian pkg-config + case ${OS_NAME} in + debian|ubuntu) + apt-get install -y build-essential jq libssl-dev lintian pkg-config + ;; + centos) + yum install epel-release -y + yum update -y + yum install -y jq openssl-devel rpmlint + yum groupinstall -y "Development Tools" + ;; + esac env: DEBIAN_FRONTEND: noninteractive @@ -117,33 +137,56 @@ jobs: path: | ~/.cargo/registry ~/.cargo/git - target key: ${{ matrix.image }}-cargo-${{ hashFiles('**/Cargo.lock') }} - # Speed up cargo-deb installation by only re-downloading and re-building its - # dependent crates if we change the version of cargo-deb that we are using. - - name: Cache Cargo Deb binary + # Speed up tooling installation by only re-downloading and re-building dependent crates if we change the version of + # the tool that we are using. + - name: Cache Cargo Deb if available id: cache-cargo-deb uses: actions/cache@v2 with: path: ~/.cargo/bin/cargo-deb key: ${{ matrix.image }}-cargo-deb-${{ env.CARGO_DEB_VER }} - # Only install cargo-deb if not already fetched from the cache. - - name: Install Cargo Deb + - name: Cache Cargo Generate RPM if available + id: cache-cargo-generate-rpm + uses: actions/cache@v2 + with: + path: ~/.cargo/bin/cargo-generate-rpm + key: ${{ matrix.image }}-cargo-generate-rpm-${{ env.CARGO_GENERATE_RPM_VER }} + + # Only install cargo-deb or cargo-generate-rpm if not already fetched from the cache. + - name: Install Cargo Deb if needed if: steps.cache-cargo-deb.outputs.cache-hit != 'true' run: | - cargo install cargo-deb --version $CARGO_DEB_VER + case ${OS_NAME} in + debian|ubuntu) + cargo install cargo-deb --version ${CARGO_DEB_VER} + ;; + esac - # Instruct cargo-deb to build the Debian package using the config section - # in Cargo.toml for the specified "variant". - - name: Create the DEB package + - name: Install Cargo Generate RPM if needed + if: steps.cache-cargo-generate-rpm.outputs.cache-hit != 'true' run: | - # Packages for different distributions (e.g. Stretch, Buster) of the same - # O/S (e.g. Debian) when served from a single package repository MUST have - # unique package_ver_architecture triples. Cargo deb can vary the name based - # on the 'variant' config section in use, but doesn't do so according to - # Debian policy (as it modifies the package name, not the package version). + case ${OS_NAME} in + centos) + cargo install cargo-generate-rpm --version ${CARGO_GENERATE_RPM_VER} + ;; + esac + + # Instruct cargo-deb or cargo-generate-rpm to build the package based on Cargo.toml settings and command line + # arguments. + - name: Create the package + env: + MATRIX_IMAGE: ${{ matrix.image }} + EXTRA_BUILD_ARGS: ${{ matrix.extra_build_args }} + run: | + # Debian + # ============================================================================================================== + # Packages for different distributions (e.g. Stretch, Buster) of the same O/S (e.g. Debian) when served from a + # single package repository MUST have unique package_ver_architecture triples. Cargo deb can vary the name based + # on the 'variant' config section in use, but doesn't do so according to Debian policy (as it modifies the + # package name, not the package version). # Format: package_ver_architecture # Where ver has format: [epoch:]upstream_version[-debian_revision] # And debian_version should be of the form: 1 @@ -151,68 +194,115 @@ jobs: # See: # - https://unix.stackexchange.com/a/190899 # - https://www.debian.org/doc/debian-policy/ch-controlfields.html#version - # - https://readme.phys.ethz.ch/documentation/debian_version_numbers/ # Therefore we generate the version ourselves. # - # In addition, Semantic Versioning and Debian version policy cannot - # express a pre-release label in the same way. For example 0.8.0-rc.1 - # is a valid Cargo.toml [package].version value but when used as a - # Debian package version 0.8.0-rc.1 would be considered _NEWER_ than - # the final 0.8.0 release. To express this in a Debian compatible way we - # must replace the dash '-' with a tilda '~'. + # In addition, Semantic Versioning and Debian version policy cannot express a pre-release label in the same way. + # For example 0.8.0-rc.1 is a valid Cargo.toml [package].version value but when used as a Debian package version + # 0.8.0-rc.1 would be considered _NEWER_ than the final 0.8.0 release. To express this in a Debian compatible + # way we must replace the dash '-' with a tilda '~'. # - # Finally, sometimes we want a version to be NEWER than the latest - # release but without having to decide what higher semver number to bump - # to. In this case we do NOT want dash '-' to become '~' because `-` - # is treated as higher and tilda is treated as lower. - KRILL_VER=$(cargo read-manifest | jq -r '.version' | tr '-' '~') - DEB_KRILL_VER=$(echo $KRILL_VER | sed -e "s/~$NEXT_VER_LABEL/-$NEXT_VER_LABEL/") + # RPM + # ============================================================================================================== + # Handle the release candidate case where the version string needs to have dash replaced by tilda. The cargo + # build command won't work if the version key in Cargo.toml contains a tilda but we have to put the tilda there + # for when we run cargo generate-rpm so that it uses it. + # + # For background on RPM versioning see: + # https://docs.fedoraproject.org/en-US/packaging-guidelines/Versioning/ + # + # COMMON + # ============================================================================================================== + # Finally, sometimes we want a version to be NEWER than the latest release but without having to decide what + # higher semver number to bump to. In this case we do NOT want dash '-' to become '~' because `-` is treated as + # higher and tilda is treated as lower. + KRILL_VER=$(cargo read-manifest | jq -r '.version') + KRILL_NEW_VER=$(echo $KRILL_VER | tr '-' '~') + PKG_KRILL_VER=$(echo $KRILL_NEW_VER | sed -e "s/~$NEXT_VER_LABEL/-$NEXT_VER_LABEL/") - case ${{ github.event_name }} in - pull_request) MAINTAINER="${{ github.actor }} " ;; - push) MAINTAINER="${{ github.event.pusher.name }} <${{ github.event.pusher.email }}>" ;; - *) echo 2>&1 "ERROR: Unexpected GitHub Actions event"; exit 1 ;; + case ${OS_NAME} in + debian|ubuntu) + case ${{ github.event_name }} in + pull_request) MAINTAINER="${{ github.actor }} " ;; + push) MAINTAINER="${{ github.event.pusher.name }} <${{ github.event.pusher.email }}>" ;; + *) echo 2>&1 "ERROR: Unexpected GitHub Actions event"; exit 1 ;; + esac + + # Generate the RFC 5322 format date by hand instead of using date --rfc-email because that option doesn't exist + # on Ubuntu 16.04 and Debian 9 + RFC5322_TS=$(LC_TIME=en_US.UTF-8 date +'%a, %d %b %Y %H:%M:%S %z') + + # Generate the changelog file that Debian packages are required to have. + # See: https://www.debian.org/doc/manuals/maint-guide/dreq.en.html#changelog + if [ ! -d target/debian ]; then + mkdir -p target/debian + fi + echo "krill (${PKG_KRILL_VER}) unstable; urgency=medium" >target/debian/changelog + echo " * See: https://github.com/NLnetLabs/krill/releases/tag/v${KRILL_NEW_VER}" >>target/debian/changelog + echo " -- maintainer ${MAINTAINER} ${RFC5322_TS}" >>target/debian/changelog + + DEB_VER="${PKG_KRILL_VER}-1${OS_REL}" + cargo deb --variant ${OS_NAME}-${OS_REL} --deb-version ${DEB_VER} -v -- --locked ${EXTRA_BUILD_ARGS} + ;; + centos) + # Build and strip Krill as cargo generate-rpm doesn't do this for us + cargo build --release --locked -v ${EXTRA_BUILD_ARGS} + strip -s target/release/krill + + # Fix the version string to be used for the RPM package + sed -i -e "s/$KRILL_VER/$PKG_KRILL_VER/" Cargo.toml + + # Select the correct systemd service unit file for the target operating system + case ${MATRIX_IMAGE} in + ubuntu:xenial|centos:7) SYSTEMD_SERVICE_UNIT_FILE="krill-ubuntu-xenial.krill.service" ;; + ubuntu:bionic) SYSTEMD_SERVICE_UNIT_FILE="krill-ubuntu-bionic.krill.service" ;; + ubuntu:focal|centos:8) SYSTEMD_SERVICE_UNIT_FILE="krill-ubuntu-focal.krill.service" ;; + debian:stretch) SYSTEMD_SERVICE_UNIT_FILE="krill-debian-stretch.krill.service" ;; + debian:buster) SYSTEMD_SERVICE_UNIT_FILE="krill-debian-buster.krill.service" ;; + debian:bullseye) SYSTEMD_SERVICE_UNIT_FILE="krill-debian-bullseye.krill.service" ;; + *) echo >&2 "ERROR: Unsupported matrix image value: '${MATRIX_IMAGE}'" ;; + esac + + # Copy the chosen systemd service unit file to where Cargo.toml expects it to be + mkdir -p target/rpm + cp pkg/common/${SYSTEMD_SERVICE_UNIT_FILE} target/rpm/krill.service + + cargo generate-rpm + ;; esac - # Generate the RFC 5322 format date by hand instead of using date --rfc-email - # because that option doesn't exist on Ubuntu 16.04 and Debian 9 - RFC5322_TS=$(LC_TIME=en_US.UTF-8 date +'%a, %d %b %Y %H:%M:%S %z') - - # Generate the changelog file that Debian packages are required to have. - # See: https://www.debian.org/doc/manuals/maint-guide/dreq.en.html#changelog - echo "krill (${DEB_KRILL_VER}) unstable; urgency=medium" >debian/changelog - echo " * See: https://github.com/NLnetLabs/krill/releases/tag/v${KRILL_VER}" >>debian/changelog - echo " -- maintainer ${MAINTAINER} ${RFC5322_TS}" >>debian/changelog - - DEB_VER="${DEB_KRILL_VER}-1${OS_REL}" - cargo deb --variant ${OS_NAME}-${OS_REL} --deb-version ${DEB_VER} -v -- --locked - - # See what Lintian thinks of our package. - - name: Verify the DEB package + # See what O/S specific linting tools think of our package. + - name: Verify the package run: | - lintian -v target/debian/*.deb + case ${OS_NAME} in + debian|ubuntu) + lintian -v target/debian/*.deb + ;; + centos) + # cargo generate-rpm creates RPMs that rpmlint considers to have + # errors so don't use the rpmlint exit code otherwise we will always + # abort the workflow. + rpmlint target/generate-rpm/*.rpm || true + ;; + esac - # Upload the produced DEB package. The artifact will be available - # via the GH Actions job summary and build log pages, but only to - # users logged in to GH with sufficient rights in this project. The - # uploaded artifact is also downloaded by the next job (see below) - # to sanity check that it can be installed and results in a working - # Krill installation. - - name: Upload DEB package + # Upload the produced package. The artifact will be available via the GH Actions job summary and build log pages, + # but only to users logged in to GH with sufficient rights in this project. The uploaded artifact is also downloaded + # by the next job (see below) to sanity check that it can be installed and results in a working Krill installation. + - name: Upload package uses: actions/upload-artifact@v2 with: name: ${{ env.OS_NAME }}_${{ env.OS_REL }} - path: target/debian/*.deb + path: | + target/debian/*.deb + target/generate-rpm/*.rpm - # Download and sanity check on target operating systems the packages created - # by previous jobs (see above). Don't test on GH runners as they come with - # lots of software and libraries pre-installed and thus are not representative - # of the actual deployment targets, nor do GH runners support all targets that - # we want to test. Don't test in Docker containers as they do not support - # systemd. - deb-pkg-test: - name: deb-pkg-test - needs: deb-pkg + # Download and sanity check on target operating systems the packages created by previous jobs (see above). Don't test + # on GH runners as they come with lots of software and libraries pre-installed and thus are not representative of the + # actual deployment targets, nor do GH runners support all targets that we want to test. Don't test in Docker + # containers as they do not support systemd. + pkg-test: + name: pkg-test + needs: pkg runs-on: ubuntu-latest strategy: fail-fast: false @@ -224,23 +314,27 @@ jobs: - 'debian:stretch' # debian/9 - 'debian:buster' # debian/10 - 'debian:bullseye' # debian/11 + - 'centos:7' + - 'centos:8' mode: - 'fresh-install' - 'upgrade-from-published' exclude: - image: 'debian:bullseye' mode: 'upgrade-from-published' + - image: 'centos:7' + mode: 'upgrade-from-published' + - image: 'centos:8' + mode: 'upgrade-from-published' steps: - # Set some environment variables that will be available to "run" steps below - # in this job, and some output variables that will be available in GH Action - # step definitions below. + # Set some environment variables that will be available to "run" steps below in this job, and some output variables + # that will be available in GH Action step definitions below. - name: Set vars id: setvars shell: bash run: | - # Get the operating system and release name (e.g. ubuntu and xenial) from - # the image name (e.g. ubuntu:xenial) by extracting only the parts before - # and after but not including the colon: + # Get the operating system and release name (e.g. ubuntu and xenial) from the image name (e.g. ubuntu:xenial) by + # extracting only the parts before and after but not including the colon: OS_NAME=${MATRIX_IMAGE%:*} OS_REL=${MATRIX_IMAGE#*:} @@ -250,7 +344,7 @@ jobs: env: MATRIX_IMAGE: ${{ matrix.image }} - - name: Download DEB package + - name: Download package uses: actions/download-artifact@v2 with: name: ${{ env.OS_NAME }}_${{ env.OS_REL }} @@ -267,19 +361,24 @@ jobs: run: | sg lxd -c "lxc info" + # Use of IPv6 sometimes prevents yum update being able to resolve + # mirrorlist.centos.org. + - name: Disable LXD assignment of IPv6 addresses + run: | + sg lxd -c "lxc network set lxdbr0 ipv6.address none" + - name: Launch LXC container run: | - - # security.nesting=true is needed to avoid error "Failed to set up mount - # namespacing: Permission denied" in a Debian 10 container. + # security.nesting=true is needed to avoid error "Failed to set up mount namespacing: Permission denied" in a + # Debian 10 container. sg lxd -c "lxc launch ${LXC_IMAGE} -c security.nesting=true testcon" - # Run apt-get update and install man and sudo support (missing in some LXC/LXD - # O/S images) but first wait for cloud-init to finish otherwise the network - # isn't yet ready. Don't use cloud-init status --wait as that isn't supported - # on older O/S's like Ubuntu 16.04 and Debian 9. Use the sudo package provided - # configuration files otherwise when using sudo we get an error that the root - # user isn't allowed to use sudo. + # Run package update and install man and sudo support (missing in some + # LXC/LXD O/S images) but first wait for cloud-init to finish otherwise the + # network isn't yet ready. Don't use cloud-init status --wait as that isn't + # supported on older O/S's like Ubuntu 16.04 and Debian 9. Use the sudo + # package provided configuration files otherwise when using sudo we get an + # error that the root user isn't allowed to use sudo. - name: Prepare container shell: bash run: | @@ -287,29 +386,66 @@ jobs: while ! sudo lxc exec testcon -- ls -la /var/lib/cloud/data/result.json; do sleep 1s done - sg lxd -c "lxc exec testcon -- apt-get update" - sg lxd -c "lxc exec testcon -- apt-get install -y -o Dpkg::Options::=\"--force-confnew\" apt-transport-https gnupg2 man sudo wget" - - name: Copy DEB into LXC container + case ${OS_NAME} in + debian|ubuntu) + sg lxd -c "lxc exec testcon -- apt-get update" + sg lxd -c "lxc exec testcon -- apt-get install -y -o Dpkg::Options::=\"--force-confnew\" apt-transport-https ca-certificates man sudo wget" + ;; + centos) + sg lxd -c "lxc exec testcon -- yum update -y" + sg lxd -c "lxc exec testcon -- yum install -y man" + ;; + esac + + - name: Copy package into the LXC container run: | - DEB_FILE=$(ls -1 *.deb) - sg lxd -c "lxc file push ${DEB_FILE} testcon/tmp/" - echo "DEB_FILE=${DEB_FILE}" >> $GITHUB_ENV + case ${OS_NAME} in + debian|ubuntu) + DEB_FILE=$(ls -1 debian/*.deb) + sg lxd -c "lxc file push ${DEB_FILE} testcon/tmp/" + echo "PKG_FILE=$(basename $DEB_FILE)" >> $GITHUB_ENV + ;; + centos) + RPM_FILE=$(ls -1 generate-rpm/*.rpm) + sg lxd -c "lxc file push ${RPM_FILE} testcon/tmp/" + echo "PKG_FILE=$(basename $RPM_FILE)" >> $GITHUB_ENV + ;; + esac - - name: Install published DEB package + - name: Install previously published package if: ${{ matrix.mode == 'upgrade-from-published' }} run: | - echo "deb [arch=amd64] https://packages.nlnetlabs.nl/linux/${OS_NAME}/ ${OS_REL} main" >$HOME/nlnetlabs.list - sg lxd -c "lxc file push $HOME/nlnetlabs.list testcon/etc/apt/sources.list.d/" - sg lxd -c "lxc exec testcon -- wget -q https://packages.nlnetlabs.nl/aptkey.asc" - sg lxd -c "lxc exec testcon -- apt-key add ./aptkey.asc" - sg lxd -c "lxc exec testcon -- apt update" - sg lxd -c "lxc exec testcon -- apt install -y krill" + case ${OS_NAME} in + debian|ubuntu) + echo "deb [arch=amd64] https://packages.nlnetlabs.nl/linux/${OS_NAME}/ ${OS_REL} main" >$HOME/nlnetlabs.list + sg lxd -c "lxc file push $HOME/nlnetlabs.list testcon/etc/apt/sources.list.d/" + sg lxd -c "lxc exec testcon -- wget -q https://packages.nlnetlabs.nl/aptkey.asc" + sg lxd -c "lxc exec testcon -- apt-key add ./aptkey.asc" + sg lxd -c "lxc exec testcon -- apt update" + sg lxd -c "lxc exec testcon -- apt install -y krill" + ;; + centos) + rpm --import https://packages.nlnetlabs.nl/aptkey.asc + cat "[nlnetlabs]" >/etc/yum.repos.d/nlnetlabs.repo + cat "name=NLnet Labs" >>/etc/yum.repos.d/nlnetlabs.repo + cat "baseurl=https://packages.nlnetlabs.nl/linux/centos/$releasever/main/$basearch" >>/etc/yum.repos.d/nlnetlabs.repo + cat "enabled=1" >>/etc/yum.repos.d/nlnetlabs.repo + yum install -y krill + ;; + esac - - name: Install new DEB package + - name: Install new package if: ${{ matrix.mode == 'fresh-install' }} run: | - sg lxd -c "lxc exec testcon -- apt-get -y install /tmp/${DEB_FILE}" + case ${OS_NAME} in + debian|ubuntu) + sg lxd -c "lxc exec testcon -- apt-get -y install /tmp/${PKG_FILE}" + ;; + centos) + sg lxd -c "lxc exec testcon -- yum install -y /tmp/${PKG_FILE}" + ;; + esac - name: Test installed packages run: | @@ -344,10 +480,17 @@ jobs: echo -e "\nKRILL MAN PAGE:" sg lxd -c "lxc exec testcon -- man -P cat krill" - - name: Install new DEB package + - name: Install new package if: ${{ matrix.mode == 'upgrade-from-published' }} run: | - sg lxd -c "lxc exec testcon -- apt-get -y install /tmp/${DEB_FILE}" + case ${OS_NAME} in + debian|ubuntu) + sg lxd -c "lxc exec testcon -- apt-get -y install /tmp/${PKG_FILE}" + ;; + centos) + sg lxd -c "lxc exec testcon -- yum install -y /tmp/${PKG_FILE}" + ;; + esac - name: Test installed packages if: ${{ matrix.mode == 'upgrade-from-published' }} diff --git a/.rpm/krill.spec b/.rpm/krill.spec deleted file mode 100644 index 20250379..00000000 --- a/.rpm/krill.spec +++ /dev/null @@ -1,32 +0,0 @@ -%define __spec_install_post %{nil} -%define __os_install_post %{_dbpath}/brp-compress -%define debug_package %{nil} - -Name: krill -Summary: Resource Public Key Infrastructure (RPKI) daemon -Version: @@VERSION@@ -Release: @@RELEASE@@ -License: MPLv2.0 -Group: Applications/System -Source0: %{name}-%{version}.tar.gz -URL: https://www.nlnetlabs.nl/projects/rpki/krill/ - -BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root - -%description -%{summary} - -%prep -%setup -q - -%install -rm -rf %{buildroot} -mkdir -p %{buildroot} -cp -a * %{buildroot} - -%clean -rm -rf %{buildroot} - -%files -%defattr(-,root,root,-) -%{_bindir}/* diff --git a/Cargo.toml b/Cargo.toml index 8621b070..7eab324a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,11 +106,11 @@ ctrlc = "^3.1" name = "krill" priority = "optional" section = "net" -extended-description-file = "debian/description.txt" +extended-description-file = "pkg/debian/description.txt" license-file = ["LICENSE", "0"] -depends = "$auto, adduser, libssl1.1" -maintainer-scripts = "debian/" -changelog = "debian/changelog" # this will be generated by the pkg workflow +depends = "$auto, passwd, libssl1.1" +maintainer-scripts = "pkg/debian/" +changelog = "target/debian/changelog" # this will be generated by the pkg workflow copyright = "Copyright (c) 2019, NLnet Labs. All rights reserved." assets = [ ["target/release/krill", "/usr/bin/krill", "755"], @@ -118,9 +118,9 @@ assets = [ ["defaults/krill.conf", "/usr/share/doc/krill/krill.conf", "644"], ["doc/krill.1", "/usr/share/man/man1/krill.1", "644"], ["doc/krillc.1", "/usr/share/man/man1/krillc.1", "644"], - ["debian/krill.service.preset", "/lib/systemd/system-preset/50-krill.preset", "644"], + ["pkg/common/krill.service.preset", "/lib/systemd/system-preset/50-krill.preset", "644"], ] -systemd-units = { unit-name = "krill", enable = false } +systemd-units = { unit-name = "krill", unit-scripts = "pkg/common", enable = false } # Variant of the Debian packaging configuration that: # a) statically links with OpenSSL when building a Debian package because the @@ -131,7 +131,7 @@ systemd-units = { unit-name = "krill", enable = false } # support newer features supported by Ubuntu 18.04 and 20.04. [package.metadata.deb.variants.ubuntu-xenial] features = [ "static-openssl" ] -depends = "$auto, adduser" +depends = "$auto, passwd" [package.metadata.deb.variants.ubuntu-bionic] @@ -139,7 +139,7 @@ depends = "$auto, adduser" [package.metadata.deb.variants.debian-stretch] features = [ "static-openssl" ] -depends = "$auto, adduser" +depends = "$auto, passwd" [package.metadata.deb.variants.debian-buster] @@ -147,3 +147,33 @@ depends = "$auto, adduser" # END DEBIAN PACKAGING # ------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ +# START RPM PACKAGING +# +# Configurations for the cargo-generate-rpm cargo plugin which builds RPM +# packages in target/generate-rpm/ when invoked with: cargo generate-rpm +# +[package.metadata.generate-rpm] +# See: https://fedoraproject.org/wiki/Licensing:Main?rd=Licensing +license = "MPLv2.0" +assets = [ + { source = "target/release/krill", dest = "/usr/bin/krill", mode = "755" }, + { source = "target/release/krillc", dest = "/usr/bin/krillc", mode = "755" }, + { source = "target/rpm/krill.service", dest = "/lib/systemd/system/krill.service", mode = "644" }, + { source = "defaults/krill.conf", dest = "/usr/share/doc/krill/krill.conf", mode = "644", doc = true }, + { source = "doc/krill.1", dest = "/usr/share/man/man1/krill.1", mode = "644", doc = true }, + { source = "doc/krillc.1", dest = "/usr/share/man/man1/krillc.1", mode = "644", doc = true }, + { source = "pkg/common/krill.service.preset", dest = "/lib/systemd/system-preset/50-krill.preset", mode = "644" }, + { source = "pkg/rpm/postinst", dest = "/usr/share/krill/rpm/postinst", mode = "755" }, + { source = "pkg/rpm/postrm", dest = "/usr/share/krill/rpm/postrm", mode = "755" }, +] +post_install_script = "/usr/share/krill/rpm/postinst $*" +post_uninstall_script = "/usr/share/krill/rpm/postrm $*" + +# ensure that the useradd tool is present by depending on its package +[package.metadata.generate-rpm.requires] +shadow-utils = "*" + +# END RPM PACKAGING +# ------------------------------------------------------------------------------ diff --git a/Changelog.md b/Changelog.md index ffdfb094..e09149f7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,16 @@ # Change Log +## 0.9.1-RC1 'All for One' + +Note: this is an RC release meant for testing. If no issues are found we will release it +on Monday 19 July 2021. + +This release fixes an issue where the Publication Server would lock up (#606). Users who do +not use Krill to operate their own Publication Server do not need to upgrade to this release. + +This locking issue was cause by slow deserialisation of the repository content. It primarily +affected large repositories because more content makes this process slower, and having more +publishers who publish regularly means it is triggered more frequently. ## 0.9.0 'One for All' diff --git a/defaults/krill.conf b/defaults/krill.conf index e02620f3..2b4d74c9 100644 --- a/defaults/krill.conf +++ b/defaults/krill.conf @@ -115,8 +115,8 @@ # CA certificate refresh rate # -# This defines the rate, in seconds, for Krill CAs to to contact their parent -# CA and query for updates in resource entitlements. +# This defines the rate, in seconds, for Krill CAs to to contact their parent CA +# via the RFC 6492 up-down protocol and query for updates in resource entitlements. # # Defaults to 10 minutes # diff --git a/doc/development/05_repo_manager.md b/doc/development/05_repo_manager.md index d389d33f..69b77da8 100644 --- a/doc/development/05_repo_manager.md +++ b/doc/development/05_repo_manager.md @@ -43,6 +43,7 @@ pub struct RepositoryAccessProxy { /// so that callers don't need to worry about storage details. #[derive(Debug)] pub struct RepositoryContentProxy { + cache: RwLock>>, store: RwLock, key: KeyStoreKey, } diff --git a/doc/development/multi_user/images/cypress-test-running.png b/doc/development/multi_user/images/cypress-test-running.png new file mode 100644 index 00000000..570326a6 Binary files /dev/null and b/doc/development/multi_user/images/cypress-test-running.png differ diff --git a/doc/development/multi_user/images/cypress-welcome-popup.png b/doc/development/multi_user/images/cypress-welcome-popup.png new file mode 100644 index 00000000..ce3bbc90 Binary files /dev/null and b/doc/development/multi_user/images/cypress-welcome-popup.png differ diff --git a/doc/development/multi_user/testing.md b/doc/development/multi_user/testing.md index 6dc01cae..3b14c867 100644 --- a/doc/development/multi_user/testing.md +++ b/doc/development/multi_user/testing.md @@ -25,11 +25,33 @@ this has not yet been verified as working on Mac OS. Cypress in turn is driven b As UI based tests can be quite slow they are gated behind their own `ui-tests` feature, which also has the benefit that users without a working Docker setup are still able to run `cargo test`. -To run the UI tests one must therefore do: `cargo test --features ui-tests`. +To run the UI tests one must therefore do: + +``` +cargo test --features ui-tests +``` Cypress has a very useful interactive test run mode which can be launched like so: ``` $ xhost + -$ CYPRESS_INTERACTIVE=1 cargo test --features ui-tests -``` \ No newline at end of file +$ CYPRESS_INTERACTIVE=1 cargo test --features ui-tests +``` + +You want the `` because you want Krill to be setup correctly to run a particular test that you will then run interactively, you don't want Krill to run all tests and constantly be changing the backend state as a result while you try to use Cypress to run a single test suite that has expectations about the state that Krill is in. + +For example you might do: +``` +$ xhost + +$ CYPRESS_INTERACTIVE=1 cargo test --features ui-tests multi_user_config_file_with_ta +``` + +After a short delay a browser window should open with a Cypress welcome message something like this: + +![Cypress welcome popup](images/cypress-welcome-popup.png) + +Dismiss the message and then click on the `multi_user_config_file_with_ta.js` test in the tree of tests that is shown to you, i.e. run the same test as you invoked with `cargo test` so that Krill has the expected configuration and starting conditions. + +You should then see something like this: + +![Cypress test running](images/cypress-test-running.png) diff --git a/debian/krill-debian-bullseye.krill.service b/pkg/common/krill-debian-bullseye.krill.service similarity index 100% rename from debian/krill-debian-bullseye.krill.service rename to pkg/common/krill-debian-bullseye.krill.service diff --git a/debian/krill-debian-buster.krill.service b/pkg/common/krill-debian-buster.krill.service similarity index 100% rename from debian/krill-debian-buster.krill.service rename to pkg/common/krill-debian-buster.krill.service diff --git a/debian/krill-debian-stretch.krill.service b/pkg/common/krill-debian-stretch.krill.service similarity index 100% rename from debian/krill-debian-stretch.krill.service rename to pkg/common/krill-debian-stretch.krill.service diff --git a/debian/krill-ubuntu-bionic.krill.service b/pkg/common/krill-ubuntu-bionic.krill.service similarity index 100% rename from debian/krill-ubuntu-bionic.krill.service rename to pkg/common/krill-ubuntu-bionic.krill.service diff --git a/debian/krill-ubuntu-focal.krill.service b/pkg/common/krill-ubuntu-focal.krill.service similarity index 100% rename from debian/krill-ubuntu-focal.krill.service rename to pkg/common/krill-ubuntu-focal.krill.service diff --git a/debian/krill-ubuntu-xenial.krill.service b/pkg/common/krill-ubuntu-xenial.krill.service similarity index 100% rename from debian/krill-ubuntu-xenial.krill.service rename to pkg/common/krill-ubuntu-xenial.krill.service diff --git a/debian/krill.service.preset b/pkg/common/krill.service.preset similarity index 100% rename from debian/krill.service.preset rename to pkg/common/krill.service.preset diff --git a/debian/description.txt b/pkg/debian/description.txt similarity index 100% rename from debian/description.txt rename to pkg/debian/description.txt diff --git a/debian/postinst b/pkg/debian/postinst similarity index 100% rename from debian/postinst rename to pkg/debian/postinst diff --git a/debian/postrm b/pkg/debian/postrm similarity index 100% rename from debian/postrm rename to pkg/debian/postrm diff --git a/debian/preinst b/pkg/debian/preinst similarity index 100% rename from debian/preinst rename to pkg/debian/preinst diff --git a/pkg/rpm/postinst b/pkg/rpm/postinst new file mode 100755 index 00000000..e12f9448 --- /dev/null +++ b/pkg/rpm/postinst @@ -0,0 +1,66 @@ +#!/bin/bash -e +# Script based on the RPM %systemd_post scriptlet. See: +# - https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_systemd +# - https://cgit.freedesktop.org/systemd/systemd/tree/src/core/macros.systemd.in + +KRILL_USER=krill +KRILL_GROUP=${KRILL_USER} +KRILL_HOME_DIR="/var/lib/krill" +KRILL_HOME_DIR_PERMS=700 +KRILL_CONF="/etc/krill.conf" +KRILL_DATA="${KRILL_HOME_DIR}/data/" + +if [ $EUID -ne 0 ]; then + echo >&2 "ERROR: RTRTR postinst script must be run as root" + exit 1 +fi + +create_user() { + # According to the CentOS 7 useradd man page: + # --user-group causes a group by the same name as the user to be created + # --create-home should force creation of a home dir even for a system account. + useradd --system --home-dir ${KRILL_HOME_DIR} --system --create-home --user-group ${KRILL_USER} + + # Ensure that the home directory has the correct ownership + chown -R ${KRILL_USER}:${KRILL_GROUP} ${KRILL_HOME_DIR} + + # Ensure that the home directory has the correct permissions + chmod ${KRILL_HOME_DIR_PERMS} ${KRILL_HOME_DIR} +} + +init_systemd_service() { + systemctl preset krill.service 2>&1 || : +} + +generate_password() { + # Tries not to depend on too many other commmands + # being installed. + date | md5sum | awk '{print $1}' +} + +create_first_time_configuration() { + if [ ! -f "${KRILL_CONF}" ]; then + # generate a token for authenticating with Krill + ADMIN_TOKEN="$(generate_password)" + + # generate a config file using our preferred filesystem locations + # and generated admin token + # note: we don't configure Krill to store its PID file under /var/run/ + # because that requires root privileges potentially at least once per + # boot, and Krill doesn't drop privileges yet so when run as a non-root + # user has no right to create the file or missing /var/run/subdir. + # See: https://stackoverflow.com/a/28312577 + krillc config simple \ + --data "${KRILL_DATA}" \ + --token "${ADMIN_TOKEN}" | + sed -e "s|^\(### log_type.\+\)|\1\nlog_type = \"syslog\"|" \ + > "${KRILL_CONF}" + fi +} + +if [ $1 -eq 1 ] ; then + # Initial installation + create_user + init_systemd_service + create_first_time_configuration +fi \ No newline at end of file diff --git a/pkg/rpm/postrm b/pkg/rpm/postrm new file mode 100644 index 00000000..9b3dc5e9 --- /dev/null +++ b/pkg/rpm/postrm @@ -0,0 +1,9 @@ +#!/bin/bash -e +# Script based on the RPM %systemd_postun scriptlet. See: +# - https://docs.fedoraproject.org/en-US/packaging-guidelines/Scriptlets/#_systemd +# - https://cgit.freedesktop.org/systemd/systemd/tree/src/core/macros.systemd.in + +systemctl daemon-reload >/dev/null 2>&1 || : +if [ $1 -ge 1 ] ; then + systemctl try-restart krill.service >/dev/null 2>&1 || : +fi \ No newline at end of file diff --git a/src/daemon/http/mod.rs b/src/daemon/http/mod.rs index 403a82b8..7e7a85d8 100644 --- a/src/daemon/http/mod.rs +++ b/src/daemon/http/mod.rs @@ -339,7 +339,7 @@ pub struct Request { impl Request { pub async fn new(request: hyper::Request, state: State) -> Self { let path = RequestPath::from_request(&request); - let actor = state.read().await.actor_from_request(&request); + let actor = state.actor_from_request(&request); Request { request, @@ -355,7 +355,7 @@ impl Request { pub async fn upgrade_from_anonymous(&mut self, actor_def: ActorDef) { if self.actor.is_anonymous() { - self.actor = self.state.read().await.actor_from_def(actor_def); + self.actor = self.state.actor_from_def(actor_def); info!( "Permitted anonymous actor to become actor '{}' for the duration of this request", self.actor.name() @@ -410,17 +410,17 @@ impl Request { } pub async fn api_bytes(self) -> Result { - let limit = self.state().read().await.limit_api(); + let limit = self.state().limit_api(); self.read_bytes(limit).await } pub async fn rfc6492_bytes(self) -> Result { - let limit = self.state().read().await.limit_rfc6492(); + let limit = self.state().limit_rfc6492(); self.read_bytes(limit).await } pub async fn rfc8181_bytes(self) -> Result { - let limit = self.state().read().await.limit_rfc8181(); + let limit = self.state().limit_rfc8181(); self.read_bytes(limit).await } @@ -488,15 +488,15 @@ impl Request { } pub async fn get_login_url(&self) -> KrillResult { - self.state.read().await.get_login_url() + self.state.get_login_url() } pub async fn login(&self) -> KrillResult { - self.state.read().await.login(&self.request) + self.state.login(&self.request) } pub async fn logout(&self) -> KrillResult { - self.state.read().await.logout(&self.request) + self.state.logout(&self.request) } } diff --git a/src/daemon/http/server.rs b/src/daemon/http/server.rs index 834ebeb5..6986d049 100644 --- a/src/daemon/http/server.rs +++ b/src/daemon/http/server.rs @@ -13,8 +13,6 @@ use std::sync::Arc; use bytes::Bytes; use serde::Serialize; -use tokio::sync::RwLock; - use futures::TryFutureExt; use hyper::header::HeaderName; use hyper::http::HeaderValue; @@ -52,7 +50,7 @@ use crate::{ //------------ State ----------------------------------------------------- -pub type State = Arc>; +pub type State = Arc; pub fn parse_config() -> KrillResult { Config::create().map_err(|e| Error::Custom(format!("Could not parse config: {}", e))) @@ -129,7 +127,7 @@ pub async fn start_krill_daemon(config: Arc) -> Result<(), Error> { println!("Krill upgrade successful"); } - let state = Arc::new(RwLock::new(krill)); + let state = Arc::new(krill); let service = make_service_fn(move |_| { let state = state.clone(); @@ -357,7 +355,6 @@ pub async fn health(req: Request) -> RoutingResult { pub async fn metrics(req: Request) -> RoutingResult { if req.is_get() && req.path().segment().starts_with("metrics") { let server = req.state(); - let server = server.read().await; struct AllBgpStats { announcements_valid: HashMap, @@ -618,8 +615,7 @@ pub async fn rfc8181(req: Request) -> RoutingResult { Err(e) => return render_error(e), }; - let read = state.read().await; - match read.rfc8181(publisher, bytes) { + match state.rfc8181(publisher, bytes) { Ok(bytes) => Ok(HttpResponse::rfc8181(bytes.to_vec())), Err(e) => render_error(e), } @@ -642,14 +638,14 @@ async fn ta(req: Request) -> RoutingResult { } pub async fn tal(req: Request) -> RoutingResult { - match req.state().read().await.ta().await { + match req.state().ta().await { Ok(ta) => Ok(HttpResponse::text(format!("{}", ta.tal()).into_bytes())), Err(_) => render_unknown_resource(), } } pub async fn ta_cer(req: Request) -> RoutingResult { - match req.state().read().await.trust_anchor_cert().await { + match req.state().trust_anchor_cert().await { Some(cert) => Ok(HttpResponse::cert(cert.to_captured().to_vec())), None => render_unknown_resource(), } @@ -674,7 +670,7 @@ pub async fn rfc6492(req: Request) -> RoutingResult { Ok(bytes) => bytes, Err(e) => return render_error(e), }; - let krill_server = state.read().await; + let krill_server = state; match krill_server.rfc6492(ca, bytes, &actor).await { Ok(bytes) => Ok(HttpResponse::rfc6492(bytes.to_vec())), Err(e) => render_error(e), @@ -688,9 +684,9 @@ pub async fn rfc6492(req: Request) -> RoutingResult { async fn stats(req: Request) -> RoutingResult { match *req.method() { Method::GET => match req.path().full() { - "/stats/info" => render_json(req.state().read().await.server_info()), - "/stats/repo" => render_json_res(req.state().read().await.repo_stats()), - "/stats/cas" => render_json_res(req.state().read().await.cas_stats().await), + "/stats/info" => render_json(req.state().server_info()), + "/stats/repo" => render_json_res(req.state().repo_stats()), + "/stats/cas" => render_json_res(req.state().cas_stats().await), _ => Err(req), }, _ => Err(req), @@ -951,11 +947,11 @@ async fn api_publication_server(req: Request, path: &mut RequestPath) -> Routing Method::POST => { let state = req.state.clone(); match req.json().await { - Ok(uris) => render_empty_res(state.write().await.repository_init(uris)), + Ok(uris) => render_empty_res(state.repository_init(uris)), Err(e) => render_error(e), } } - Method::DELETE => render_empty_res(req.state.write().await.repository_clear()), + Method::DELETE => render_empty_res(req.state.repository_clear()), _ => render_unknown_method(), }, _ => render_unknown_method(), @@ -995,10 +991,7 @@ pub async fn api_stale_publishers(req: Request, seconds: Option<&str>) -> Routin let seconds = seconds.unwrap_or(""); match i64::from_str(seconds) { Ok(seconds) => render_json_res( - req.state() - .read() - .await - .repo_stats() + req.state().repo_stats() .map(|stats| PublisherList::build(&stats.stale_publishers(seconds))), ), Err(_) => render_error(Error::ApiInvalidSeconds), @@ -1010,11 +1003,7 @@ pub async fn api_stale_publishers(req: Request, seconds: Option<&str>) -> Routin pub async fn api_list_pbl(req: Request) -> RoutingResult { aa!(req, Permission::PUB_LIST, { render_json_res( - req.state() - .read() - .await - .publishers() - .map(|publishers| PublisherList::build(&publishers)), + req.state().publishers().map(|publishers| PublisherList::build(&publishers)), ) }) } @@ -1025,7 +1014,7 @@ pub async fn api_add_pbl(req: Request) -> RoutingResult { let actor = req.actor(); let server = req.state().clone(); match req.json().await { - Ok(pbl) => render_json_res(server.write().await.add_publisher(pbl, &actor)), + Ok(pbl) => render_json_res(server.add_publisher(pbl, &actor)), Err(e) => render_error(e), } }) @@ -1036,7 +1025,7 @@ pub async fn api_add_pbl(req: Request) -> RoutingResult { pub async fn api_remove_pbl(req: Request, publisher: Handle) -> RoutingResult { aa!(req, Permission::PUB_DELETE, publisher.clone(), { let actor = req.actor(); - render_empty_res(req.state().write().await.remove_publisher(publisher, &actor)) + render_empty_res(req.state().remove_publisher(publisher, &actor)) }) } @@ -1046,7 +1035,7 @@ pub async fn api_show_pbl(req: Request, publisher: Handle) -> RoutingResult { req, Permission::PUB_READ, publisher.clone(), - render_json_res(req.state().read().await.get_publisher(&publisher)) + render_json_res(req.state().get_publisher(&publisher)) ) } @@ -1071,7 +1060,7 @@ pub async fn api_repository_response_json(req: Request, publisher: Handle) -> Ro } async fn repository_response(req: &Request, publisher: &Handle) -> Result { - req.state().read().await.repository_response(publisher) + req.state().repository_response(publisher) } pub async fn api_ca_add_child(req: Request, parent: ParentHandle) -> RoutingResult { @@ -1079,7 +1068,7 @@ pub async fn api_ca_add_child(req: Request, parent: ParentHandle) -> RoutingResu let actor = req.actor(); let server = req.state().clone(); match req.json().await { - Ok(child_req) => render_json_res(server.read().await.ca_add_child(&parent, child_req, &actor).await), + Ok(child_req) => render_json_res(server.ca_add_child(&parent, child_req, &actor).await), Err(e) => render_error(e), } }) @@ -1090,7 +1079,7 @@ async fn api_ca_child_update(req: Request, ca: Handle, child: ChildHandle) -> Ro let actor = req.actor(); let server = req.state().clone(); match req.json().await { - Ok(child_req) => render_empty_res(server.read().await.ca_child_update(&ca, child, child_req, &actor).await), + Ok(child_req) => render_empty_res(server.ca_child_update(&ca, child, child_req, &actor).await), Err(e) => render_error(e), } }) @@ -1099,7 +1088,7 @@ async fn api_ca_child_update(req: Request, ca: Handle, child: ChildHandle) -> Ro pub async fn api_ca_child_remove(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult { aa!(req, Permission::CA_UPDATE, ca.clone(), { let actor = req.actor(); - render_empty_res(req.state().read().await.ca_child_remove(&ca, child, &actor).await) + render_empty_res(req.state().ca_child_remove(&ca, child, &actor).await) }) } @@ -1108,7 +1097,7 @@ async fn api_ca_child_show(req: Request, ca: Handle, child: ChildHandle) -> Rout req, Permission::CA_READ, ca.clone(), - render_json_res(req.state().read().await.ca_child_show(&ca, &child).await) + render_json_res(req.state().ca_child_show(&ca, &child).await) ) } @@ -1117,7 +1106,7 @@ async fn api_ca_parent_contact(req: Request, ca: Handle, child: ChildHandle) -> req, Permission::CA_READ, ca.clone(), - render_json_res(req.state().read().await.ca_parent_contact(&ca, child.clone()).await) + render_json_res(req.state().ca_parent_contact(&ca, child.clone()).await) ) } @@ -1126,13 +1115,13 @@ async fn api_ca_parent_res_json(req: Request, ca: Handle, child: ChildHandle) -> req, Permission::CA_READ, ca.clone(), - render_json_res(req.state().read().await.ca_parent_response(&ca, child.clone()).await) + render_json_res(req.state().ca_parent_response(&ca, child.clone()).await) ) } pub async fn api_ca_parent_res_xml(req: Request, ca: Handle, child: ChildHandle) -> RoutingResult { aa!(req, Permission::CA_READ, ca.clone(), { - match req.state().read().await.ca_parent_response(&ca, child.clone()).await { + match req.state().ca_parent_response(&ca, child.clone()).await { Ok(res) => Ok(HttpResponse::xml(res.encode_vec())), Err(e) => render_error(e), } @@ -1145,7 +1134,7 @@ async fn api_all_ca_issues(req: Request) -> RoutingResult { match *req.method() { Method::GET => aa!(req, Permission::CA_READ, { let actor = req.actor(); - render_json_res(req.state().read().await.all_ca_issues(&actor).await) + render_json_res(req.state().all_ca_issues(&actor).await) }), _ => render_unknown_method(), } @@ -1158,7 +1147,7 @@ async fn api_ca_issues(req: Request, ca: Handle) -> RoutingResult { req, Permission::CA_READ, ca.clone(), - render_json_res(req.state().read().await.ca_issues(&ca).await) + render_json_res(req.state().ca_issues(&ca).await) ), _ => render_unknown_method(), } @@ -1167,7 +1156,7 @@ async fn api_ca_issues(req: Request, ca: Handle) -> RoutingResult { async fn api_cas_list(req: Request) -> RoutingResult { aa!(req, Permission::CA_LIST, { let actor = req.actor(); - render_json_res(req.state().read().await.ca_list(&actor)) + render_json_res(req.state().ca_list(&actor)) }) } @@ -1176,7 +1165,7 @@ pub async fn api_ca_init(req: Request) -> RoutingResult { let state = req.state().clone(); match req.json().await { - Ok(ca_init) => render_empty_res(state.write().await.ca_init(ca_init).await), + Ok(ca_init) => render_empty_res(state.ca_init(ca_init)), Err(e) => render_error(e), } }) @@ -1186,7 +1175,7 @@ async fn api_ca_id(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingR match *req.method() { Method::POST => aa!(req, Permission::CA_UPDATE, ca.clone(), { let actor = req.actor(); - render_empty_res(req.state().read().await.ca_update_id(ca, &actor).await) + render_empty_res(req.state().ca_update_id(ca, &actor).await) }), Method::GET => match path.next() { Some("child_request.xml") => api_ca_child_req_xml(req, ca).await, @@ -1204,7 +1193,7 @@ async fn api_ca_info(req: Request, handle: Handle) -> RoutingResult { req, Permission::CA_READ, handle.clone(), - render_json_res(req.state().read().await.ca_info(&handle).await) + render_json_res(req.state().ca_info(&handle).await) ) } @@ -1214,7 +1203,7 @@ async fn api_ca_delete(req: Request, handle: Handle) -> RoutingResult { req, Permission::CA_DELETE, handle.clone(), - render_json_res(req.state().read().await.ca_delete(&handle, &actor).await) + render_json_res(req.state().ca_delete(&handle, &actor).await) ) } @@ -1223,7 +1212,7 @@ async fn api_ca_my_parent_contact(req: Request, ca: Handle, parent: ParentHandle req, Permission::CA_READ, ca.clone(), - render_json_res(req.state().read().await.ca_my_parent_contact(&ca, &parent).await) + render_json_res(req.state().ca_my_parent_contact(&ca, &parent).await) ) } @@ -1232,7 +1221,7 @@ async fn api_ca_my_parent_statuses(req: Request, ca: Handle) -> RoutingResult { req, Permission::CA_READ, ca.clone(), - render_json_res(req.state().read().await.ca_my_parent_statuses(&ca).await) + render_json_res(req.state().ca_my_parent_statuses(&ca).await) ) } @@ -1278,7 +1267,7 @@ async fn api_ca_history_commands(req: Request, path: &mut RequestPath, handle: H if let Some(before) = path.path_arg() { crit.set_before(before); } - match req.state().read().await.ca_history(&handle, crit).await { + match req.state().ca_history(&handle, crit).await { Ok(history) => render_json(history), Err(e) => render_error(e), } @@ -1300,7 +1289,7 @@ async fn api_ca_command_details(req: Request, path: &mut RequestPath, handle: Ha match path.path_arg() { Some(key) => match *req.method() { Method::GET => aa!(req, Permission::CA_READ, handle.clone(), { - match req.state().read().await.ca_command_details(&handle, key) { + match req.state().ca_command_details(&handle, key) { Ok(details) => render_json(details), Err(e) => match e { Error::AggregateStoreError(AggregateStoreError::UnknownCommand(_, _)) => { @@ -1347,7 +1336,7 @@ async fn api_ca_child_req_json(req: Request, handle: Handle) -> RoutingResult { } async fn ca_child_req(req: &Request, handle: &Handle) -> Result { - req.state().read().await.ca_child_req(handle).await + req.state().ca_child_req(handle).await } async fn api_ca_publisher_req_json(req: Request, handle: Handle) -> RoutingResult { @@ -1356,7 +1345,7 @@ async fn api_ca_publisher_req_json(req: Request, handle: Handle) -> RoutingResul req, Permission::CA_READ, handle.clone(), - render_json_res(req.state().read().await.ca_publisher_req(&handle).await) + render_json_res(req.state().ca_publisher_req(&handle).await) ), _ => render_unknown_method(), } @@ -1368,7 +1357,7 @@ async fn api_ca_publisher_req_xml(req: Request, handle: Handle) -> RoutingResult req, Permission::CA_READ, handle.clone(), - match req.state().read().await.ca_publisher_req(&handle).await { + match req.state().ca_publisher_req(&handle).await { Ok(res) => Ok(HttpResponse::xml(res.encode_vec())), Err(e) => render_error(e), } @@ -1382,7 +1371,7 @@ async fn api_ca_repo_details(req: Request, handle: Handle) -> RoutingResult { req, Permission::CA_READ, handle.clone(), - render_json_res(req.state().read().await.ca_repo_details(&handle).await) + render_json_res(req.state().ca_repo_details(&handle).await) ) } @@ -1392,7 +1381,7 @@ async fn api_ca_repo_status(req: Request, handle: Handle) -> RoutingResult { req, Permission::CA_READ, handle.clone(), - render_json_res(req.state().read().await.ca_repo_status(&handle).await) + render_json_res(req.state().ca_repo_status(&handle).await) ), _ => render_unknown_method(), } @@ -1425,7 +1414,7 @@ async fn api_ca_repo_update(req: Request, handle: Handle) -> RoutingResult { .await .map(|bytes| extract_repository_contact(&handle, bytes)) { - Ok(Ok(update)) => render_empty_res(server.read().await.ca_repo_update(handle, update, &actor).await), + Ok(Ok(update)) => render_empty_res(server.ca_repo_update(handle, update, &actor).await), Ok(Err(e)) | Err(e) => render_error(e), } }) @@ -1443,11 +1432,7 @@ async fn api_ca_parent_add_or_update(req: Request, ca: Handle, parent_override: match extract_parent_ca_req(&ca, bytes, parent_override) { Ok(parent_req) => render_empty_res( - server - .read() - .await - .ca_parent_add_or_update(ca, parent_req, &actor) - .await, + server.ca_parent_add_or_update(ca, parent_req, &actor).await, ), Err(e) => render_error(e), } @@ -1489,7 +1474,7 @@ fn extract_parent_ca_req(ca: &Handle, bytes: Bytes, parent_override: Option RoutingResult { aa!(req, Permission::CA_UPDATE, ca.clone(), { let actor = req.actor(); - render_empty_res(req.state().read().await.ca_parent_remove(ca, parent, &actor).await) + render_empty_res(req.state().ca_parent_remove(ca, parent, &actor).await) }) } @@ -1497,7 +1482,7 @@ async fn api_ca_remove_parent(req: Request, ca: Handle, parent: Handle) -> Routi async fn api_ca_kr_init(req: Request, ca: Handle) -> RoutingResult { aa!(req, Permission::CA_UPDATE, ca.clone(), { let actor = req.actor(); - render_empty_res(req.state().read().await.ca_keyroll_init(ca, &actor).await) + render_empty_res(req.state().ca_keyroll_init(ca, &actor).await) }) } @@ -1505,7 +1490,7 @@ async fn api_ca_kr_init(req: Request, ca: Handle) -> RoutingResult { async fn api_ca_kr_activate(req: Request, ca: Handle) -> RoutingResult { aa!(req, Permission::CA_UPDATE, ca.clone(), { let actor = req.actor(); - render_empty_res(req.state().read().await.ca_keyroll_activate(ca, &actor).await) + render_empty_res(req.state().ca_keyroll_activate(ca, &actor).await) }) } @@ -1517,7 +1502,7 @@ async fn api_ca_routes_update(req: Request, ca: Handle) -> RoutingResult { match req.json().await { Err(e) => render_error(e), - Ok(updates) => render_empty_res(state.read().await.ca_routes_update(ca, updates, &actor).await), + Ok(updates) => render_empty_res(state.ca_routes_update(ca, updates, &actor).await), } }) } @@ -1533,7 +1518,7 @@ async fn api_ca_routes_try_update(req: Request, ca: Handle) -> RoutingResult { match req.json::().await { Err(e) => render_error(e), Ok(updates) => { - let server = state.read().await; + let server = state; match server.ca_routes_bgp_dry_run(&ca, updates.clone()).await { Err(e) => { // update was rejected, return error @@ -1564,7 +1549,7 @@ async fn api_ca_routes_try_update(req: Request, ca: Handle) -> RoutingResult { /// show the route authorizations for this CA async fn api_ca_routes_show(req: Request, ca: Handle) -> RoutingResult { aa!(req, Permission::ROUTES_READ, ca.clone(), { - match req.state().read().await.ca_routes_show(&ca).await { + match req.state().ca_routes_show(&ca).await { Ok(roas) => render_json(roas), Err(_) => render_unknown_resource(), } @@ -1575,25 +1560,25 @@ async fn api_ca_routes_show(req: Request, ca: Handle) -> RoutingResult { async fn api_ca_routes_analysis(req: Request, path: &mut RequestPath, ca: Handle) -> RoutingResult { aa!(req, Permission::ROUTES_ANALYSIS, ca.clone(), { match path.next() { - Some("full") => render_json_res(req.state().read().await.ca_routes_bgp_analysis(&ca).await), + Some("full") => render_json_res(req.state().ca_routes_bgp_analysis(&ca).await), Some("dryrun") => match *req.method() { Method::POST => { let state = req.state.clone(); match req.json().await { Err(e) => render_error(e), - Ok(updates) => render_json_res(state.read().await.ca_routes_bgp_dry_run(&ca, updates).await), + Ok(updates) => render_json_res(state.ca_routes_bgp_dry_run(&ca, updates).await), } } _ => render_unknown_method(), }, Some("suggest") => match *req.method() { - Method::GET => render_json_res(req.state().read().await.ca_routes_bgp_suggest(&ca, None).await), + Method::GET => render_json_res(req.state().ca_routes_bgp_suggest(&ca, None).await), Method::POST => { let server = req.state().clone(); match req.json().await { Err(e) => render_error(e), Ok(resources) => { - render_json_res(server.read().await.ca_routes_bgp_suggest(&ca, Some(resources)).await) + render_json_res(server.ca_routes_bgp_suggest(&ca, Some(resources)).await) } } } @@ -1609,7 +1594,7 @@ async fn api_ca_routes_analysis(req: Request, path: &mut RequestPath, ca: Handle async fn api_republish_all(req: Request) -> RoutingResult { match *req.method() { Method::POST => aa!(req, Permission::CA_ADMIN, { - render_empty_res(req.state().read().await.republish_all().await) + render_empty_res(req.state().republish_all().await) }), _ => render_unknown_method(), } @@ -1619,7 +1604,7 @@ async fn api_resync_all(req: Request) -> RoutingResult { match *req.method() { Method::POST => aa!(req, Permission::CA_ADMIN, { let actor = req.actor(); - render_empty_res(req.state().read().await.resync_all(&actor).await) + render_empty_res(req.state().resync_all(&actor).await) }), _ => render_unknown_method(), } @@ -1630,7 +1615,7 @@ async fn api_refresh_all(req: Request) -> RoutingResult { match *req.method() { Method::POST => aa!(req, Permission::CA_ADMIN, { let actor = req.actor(); - render_empty_res(req.state().read().await.cas_refresh_all(&actor).await) + render_empty_res(req.state().cas_refresh_all(&actor).await) }), _ => render_unknown_method(), } @@ -1642,7 +1627,7 @@ async fn rrdp(req: Request) -> RoutingResult { if !req.path().full().starts_with("/rrdp/") { Err(req) // Not for us } else { - let mut full_path: PathBuf = req.state.read().await.rrdp_base_path(); + let mut full_path: PathBuf = req.state.rrdp_base_path(); let (_, path) = req.path.remaining().split_at(1); let cache_seconds = if path.ends_with("notification.xml") { 60 } else { 86400 }; full_path.push(path); @@ -1694,7 +1679,7 @@ async fn api_ca_rta_list(req: Request, ca: Handle) -> RoutingResult { req, Permission::RTA_LIST, ca.clone(), - render_json_res(req.state().read().await.rta_list(ca).await) + render_json_res(req.state().rta_list(ca).await) ) } @@ -1703,7 +1688,7 @@ async fn api_ca_rta_show(req: Request, ca: Handle, name: RtaName) -> RoutingResu req, Permission::RTA_READ, ca.clone(), - render_json_res(req.state().read().await.rta_show(ca, name).await) + render_json_res(req.state().rta_show(ca, name).await) ) } @@ -1713,7 +1698,7 @@ async fn api_ca_rta_sign(req: Request, ca: Handle, name: RtaName) -> RoutingResu let state = req.state().clone(); match req.json().await { Err(e) => render_error(e), - Ok(request) => render_empty_res(state.read().await.rta_sign(ca, name, request, &actor).await), + Ok(request) => render_empty_res(state.rta_sign(ca, name, request, &actor).await), } }) } @@ -1724,7 +1709,7 @@ async fn api_ca_rta_multi_prep(req: Request, ca: Handle, name: RtaName) -> Routi let state = req.state().clone(); match req.json().await { - Ok(resources) => render_json_res(state.read().await.rta_multi_prep(ca, name, resources, &actor).await), + Ok(resources) => render_json_res(state.rta_multi_prep(ca, name, resources, &actor).await), Err(e) => render_error(e), } }) @@ -1735,7 +1720,7 @@ async fn api_ca_rta_multi_sign(req: Request, ca: Handle, name: RtaName) -> Routi let actor = req.actor(); let state = req.state().clone(); match req.json().await { - Ok(rta) => render_empty_res(state.read().await.rta_multi_cosign(ca, name, rta, &actor).await), + Ok(rta) => render_empty_res(state.rta_multi_cosign(ca, name, rta, &actor).await), Err(_) => render_error(Error::custom("Cannot decode RTA for co-signing")), } }) diff --git a/src/daemon/http/testbed.rs b/src/daemon/http/testbed.rs index ad5b85bb..07ead962 100644 --- a/src/daemon/http/testbed.rs +++ b/src/daemon/http/testbed.rs @@ -35,7 +35,7 @@ use crate::{commons::api::Handle, constants::ACTOR_DEF_TESTBED}; pub async fn testbed(mut req: Request) -> RoutingResult { if !req.path().full().starts_with("/testbed") { Err(req) // Not for us - } else if !req.state().read().await.testbed_enabled() { + } else if !req.state().testbed_enabled() { render_unknown_method() } else { // The testbed is intended to be used without being logged in but diff --git a/src/daemon/krillserver.rs b/src/daemon/krillserver.rs index 4aa8645c..98953596 100644 --- a/src/daemon/krillserver.rs +++ b/src/daemon/krillserver.rs @@ -334,7 +334,7 @@ impl KrillServer { } /// Removes a publisher, blows up if it didn't exist. - pub fn remove_publisher(&mut self, publisher: PublisherHandle, actor: &Actor) -> KrillEmptyResult { + pub fn remove_publisher(&self, publisher: PublisherHandle, actor: &Actor) -> KrillEmptyResult { self.repo_manager.remove_publisher(publisher, actor) } @@ -606,7 +606,7 @@ impl KrillServer { self.ca_manager.get_ca(handle).await.map(|ca| ca.publisher_request()) } - pub async fn ca_init(&mut self, init: CertAuthInit) -> KrillEmptyResult { + pub fn ca_init(&self, init: CertAuthInit) -> KrillEmptyResult { let handle = init.unpack(); self.ca_manager.init_ca(&handle) } diff --git a/src/pubd/repository.rs b/src/pubd/repository.rs index 1f9d8c77..52775c84 100644 --- a/src/pubd/repository.rs +++ b/src/pubd/repository.rs @@ -49,6 +49,7 @@ use super::RepositoryAccessInitDetails; /// so that callers don't need to worry about storage details. #[derive(Debug)] pub struct RepositoryContentProxy { + cache: RwLock>, store: RwLock, key: KeyStoreKey, } @@ -59,32 +60,59 @@ impl RepositoryContentProxy { let store = KeyValueStore::disk(work_dir, PUBSERVER_CONTENT_DIR)?; let store = RwLock::new(store); let key = KeyStoreKey::simple(format!("{}.json", PUBSERVER_DFLT)); + let cache = RwLock::new(None); - Ok(RepositoryContentProxy { store, key }) + let proxy = RepositoryContentProxy { cache, store, key }; + proxy.warm_cache()?; + + Ok(proxy) + } + + fn warm_cache(&self) -> KrillResult<()> { + + let key_store_read = self.store + .read() + .unwrap(); + + if key_store_read.has(&self.key)? { + info!("Warming the repository content cache, this can take a minute for large repositories."); + let content = key_store_read.get(&self.key)?.unwrap(); + self.cache.write().unwrap().replace(content); + } + + Ok(()) } - // Initialize + /// Initialize pub fn init(&self, work_dir: &Path, uris: PublicationServerUris) -> KrillResult<()> { if self.store.read().unwrap().has(&self.key)? { Err(Error::RepositoryServerAlreadyInitialized) } else { - let (rrdp_base_uri, rsync_jail) = uris.unpack(); + // initialize new repo content + let repository_content = { + let (rrdp_base_uri, rsync_jail) = uris.unpack(); - let publishers = HashMap::new(); + let publishers = HashMap::new(); - let session = RrdpSession::default(); - let stats = RepoStats::new(session); + let session = RrdpSession::default(); + let stats = RepoStats::new(session); - let mut repo_dir = work_dir.to_path_buf(); - repo_dir.push(REPOSITORY_DIR); + let mut repo_dir = work_dir.to_path_buf(); + repo_dir.push(REPOSITORY_DIR); - let rrdp = RrdpServer::create(rrdp_base_uri, &repo_dir, session); - let rsync = RsyncdStore::new(rsync_jail, &repo_dir); + let rrdp = RrdpServer::create(rrdp_base_uri, &repo_dir, session); + let rsync = RsyncdStore::new(rsync_jail, &repo_dir); - let repo = RepositoryContent::new(publishers, rrdp, rsync, stats); + RepositoryContent::new(publishers, rrdp, rsync, stats) + }; + // Store newly initialized repo content on disk let store = self.store.write().unwrap(); - store.store(&self.key, &repo)?; + store.store(&self.key, &repository_content)?; + + // Store newly initialized repo content in cache + let mut cache = self.cache.write().unwrap(); + cache.replace(repository_content); Ok(()) } @@ -100,25 +128,29 @@ impl RepositoryContentProxy { store.drop_key(&self.key)?; } + let mut cache = self.cache.write().unwrap(); + cache.take(); + Ok(()) } + /// Return the repository content stats pub fn stats(&self) -> KrillResult { - self.read_content().map(|c| c.stats().clone()) + self.read(|content| Ok(content.stats().clone())) } - // Adds a publisher with an empty set of published objects. - // Replaces an existing publisher if it existed. - // This is only supposed to be called if adding the publisher - // to the RepositoryAccess was successful (and *that* will fail if - // the publisher is a duplicate). This method can only fail if - // there is an issue with the underlying key value store. + /// Add a publisher with an empty set of published objects. + /// + /// Replaces an existing publisher if it existed. + /// This is only supposed to be called if adding the publisher + /// to the RepositoryAccess was successful (and *that* will fail if + /// the publisher is a duplicate). This method can only fail if + /// there is an issue with the underlying key value store. pub fn add_publisher(&self, name: PublisherHandle) -> KrillResult<()> { self.write(|content| content.add_publisher(name)) } - // Removes a publisher and its content. Will also write the updated - // RRDP and rsync content. + /// Removes a publisher and its content. pub fn remove_publisher( &self, name: &PublisherHandle, @@ -128,9 +160,10 @@ impl RepositoryContentProxy { self.write(|content| content.remove_publisher(name, jail, config)) } - // Publish an update for a publisher. Assumes that the RFC 8181 CMS has - // been verified, but will check that all objects are within the publisher's - // uri space (jail). + /// Publish an update for a publisher. + /// + /// Assumes that the RFC 8181 CMS has been verified, but will check that all objects + /// are within the publisher's uri space (jail). pub fn publish( &self, name: &PublisherHandle, @@ -141,40 +174,52 @@ impl RepositoryContentProxy { self.write(|content| content.publish(name, delta.into(), jail, config)) } - // Write all current files to disk + /// Write all current files to disk pub fn write_repository(&self, config: &RepositoryRetentionConfig) -> KrillResult<()> { - self.read_content()?.write_repository(config) + self.read(|content| content.write_repository(config)) } - // Reset the RRDP session + /// Reset the RRDP session pub fn session_reset(&self, config: &RepositoryRetentionConfig) -> KrillResult<()> { self.write(|content| content.session_reset(config)) } + /// Create a list reply containing all current objects for a publisher + pub fn list_reply(&self, name: &PublisherHandle) -> KrillResult { + self.read(|content| content.list_reply(name)) + } + + // Get all current objects for a publisher + pub fn current_objects(&self, name: &PublisherHandle) -> KrillResult { + self.read(|content| content.objects_for_publisher(name).map(|o| o.clone())) + } + + // Execute a closure on a mutable repository content in a single write 'transaction' fn write KrillResult<()>>(&self, op: F) -> KrillResult<()> { + // If there is any existing content, then we can assume that the cache + // has it - because it's initialized when we read the content during + // initialization. let store = self.store.write().unwrap(); - let mut content: RepositoryContent = store.get(&self.key)?.ok_or(Error::RepositoryServerNotInitialized)?; + let mut cache = self.cache.write().unwrap(); - op(&mut content)?; + let content: &mut RepositoryContent = cache.as_mut().ok_or(Error::RepositoryServerNotInitialized)?; - store.store(&self.key, &content)?; + op(content)?; + + store.store(&self.key, content)?; Ok(()) } - fn read_content(&self) -> KrillResult { - self.store - .read() - .unwrap() - .get(&self.key)? - .ok_or(Error::RepositoryServerNotInitialized) - } - - pub fn list_reply(&self, name: &PublisherHandle) -> KrillResult { - self.read_content()?.list_reply(name) - } - - pub fn current_objects(&self, name: &PublisherHandle) -> KrillResult { - self.read_content()?.objects_for_publisher(name).map(|o| o.clone()) + // Execute a closure on a mutable repository content in a single read 'transaction' + // + // This function fails if the repository content is not initialized. + fn read KrillResult>(&self, op: F) -> KrillResult { + // Note that because the content is initialized it is implied that the cache MUST always be + // set. I.e. it is set on initialization and updated whenever the repository content is updated. + // So, we can safely read from the cache only. + let cache = self.cache.read().unwrap(); + let content = cache.as_ref().ok_or(Error::RepositoryServerNotInitialized)?; + op(content) } } diff --git a/src/upgrades/mod.rs b/src/upgrades/mod.rs index 0963deff..e9c6190d 100644 --- a/src/upgrades/mod.rs +++ b/src/upgrades/mod.rs @@ -7,15 +7,13 @@ use std::{fmt, path::Path, str::FromStr, sync::Arc}; use serde::de::DeserializeOwned; use crate::commons::error::KrillIoError; -use crate::constants::KRILL_VERSION; -use crate::daemon::ca::CertAuth; -use crate::pubd::RepositoryAccess; +use crate::commons::util::file; use crate::{commons::api::Handle, daemon::config::Config}; use crate::{ commons::{ crypto::KrillSigner, eventsourcing::{ - AggregateStore, AggregateStoreError, CommandKey, KeyStoreKey, KeyValueError, KeyValueStore, + AggregateStoreError, CommandKey, KeyStoreKey, KeyValueError, KeyValueStore, }, util::KrillVersion, }, @@ -171,45 +169,47 @@ pub fn pre_start_upgrade(config: Arc) -> Result<(), UpgradeError> { pub async fn update_storage_version(work_dir: &Path) -> Result<(), UpgradeError> { let current = KrillVersion::current(); - let mut ca_dir = work_dir.to_path_buf(); - ca_dir.push("cas"); - if ca_dir.exists() { - let ca_store: AggregateStore = AggregateStore::disk(work_dir, "cas")?; - if ca_store.get_version()? != current { - ca_store.set_version(¤t)?; - } + if needs_v0_9_0_upgrade(work_dir, "cas") { + debug!("Updating version file for cas"); + file::save_json(¤t, &work_dir.join("cas/version"))?; + } + + if needs_v0_9_0_upgrade(work_dir, "pubd") { + debug!("Updating version file for pubd"); + file::save_json(¤t, &work_dir.join("pubd/version"))?; } - let mut pubd_dir = work_dir.to_path_buf(); - pubd_dir.push("pubd"); - if pubd_dir.exists() { - let pubd_store: AggregateStore = AggregateStore::disk(work_dir, "pubd")?; - if pubd_store.get_version()? != current { - pubd_store.set_version(¤t)?; - } - } - - info!("Upgraded Krill to version: {}", KRILL_VERSION); Ok(()) } fn upgrade_0_9_0(config: Arc) -> Result<(), UpgradeError> { - let mut pubd_dir = config.data_dir.clone(); - pubd_dir.push("pubd"); - if pubd_dir.exists() { + let work_dir = &config.data_dir; + if needs_v0_9_0_upgrade(work_dir, "pubd") { PubdObjectsMigration::migrate(config.clone())?; } - let signer = Arc::new(KrillSigner::build(&config.data_dir)?); - let repo_manager = RepositoryManager::build(config.clone(), signer)?; - let mut cas_dir = config.data_dir.clone(); - cas_dir.push("cas"); - if cas_dir.exists() { + if needs_v0_9_0_upgrade(work_dir, "cas") { + let signer = Arc::new(KrillSigner::build(work_dir)?); + let repo_manager = RepositoryManager::build(config.clone(), signer)?; + CaObjectsMigration::migrate(config, repo_manager)?; } + Ok(()) } +fn needs_v0_9_0_upgrade(work_dir: &Path, ns: &str) -> bool { + let keystore_path = work_dir.join(ns); + if keystore_path.exists() { + let version_path = keystore_path.join("version"); + let version_found = file::load_json(&version_path).unwrap_or_else(|_| KrillVersion::v0_5_0_or_before()); + version_found < KrillVersion::release(0, 9, 0) + } else { + false + } + +} + //------------ Tests --------------------------------------------------------- #[cfg(test)] diff --git a/test-resources/krill-init-multi-user.conf b/test-resources/krill-init-multi-user.conf index c291d622..e40a5a92 100644 --- a/test-resources/krill-init-multi-user.conf +++ b/test-resources/krill-init-multi-user.conf @@ -115,8 +115,8 @@ service_uri = "https://localhost:3001/" # CA certificate refresh rate # -# This defines the rate, in seconds, for Krill CAs to to contact their parent -# CA and query for updates in resource entitlements. +# This defines the rate, in seconds, for Krill CAs to to contact their parent CA +# via the RFC 6492 up-down protocol and query for updates in resource entitlements. # # Defaults to 10 minutes # diff --git a/test-resources/krill-init.conf b/test-resources/krill-init.conf index 56881c6a..735a2722 100644 --- a/test-resources/krill-init.conf +++ b/test-resources/krill-init.conf @@ -115,8 +115,8 @@ service_uri = "https://localhost:3001/" # CA certificate refresh rate # -# This defines the rate, in seconds, for Krill CAs to to contact their parent -# CA and query for updates in resource entitlements. +# This defines the rate, in seconds, for Krill CAs to to contact their parent CA +# via the RFC 6492 up-down protocol and query for updates in resource entitlements. # # Defaults to 10 minutes #