From 6fb64998cefda4550ac1d26e2fb4827a63ef3d67 Mon Sep 17 00:00:00 2001 From: Lars Eggert <lars@eggert.org> Date: Wed, 13 Oct 2021 11:33:37 +0000 Subject: [PATCH 01/15] Version of the new docker setup emailed out for testing on 2021-10-12 - Legacy-Id: 19417 --- docker/Dockerfile | 226 ++++++++++++----------------------------- docker/README.md | 37 +++++++ docker/README.rst | 98 ------------------ docker/build | 39 +++----- docker/devserver | 15 --- docker/docker-init.sh | 228 +++++++++++------------------------------- docker/install-extras | 14 --- docker/mailserver | 13 --- docker/run | 205 ++++--------------------------------- docker/setprompt | 26 ----- docker/setupdb | 114 --------------------- docker/updatedb | 72 ++++--------- 12 files changed, 211 insertions(+), 876 deletions(-) create mode 100644 docker/README.md delete mode 100644 docker/README.rst delete mode 100755 docker/devserver delete mode 100755 docker/install-extras delete mode 100755 docker/mailserver delete mode 100644 docker/setprompt delete mode 100755 docker/setupdb diff --git a/docker/Dockerfile b/docker/Dockerfile index f14cb9196..dc7710f4e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,185 +1,85 @@ -# -*- shell-mode -*- -# This file is a docker (https://www.docker.com/what-docker) recipe, which can be used to build -# a docker image which is ready to run a datatracker in development mode. +# This is a Dockerfile with everything in it to run the IETF datatracker. # -# It is used to build an image (once you've installed docker) using a command like this (assuming -# suitable replacement of $variables: +# If you make changes to the datatracker that add new dependencies (python +# packages or otherwise), you need to rebuild this image to make them +# available. Do this in the top-level directory of your datatracker source +# tree: # -# $ docker build -t $yourdockerid/datatracker:$version +# docker build --tag dt:latest . # -# To use a pre-built image, assuming we're on OS X and have a checked-out datatracker repository -# at /Users/$login/src/6.8.1.dev0, you would start (again assuming you've installed docker) -# a container from an image, as follows: -# -# $ docker run -ti --name=$containername -v /Users/$login:/home/$login levkowetz/datatracker:$version /bin/bash -# -# This maps your home directory to /home/$login in the container, and starts it running /bin/bash. -# -# In this first version, the docker environment is set up so that tests will run successfully, -# but the database has *not* been loaded with a dump, and supporting files (drafts, charters, etc.) -# have *not* been downloaded. +# You can then execute the datatracker like this (also from the top-level +# datatracker source directory): +# +# docker run -ti -v $(pwd):/root/src dt:latest -FROM dyne/devuan:beowulf -LABEL maintainer="Henrik Levkowetz <henrik@levkowetz.com>" +FROM ubuntu:hirsute +LABEL maintainer="IETF Tools Team <tools-discuss@ietf.org>" # Default django runserver port -EXPOSE 8000 +EXPOSE 8000 -# Run apt-get noninteractive +# Default mysqld/mariadb port +EXPOSE 3306 + +# Install all dependencies that are available as packages ENV DEBIAN_FRONTEND=noninteractive -ENV DEVUAN_FRONTEND=noninteractive - -# Uncomment this to be able to install and run apt-show-versions: -RUN rm -v /etc/apt/apt.conf.d/docker-compress -RUN rm -v /var/lib/apt/lists/*lz4 - -RUN apt-get update --allow-releaseinfo-change -RUN apt-get install -qy apt-transport-https - -# Use backports, updates, and security updates -RUN echo "deb http://deb.devuan.org/merged beowulf main contrib non-free" > /etc/apt/sources.list -RUN echo "deb http://deb.devuan.org/merged beowulf-security main contrib non-free" >> /etc/apt/sources.list -RUN echo "deb http://deb.devuan.org/merged beowulf-updates main contrib non-free" >> /etc/apt/sources.list -RUN echo "deb http://deb.devuan.org/merged beowulf-backports main contrib non-free" >> /etc/apt/sources.list - -# Remove some excludes for the docker image -RUN sed -i -e '/^path-exclude=.*\/groff/d' \ - -e '/^path-exclude=.*\/locale/d' \ - -e '/^path-exclude=.*\/man/d' /etc/dpkg/dpkg.cfg.d/docker-excludes - -# Install needed packages -# -# We're not including graphviz and ghostscript, needed for the 3 document -# dependency graph tests; they would increase the size of the image by about -# 15%, about 100MB. - -# Fetch apt package information, and upgrade to latest package versions - -RUN apt-get update -RUN apt-get -qy upgrade - -# Install the packages we need -RUN apt-get install -qy \ - build-essential \ - bzip2 \ - ca-certificates \ - colordiff \ - gawk \ - gcc \ - ipython \ - jq \ - less \ - libbz2-dev \ - libdb5.3-dev \ - libexpat1-dev \ - libffi-dev \ - libgdbm-dev \ - libjpeg62-turbo-dev \ - liblzma-dev \ - libmagic1 \ - libmariadbclient-dev \ - libncurses5-dev \ - libncursesw5-dev \ - libreadline-dev \ - libsqlite3-dev \ - libssl-dev \ - libsvn1 \ - libxml2-dev \ - libxslt-dev \ - libz-dev \ - libffi-dev \ - locales \ - make \ - man \ - mariadb-client \ - mariadb-server \ - openssh-client \ - patch \ - procps \ - pv \ - rsync \ +RUN apt-get -y update && \ + apt-get -y install --no-install-recommends \ + apache2-utils \ + apt-file \ + apt-utils \ + chromium-driver \ + curl \ + enscript \ + gcc \ + ghostscript \ + git \ + graphviz \ + libmagic-dev \ + libmariadb-dev \ + mariadb-server \ + npm \ + pigz \ + pv \ + python-is-python3 \ + python3-dev \ + python3-pip \ rsyslog \ - subversion \ - sudo \ - uuid-dev \ - vim \ - wget \ - xz-utils\ - zile \ - zlib1g-dev + unzip \ + yang-tools && \ + apt-get -y clean && \ + rm -rf /var/lib/apt/lists/* -# Postgresql packages -RUN apt-get install -qy \ - postgresql-11 \ - postgresql-server-dev-11 +# Install bower +RUN npm install -g bower -# Get the key used to sign the libyang repo -RUN wget -nv http://download.opensuse.org/repositories/home:liberouter/Debian_9.0/Release.key -RUN apt-key add - < Release.key -RUN rm Release.key - -# Add apt source entry for libyang -RUN echo "deb http://download.opensuse.org/repositories/home:/liberouter/Debian_9.0/ /" >> /etc/apt/sources.list.d/libyang.list - -# Update the package defs, and install the desired mysql from the mysql repo -RUN apt-get update -RUN apt-get install -qy libyang1 - -# This is expected to exist by the mysql startup scripts: -#RUN touch /etc/mysql/debian.cnf -# ------------------------------------------------------------------------------ - -# Get rid of installation files we don't need in the image, to reduce size -RUN apt-get clean && rm -rf /var/lib/apt/lists/* - -# Enable some common locales -RUN sed -i -e 's/^. en_US/en_US/' -e 's/^. en_GB/en_GB/' -e 's/^. en_IE/en_IE/' /etc/locale.gen -RUN locale-gen - -# Remove an rsyslog module that we don't need, which also requires extra permissions -RUN sed -i -e '/load="imklog"/d' /etc/rsyslog.conf - -# Set up root password -RUN echo "root:root" | chpasswd - -# MySQL -VOLUME /var/lib/mysql - -# idnits and dependencies +# Install idnits ADD https://tools.ietf.org/tools/idnits/idnits /usr/local/bin/ RUN chmod +rx /usr/local/bin/idnits -# Directory for Mac certs -RUN mkdir /etc/certificates +# Install current datatracker python dependencies +COPY requirements.txt / +RUN pip install -r /requirements.txt -# # Python 3 -# # Comment in if OS does not provide python3.6, which is the current -# # production version -ENV PYVER=3.6.10 -ENV PYREV=3.6 +# Turn off rsyslog kernel logging (doesn't work in Docker) +RUN sed -i '/imklog/s/^/#/' /etc/rsyslog.conf -WORKDIR /usr/src -RUN wget -q https://www.python.org/ftp/python/$PYVER/Python-$PYVER.tar.xz -RUN tar xJf Python-$PYVER.tar.xz -RUN rm Python-$PYVER.tar.xz -WORKDIR /usr/src/Python-$PYVER/ -RUN ./configure -RUN make -RUN make altinstall -WORKDIR /usr/src -RUN rm -rf /usr/src/Python-$PYVER/ +# Allow access to mariadb over the network +RUN sed -i 's/127.0.0.1/0.0.0.0/' /etc/mysql/mariadb.conf.d/50-server.cnf -ENV HOSTNAME="datatracker" +# Turn on mariadb performance_schema +RUN sed -i 's/\[mysqld\]/\[mysqld\]\nperformance_schema=ON/' /etc/mysql/mariadb.conf.d/50-server.cnf -ENV DDIR="/usr/local/share/datatracker" -RUN mkdir -p $DDIR -WORKDIR $DDIR - -COPY requirements.txt ./ -COPY setprompt ./ +# Make the mariadb sys schema available for possible installation +# We would normally use the next line, but that has a bug: +# ADD https://github.com/FromDual/mariadb-sys/archive/master.zip / +# This is the repo that has the PR: +ADD https://github.com/grooverdan/mariadb-sys/archive/refs/heads/master.zip / +RUN unzip /master.zip +# Copy the startup file COPY docker-init.sh /docker-init.sh RUN chmod +x /docker-init.sh -ENTRYPOINT ["/docker-init.sh"] -CMD /bin/bash +WORKDIR /root/src +ENTRYPOINT ["/docker-init.sh"] \ No newline at end of file diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 000000000..e30940efa --- /dev/null +++ b/docker/README.md @@ -0,0 +1,37 @@ +# Datatracker Development in Docker + +1. [Set up Docker](https://docs.docker.com/get-started/) on your preferred + platform. + +2. If you have a copy of the datatracker code checked out already, simply `cd` + to the top-level directory. + + If not, check out a datatracker branch as usual. We'll check out `trunk` + below, but you can use any branch: + + svn co https://svn.ietf.org/svn/tools/ietfdb/trunk + cd trunk + +3. **TEMPORARY:** Replace the contents of the `docker` directory with Lars' + files. + +4. **TEMPORARY:** Until Lars' changes have been merged and a docker image is + available for download, you will need to build it locally: + + docker/build -l + + This will take a while, but only needs to be done once. + +5. Use the `docker/run` script to start the datatracker container. You will be + dropped into a shell from which you can start the datatracker and execute + related commands as usual, for example: + + ietf/manage.py runserver 0.0.0.0:8000 + + If you do not already have a copy of the IETF database available in the + `data` directory, one will be downloaded and imported the first time you run + `docker/run`. This will take some time. + + Once the datatracker has started, you should be able to open + [http://localhost:8000](http://localhost:8000) in a browser and see the + landing page. \ No newline at end of file diff --git a/docker/README.rst b/docker/README.rst deleted file mode 100644 index d79777e36..000000000 --- a/docker/README.rst +++ /dev/null @@ -1,98 +0,0 @@ - -============================================================================== - Datatracker Development in a Docker Container (beta) -============================================================================== - - -Intro -===== - -Docker_ is a toolkit which lets you package software together with its -dependencies in lightweight containers, and run it in isolated virtual -environments. - -During and just after IETF-94 I've spent quite a bit of time setting up a -docker image which provides the dependencies needed to run the datatracker, -and it's now available for beta testing. Hopefully this should make it -substantially easier to get started with datatracker development. - -Steps -===== - -1. Set up Docker on your preferred platform. Official installers exist for - many Linux flavours, OS X, Windows and Cloud services. Here's the full `List - of Installation Instructions`_. - - Docker containers require the services of an underlying Linux API, which - means that on OS X and Windows, these have to be provided by a virtual - machine which runs a minimal Linux image. The virtual machine used on - non-Linux platforms is commonly VirtualBox. On Linux kernels with version - 3.8 or later, no virtual machine is needed, as the docker images can be - fully supported with the native kernel services. - - Please follow the Docker installations all the way through to successfully - running the ``hello-world`` example in a terminal window ( ``$ docker run - hello-world``). - - -2. Check out your datatracker branch as usual, in a suitable directory. - We'll assume ``~/src/dt/`` here, and assume you are ``'coder'``:: - - ~/src/dt/ $ svn co https://svn.tools.ietf.org/svn/tools/ietfdb/personal/coder/6.8.2.dev0 - -3. In the checked-out working copy, you'll find a ``docker/`` directory and a - ``data/`` directory at the top level. We're first going to set up a copy of - the MySQL database files under the ``data/`` directory. - - There is a command in the ``docker/`` directory, ``setupdb`` which will do - this for you, or you can do it manually. - - Either run:: - - ~/src/dt/6.8.2.dev0/ $ docker/setupdb - - or do this step-by-step: fetch down a pre-built copy of the datatracker - database, place it in the ``data`` directory, unpack it, and fix - permissions:: - - ~/src/dt/6.8.2.dev0/ $ cd data - ~/src/dt/6.8.2.dev0/data/ $ wget https://www.ietf.org/lib/dt/sprint/ietf_utf8.bin.tar.bz2 - ~/src/dt/6.8.2.dev0/data/ $ tar xjf ietf_utf8.bin.tar.bz2 - ~/src/dt/6.8.2.dev0/data/ $ chmod -R go+rwX mysql - - -4. In the ``docker/`` directory you'll also find a wrapper script named - ``'run'``. We will be using the wrapper to run a pre-built docker image - fetched from the docker hub:: - - ~/src/dt/6.8.2.dev0/ $ docker/run - - This will pull down the latest docker ietf/datatracker-environment image, - start it up with appropriate settings, map the internal ``/var/lib/mysql/`` - directory to the external ``data/mysql/`` directory where we placed the - database, set up a python virtualenv for you, install some dependencies, - and drop you in a bash shell where you can run the datatracker. - -6. You are now ready to run the tests:: - - (virtual) $ ietf/manage.py test --settings=settings_sqlitetest - - and then start the dev server:: - - (virtual) $ ietf/manage.py runserver 0.0.0.0:8000 - - Note the IP address ``0.0.0.0`` used to make the dev server bind to all - addresses. The internal port 8000 has been mapped to port 8000 externally, - too. In order to find the IP address of the VirtualBox, run ``'$ - docker-machine ip'`` *outside* the virtual environment:: - - ~/src/dt/6.8.2.dev0/ $ docker-machine ip - 192.168.59.103 - - ~/src/dt/6.8.2.dev0/ $ open http://192.168.59.103:8000/ - -.. _Docker: https://www.docker.com/ -.. _`List of Installation Instructions`: https://docs.docker.com/v1.8/installation/ -.. _VirtualBox: https://www.virtualbox.org/ - - diff --git a/docker/build b/docker/build index eb3d66f20..2a9c4df63 100755 --- a/docker/build +++ b/docker/build @@ -1,11 +1,11 @@ #!/bin/bash -version=0.10 +version=0.11 program=${0##*/} progdir=${0%/*} if [ "$progdir" = "$program" ]; then progdir="."; fi if [ "$progdir" = "." ]; then progdir="$PWD"; fi -parent=$(dirname $progdir) +parent=$(dirname "$progdir") if [ "$parent" = "." ]; then parent="$PWD"; fi export LANG=C @@ -31,10 +31,10 @@ DESCRIPTION EOF echo -e "OPTIONS" - if [ "$(uname)" = "Linux" ]; then - egrep "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" $0 | tr -s "\t|" "\t," | sed -r -e 's/\)[ \t]+([A-Z]+)=\$2[^#]*#/=\1\t/' -e 's/\)[^#]*#/\t/' + if [ "$(uname)" = "Linux" ] || [ "$(uname)" = "Darwin" ]; then + grep -E "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" "$0" | tr -s "\t|" "\t," | sed -r -e 's/\)[ \t]+([A-Z]+)=\$2[^#]*#/=\1\t/' -e 's/\)[^#]*#/\t/' else - egrep "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" $0 | sed 's/\|.*\$2[^#]*#/ /'| sed -E 's/\|.*\)[^#]*#/ /' + grep -E "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" "$0" | sed 's/\|.*\$2[^#]*#/ /'| sed -E 's/\|.*\)[^#]*#/ /' fi cat <<EOF @@ -48,7 +48,7 @@ COPYRIGHT Copyright (c) 2016 IETF Trust and the persons identified as authors of the code. All rights reserved. License 'Simplified BSD', as specified in http://opensource.org/licenses/BSD-3-Clause. - + EOF } @@ -59,10 +59,6 @@ function die() { exit 1 } -function note() { - if [ -n "$VERBOSE" ]; then echo -e "$*"; fi -} - # ---------------------------------------------------------------------- function version() { echo -e "$program $version" @@ -77,24 +73,22 @@ trap 'echo "$program($LINENO): Command failed with error code $? ([$$] $0 $*)"; # Options shortopts=hlt:vV -longopts=help,local,tag=,verbose,version +longopts=help,local,tag=,version # Default values -BRANCH=$(svn log -v ^/tags -l 2 | grep 'A /tags/[1-9]' | awk '{print $2}') -TAG=${BRANCH##*/} +IMAGE=ietf/datatracker-environment +TAG=$(basename "$(svn info "$parent" | grep ^URL | awk '{print $2}')") LOCAL="" if [ "$(uname)" = "Linux" ]; then args=$(getopt -o "$shortopts" --long "$longopts" -n '$program' -- $SV "$@") if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi eval set -- "$args" - sed="sed -r" else # Darwin, BSDs args=$(getopt -o$shortopts $SV $*) if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi set -- $args - sed="sed -E" fi while true ; do @@ -102,7 +96,6 @@ while true ; do -h| --help) usage; exit;; # Show this help, then exit -l| --local) LOCAL=1;; # Don't upload -t| --tag) TAG=$2; shift;; # Use this docker image tag, instead of the latest svn tags name - -v| --verbose) VERBOSE=1;; # Be more talkative -V| --version) version; exit;; # Show program version, then exit --) shift; break;; *) die "Internal error, inconsistent option specification: '$1'";; @@ -113,14 +106,10 @@ done # ---------------------------------------------------------------------- # The program itself -if [ "$(uname)" == "Linux" ]; then - die "Didn't expect to run this script on Linux -- are you inside docker?" -fi - -docker rmi -f ietf/datatracker-environment:trunk || true -docker build -t ietf/datatracker-environment:$TAG docker/ -docker tag $(docker images -q ietf/datatracker-environment | head -n 1) ietf/datatracker-environment:latest +docker rmi -f $IMAGE:trunk 2>/dev/null || true +docker build --progress plain -t "$IMAGE:$TAG" docker/ +docker tag "$(docker images -q $IMAGE | head -n 1)" $IMAGE:latest if [ -z "$LOCAL" ]; then - docker push ietf/datatracker-environment:latest - docker push ietf/datatracker-environment:$TAG + docker push $IMAGE:latest + docker push "$IMAGE:$TAG" fi \ No newline at end of file diff --git a/docker/devserver b/docker/devserver deleted file mode 100755 index add4534fa..000000000 --- a/docker/devserver +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -version=0.10 -program=${0##*/} -progdir=${0%/*} -if [ "$progdir" = "$program" ]; then progdir="."; fi -if [ "$progdir" = "." ]; then progdir="$PWD"; fi -parent=$(dirname $progdir) -if [ "$parent" = "." ]; then parent="$PWD"; fi - -# ---------------------------------------------------------------------- - -$progdir/run ietf/manage.py runserver 0.0.0.0:8000 - - diff --git a/docker/docker-init.sh b/docker/docker-init.sh index de1c0a651..fdc0003a6 100644 --- a/docker/docker-init.sh +++ b/docker/docker-init.sh @@ -1,195 +1,85 @@ #!/bin/bash -# A little bit of setup -export LANG=en_GB.UTF-8 +MYSQLDIR=/var/lib/mysql -echo "Gathering info ..." -MYSQLDIR="$(mysqld --verbose --help 2>/dev/null | awk '$1 == "datadir" { print $2; exit }')" -if [ ! "$USER" ]; then - echo "Environment variable USER is not set -- will set USER='django'." - USER="django" -fi -if [ ! "$UID" ]; then - echo "Environment variable UID is not set -- will set UID='1000'." - UID="1000" -fi -if [ ! "$GID" ]; then - echo "Environment variable GID is not set -- will set GID='1000'." - GID="1000" -fi -if [ ! "$TAG" ]; then - echo "Environment variable TAG is not set -- will set TAG='datatracker'." - TAG="datatracker" -fi -echo "User $USER ($UID:$GID)" - -echo "Checking if MySQL base data exists ..." -if [ ! -d $MYSQLDIR/mysql ]; then - echo "WARNING: Expected the directory $MYSQLDIR/mysql/ to exist -- have you downloaded and unpacked the IETF binary database tarball?" +if [ ! -d "$MYSQLDIR" ]; then + echo "WARNING: Expected the directory $MYSQLDIR to exist." + exit 1 fi -echo "Setting up the 'mysql' user for database file access ..." -MYSQL_TARGET_GID=$(stat -c "%g" $MYSQLDIR/mysql) -if ! grep -q ":$MYSQL_TARGET_GID:$" /etc/group; then - groupadd -g $MYSQL_TARGET_GID mysqldata -fi -usermod -a -G $MYSQL_TARGET_GID mysql +service rsyslog start -echo "Checking if MySQL is running ..." -if ! /etc/init.d/mysql status; then - echo "Starting mysql ..." - /etc/init.d/mysql start +if [ -z "$(ls -A $MYSQLDIR/mysql 2>/dev/null)" ]; then + echo "WARNING: Database seems to be empty." + mysql_install_db > /dev/null || exit 1 fi -echo "Checking if syslogd is running ..." -if ! /etc/init.d/rsyslog status > /dev/null; then - echo "Starting syslogd ..." - /etc/init.d/rsyslog start +service mariadb start + +if ! service mariadb status; then + echo "ERROR: MySQL isn't running." + grep mysqld /var/log/syslog + exit 1 fi -# Give debian-sys-maint access, to avoid complaints later -mysql mysql <<< "GRANT ALL PRIVILEGES on *.* TO 'debian-sys-maint'@'localhost' IDENTIFIED BY '$(awk '/^password/ {print $3; exit}' /etc/mysql/debian.cnf )' WITH GRANT OPTION; FLUSH PRIVILEGES;" - -echo "Checking if the IETF database exists at $MYSQLDIR ..." if [ ! -d $MYSQLDIR/ietf_utf8 ]; then - if [ -z "$DATADIR" ]; then - echo "DATADIR is not set, but the IETF database needs to be set up -- can't continue, exiting the docker init script." - exit 1 - fi - ls -l $MYSQLDIR - - if ! /etc/init.d/mysql status; then - echo "Didn't find the IETF database, but can't set it up either, as MySQL isn't running." - else - echo "Creating database ..." - mysqladmin -u root --default-character-set=utf8 create ietf_utf8 - - echo "Setting up permissions ..." - mysql -u root ietf_utf8 <<< "GRANT ALL PRIVILEGES ON ietf_utf8.* TO django@localhost IDENTIFIED BY 'RkTkDPFnKpko'; FLUSH PRIVILEGES;" - - echo "Fetching database ..." - DUMPDIR=/home/$USER/$DATADIR - wget -N -P $DUMPDIR http://www.ietf.org/lib/dt/sprint/ietf_utf8.sql.gz - - echo "Loading database ..." - gunzip < $DUMPDIR/ietf_utf8.sql.gz \ - | pv --progress --bytes --rate --eta --cursor --size $(gzip --list --quiet $DUMPDIR/ietf_utf8.sql.gz | awk '{ print $2 }') \ - | sed -e 's/ENGINE=MyISAM/ENGINE=InnoDB/' \ - | mysql --user=django --password=RkTkDPFnKpko -s -f ietf_utf8 \ - && rm /tmp/ietf_utf8.sql.gz - fi + echo "WARNING: IETF database seems to be missing; populating it from dump." + mysqladmin -u root --default-character-set=utf8 create ietf_utf8 + pushd /mariadb-sys-master || exit + mysql -u root < sys_10.sql + popd || exit + mysql -u root ietf_utf8 <<< "GRANT ALL PRIVILEGES ON *.* TO django@localhost IDENTIFIED BY 'RkTkDPFnKpko'; FLUSH PRIVILEGES;" + /root/src/docker/updatedb fi -if ! grep -q ":$GID:$" /etc/group ; then - echo "Creating group entry for GID '$GID' ..." - groupadd -g "$GID" "$USER" -fi -if ! id -u "$USER" &> /dev/null; then - echo "Creating user '$USER' ..." - useradd -s /bin/bash --groups staff,sudo --uid $UID --gid $GID $USER - echo "$USER:$USER" | chpasswd -fi - -VIRTDIR="/opt/home/$USER/$TAG" -echo "Checking that there's a virtual environment for $TAG ..." -if [ ! -f $VIRTDIR/bin/activate ]; then - echo "Setting up python virtualenv at $VIRTDIR ..." - mkdir -p $VIRTDIR - python3.6 -m venv $VIRTDIR - echo -e " -# This is from $VIRTDIR/bin/activate, to activate the -# datatracker virtual python environment on docker container entry: -" >> /etc/bash.bashrc - cat $VIRTDIR/bin/activate >> /etc/bash.bashrc - cat /usr/local/share/datatracker/setprompt >> /etc/bash.bashrc -else - echo "Using virtual environment at $VIRTDIR" -fi - -echo "Activating the virtual python environment ..." -. $VIRTDIR/bin/activate - - -if ! $VIRTDIR/bin/python -c "import django"; then - echo "Installing requirements ..." - pip install --upgrade pip - reqs=/home/$USER/$CWD/requirements.txt - if [ ! -f $reqs ]; then - echo " Using $reqs" - pip install -r $reqs - else - echo " Didn't find $reqs" - echo " Using /usr/local/share/datatracker/requirements.txt" - pip install -r /usr/local/share/datatracker/requirements.txt - fi -fi - -if [ ! -f /home/$USER/$CWD/ietf/settings_local.py ]; then - echo "Setting up a default settings_local.py ..." - cp /home/$USER/$CWD/docker/settings_local.py /home/$USER/$CWD/ietf/settings_local.py -fi - -for sub in test/id/ test/staging/ test/archive/ test/rfc test/media test/wiki/ietf; do - dir="/home/$USER/$CWD/$sub" - if [ ! -d "$dir" ]; then - echo "Creating dir $dir" - mkdir -p "$dir"; - fi -done - for sub in \ - nomcom_keys/public_keys \ - developers/ietf-ftp \ - developers/ietf-ftp/bofreq \ - developers/ietf-ftp/charter \ - developers/ietf-ftp/conflict-reviews \ - developers/ietf-ftp/internet-drafts \ - developers/ietf-ftp/rfc \ - developers/ietf-ftp/status-changes \ - developers/ietf-ftp/yang/catalogmod \ - developers/ietf-ftp/yang/draftmod \ - developers/ietf-ftp/yang/ianamod \ - developers/ietf-ftp/yang/invalmod \ - developers/ietf-ftp/yang/rfcmod \ - developers/www6s \ - developers/www6s/staging \ - developers/www6s/wg-descriptions \ - developers/www6s/proceedings \ - developers/www6/ \ - developers/www6/iesg \ - developers/www6/iesg/evaluation \ + test/id \ + test/staging \ + test/archive \ + test/rfc \ + test/media \ + test/wiki/ietf \ + data/nomcom_keys/public_keys \ + data/developers/ietf-ftp \ + data/developers/ietf-ftp/bofreq \ + data/developers/ietf-ftp/charter \ + data/developers/ietf-ftp/conflict-reviews \ + data/developers/ietf-ftp/internet-drafts \ + data/developers/ietf-ftp/rfc \ + data/developers/ietf-ftp/status-changes \ + data/developers/ietf-ftp/yang/catalogmod \ + data/developers/ietf-ftp/yang/draftmod \ + data/developers/ietf-ftp/yang/ianamod \ + data/developers/ietf-ftp/yang/invalmod \ + data/developers/ietf-ftp/yang/rfcmod \ + data/developers/www6s \ + data/developers/www6s/staging \ + data/developers/www6s/wg-descriptions \ + data/developers/www6s/proceedings \ + data/developers/www6/ \ + data/developers/www6/iesg \ + data/developers/www6/iesg/evaluation \ ; do - dir="/home/$USER/$CWD/data/$sub" + dir="/root/src/$sub" if [ ! -d "$dir" ]; then - echo "Creating dir $dir" + echo "Creating dir $dir" mkdir -p "$dir"; - chown "$USER" "$dir" fi done -if [ ! -f "/home/$USER/$CWD/test/data/draft-aliases" ]; then - echo "Generating draft aliases ..." - ietf/bin/generate-draft-aliases } +if [ ! -f /root/src/ietf/settings_local.py ]; then + echo "Setting up a default settings_local.py ..." + cp /root/src/docker/settings_local.py /root/src/ietf/settings_local.py fi -if [ ! -f "/home/$USER/$CWD/test/data/group-aliases" ]; then - echo "Generating group aliases ..." - ietf/bin/generate-wg-aliases } -fi +python -m smtpd -n -c DebuggingServer localhost:2025 & +echo -chown -R $USER /opt/home/$USER -chmod -R g+w /usr/local/lib/ # so we can patch libs if needed - -cd "/home/$USER/$CWD" || cd "/home/$USER/" - -echo "Done!" -if ! echo "$LANG" | grep "UTF-8"; then - echo "" - echo "Make sure you export LANG=en_GB.UTF-8 (or another UTF-8 locale) in your .bashrc" +if [ -z "$*" ]; then + bash else - echo "LANG=$LANG" + bash -c "$*" fi -HOME=/opt/home/$USER - -su -p $USER +service mariadb stop +service rsyslog stop \ No newline at end of file diff --git a/docker/install-extras b/docker/install-extras deleted file mode 100755 index 6c8e700dc..000000000 --- a/docker/install-extras +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -export DEBIAN_FRONTEND=noninteractive -sudo su - -c "apt-get update \ - && apt-get install -qy \ - apt-file \ - apt-show-versions \ - graphviz \ - ghostscript \ - apache2-utils \ - chromium-driver \ - enscript \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/*" diff --git a/docker/mailserver b/docker/mailserver deleted file mode 100755 index b4d8af9e7..000000000 --- a/docker/mailserver +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -version=0.10 -program=${0##*/} -progdir=${0%/*} -if [ "$progdir" = "$program" ]; then progdir="."; fi -if [ "$progdir" = "." ]; then progdir="$PWD"; fi -parent=$(dirname $progdir) -if [ "$parent" = "." ]; then parent="$PWD"; fi - -# ---------------------------------------------------------------------- - -$progdir/run python -m smtpd -n -c DebuggingServer localhost:2025 diff --git a/docker/run b/docker/run index 96d92dfec..a24993598 100755 --- a/docker/run +++ b/docker/run @@ -1,13 +1,13 @@ #!/bin/bash -version=0.10 +version=0.20 program=${0##*/} progdir=${0%/*} if [ "$progdir" = "$program" ]; then progdir="."; fi if [ "$progdir" = "." ]; then progdir="$PWD"; fi -parent=$(dirname $progdir) +parent=$(dirname "$progdir") if [ "$parent" = "." ]; then parent="$PWD"; fi -if [[ $(uname) =~ CYGWIN.* ]]; then parent=$(echo $parent | sed -e 's/^\/cygdrive\/\(.\)/\1:/'); fi +if [[ $(uname) =~ CYGWIN.* ]]; then parent=$(echo "$parent" | sed -e 's/^\/cygdrive\/\(.\)/\1:/'); fi # ---------------------------------------------------------------------- function usage() { @@ -30,10 +30,10 @@ DESCRIPTION EOF echo -e "OPTIONS" - if [ "$(uname)" = "Linux" ]; then - egrep "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" $0 | tr -s "\t|" "\t," | sed -r -e 's/\)[ \t]+([A-Z]+)=\$2[^#]*#/=\1\t/' -e 's/\)[^#]*#/\t/' + if [ "$(uname)" = "Linux" ] || [ "$(uname)" = "Darwin" ]; then + grep -E "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" "$0" | tr -s "\t|" "\t," | sed -r -e 's/\)[ \t]+([A-Z]+)=\$2[^#]*#/=\1\t/' -e 's/\)[^#]*#/\t/' else - egrep "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" $0 | sed 's/\|.*\$2[^#]*#/ /'| sed -E 's/\|.*\)[^#]*#/ /' + grep -E "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" "$0" | sed 's/\|.*\$2[^#]*#/ /'| sed -E 's/\|.*\)[^#]*#/ /' fi cat <<EOF @@ -47,7 +47,7 @@ COPYRIGHT Copyright (c) 2015 IETF Trust and the persons identified as authors of the code. All rights reserved. License 'Simplified BSD', as specified in http://opensource.org/licenses/BSD-3-Clause. - + EOF } @@ -58,10 +58,6 @@ function die() { exit 1 } -function note() { - if [ -n "$VERBOSE" ]; then echo -e "$*"; fi -} - # ---------------------------------------------------------------------- function version() { echo -e "$program $version" @@ -75,48 +71,32 @@ trap 'echo "$program($LINENO): Command failed with error code $? ([$$] $0 $*)"; # Option parsing # Options -shortopts=dhi:m:Mp:r:t:vVu:cC -longopts=download-data,help,ietfdb-url=,mysqldata=,no-mysqldir,port=,docker-repo=,tag=,verbose,version,user=,cached,no-cached +shortopts=hp:vVcC +longopts=help,port=,version,cached,no-cached # Default values MYSQLDIR=$parent/data/mysql -FILEDIR=$parent/../data/ PORT=8000 REPO="ietf/datatracker-environment" -DBURL=https://www.ietf.org/lib/dt/sprint/ietf_utf8.bin.tar.bz2 -WHO=$(whoami) -CACHED='' +CACHED=':cached' if [ "$(uname)" = "Linux" ]; then args=$(getopt -o "$shortopts" --long "$longopts" -n '$program' -- $SV "$@") if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi eval set -- "$args" - sed="sed -r" else # Darwin, BSDs args=$(getopt -o$shortopts $SV $*) if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi set -- $args - sed="sed -E" - if [ "$(uname)" = "Darwin" ]; then - CACHED=':cached' - fi fi while true ; do case "$1" in -c| --cached) CACHED=':cached';; # Use cached disk access to reduce system load - -C| --no-cached) CACHED='';; # Use fully synchronized disk access - -d| --download-data) DOWNLOAD=1;; # Download and set up the database files + -C| --no-cached) CACHED=':consistent';; # Use fully synchronized disk access -h| --help) usage; exit;; # Show this help, then exit - -f| --filedir) FILEDIR=$2; shift;; # Set the desired location of drafts, charters etc. - -i| --ietfdb-url) DBURL=$2; shift;; # Use an alternative database tarball URL - -m| --mysqldir) MYSQLDIR=$2; shift;; # Set the desired location of MySQL's database files -p| --port) PORT=$2; shift;; # Bind the container's port 8000 to external port PORT - -r| --docker-repo) REPO=$2; shift;; # Use the given docker repository, instead of the default - -t| --tag) TAG=$2; shift;; # Use this docker image tag, instead of the svn branch name - -u| --user) WHO=$2; shift;; # Run the container as the specified user - -v| --verbose) VERBOSE=1;; # Be more talkative -V| --version) version; exit;; # Show program version, then exit --) shift; break;; *) die "Internal error, inconsistent option specification: '$1'";; @@ -125,163 +105,14 @@ while true ; do done if [ -z "$TAG" ]; then - TAG=$(basename $(svn info $parent | grep ^URL | awk '{print $2}')) + TAG=$(basename "$(svn info "$parent" | grep ^URL | awk '{print $2}')") fi # ---------------------------------------------------------------------- -# The program itself -[ -f /proc/1/cgroups ] && grep 'docker' /proc/1/cgroups && die "It looks like you're inside docker already ..." - -if [ "$(uname)" = "Darwin" ]; then - APP="/Applications/Docker.app" - CMD="open -a" -elif [ "$(uname)" = "Linux" ]; then - echo "Running on Linux." -elif [[ $(uname) =~ CYGWIN.* ]]; then - echo "Running under Cygwin." - CMD="echo" - MYSQLDIR=$(echo $MYSQLDIR | sed -e 's/^\/cygdrive\/\(.\)/\1:/') - WHO=$(echo $WHO | sed -e 's/^.*\\//' | tr -d \\r) - DRIVE=$(echo $USERPROFILE | sed -e 's/\(.\).*/\1/' -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefdhijklmnopqartuvwxyz/') - HOME=$DRIVE$(echo $USERPROFILE | sed -e 's/\\/\//g' -e's/.\(.*\)/\1/') - echo "Using home dir $HOME" - PWD=$(echo $PWD | sed -e 's/^\/cygdrive\/\(.\)/\1:/') - if [ "${PWD#$HOME}" = "$PWD" ]; then - die "You must work inside your home directory ($HOME)" - fi - CGWPARENT=$(echo $parent | sed -e 's/^\(.\)\:/\/cygdrive\/\1/') - ICSFILES=$(/usr/bin/find $CGWPARENT/vzic/zoneinfo/ -name '*.ics' -print) - for ICSFILE in $ICSFILES - do - LINK=$(head -n1 $ICSFILE | sed -e '/link .*/!d' -e 's/link \(.*\)/\1/') - if [ "$LINK" ]; then - WDIR=$(dirname $ICSFILE) - echo "Replacing $(basename $ICSFILE) with $LINK" - cp -f $WDIR/$LINK $ICSFILE - fi - done -else - die "This script does not have support for your architecture ($(uname)); sorry :-(" -fi - -WHOUID=$(id -u $WHO) -WHOGID=$(id -g $WHO) - -if [ "$(uname)" = "Linux" ]; then - echo "Not trying to start a virtual docker machine on Linux" -elif [[ $(uname) =~ CYGWIN.* ]]; then - if ! docker info 1> /dev/null 2>&1; then - echo -e "The docker VM doesn't seem to be running; docker info gave:\n $info" - die "Don't know how to start docker when running under Cygwin" - fi - TAG=$(echo $TAG | tr -d \\r) - URL="http://localhost:$PORT/" -elif [ -e "$APP" ]; then - info=$(docker info 2>&1 || true) - if ! docker info 1> /dev/null 2>&1; then - echo -e "The docker VM doesn't seem to be running; docker info gave:\n $info" - echo "Will attempt to start docker by doing '\$ $CMD $APP':" - $CMD $APP - declare -i count - printf "Waiting for docker engine .." - while true; do - printf "." - sleep 2 - if docker info >/dev/null 2>&1; then break; fi - count=$(( $count + 1)) - if [ $count -gt 10 ]; then - die "No contact with docker engine; bailing out." - fi - done - fi - URL="http://localhost:$PORT/" -else - if [ $($machine status default | tr "A-Z" "a-z" ) != "running" ]; then - echo "The docker VM doesn't seem to be running; will attempt to start it by doing '\$ $machine start':" - $machine start || die "Failed taking up the Docker VM" - fi - - if [ -f "$machine" ]; then - if [ $($machine status default | tr "A-Z" "a-z") = "running" ]; then - tmpfile=$(mktemp docker.run.XXXXXXXX) - if $machine env 2>/dev/null | grep DOCKER_ > $tmpfile; then - mv $tmpfile ~/.docker-info - elif printenv | grep DOCKER_ > $tmpfile; then - mv $tmpfile ~/.docker-info - else - rm $tmpfile - die "Failed setting the appropriate DOCKER_* environment variables." - fi - . ~/.docker-info - else - rm -f ~/.docker-info - fi - ip=$($machine ip) - URL="http://$ip:$PORT/" - fi -fi - -image=$(docker ps | grep "$REPO:$TAG" | awk '{ print $1 }') -if [ "$image" ]; then - if [ "$*" ]; then - echo "Running 'cd ~/${parent#$HOME/}; $*'" - docker exec -u "$WHO" -t $image bash -i -c "cd; cd \"${parent#$HOME/}\"; $*" - else - docker exec -u "$WHO" -ti $image bash -i - fi -else - echo "" - echo "Starting a docker container for '$TAG'." - - if [ -n "$DOWNLOAD" ]; then - ( - cd "$(dirname $MYSQLDIR)" - wget -N "$DBURL" && tar xjf ietf_utf8.bin.tar.bz2 && chmod -R go+rwX mysql - ) - [ -d "$MYSQLDIR" ] || die "The download seems to have failed; still no $MYSQLDIR. Giving up." - else - [ -d "$MYSQLDIR" ] || die "Expected $MYSQLDIR to exist, but it\ndidn't. Use '$program -d' to download and unpack the database." - fi - - if ! docker images "$REPO" | grep -q "$TAG"; then - echo "Fetching docker image '$REPO:$TAG'" - if ! docker pull "$REPO:$TAG"; then - docker pull "$REPO:latest" || die "Failed to pull down the '$REPO:latest' docker image" - id=$(docker images "$REPO" | grep latest | awk '{print $3}') - echo "Tagging $REPO:latest as $REPO:$TAG for use as environment for this branch." - docker tag $id "$REPO:$TAG" - fi - fi - - echo -e "\nThe web interface for 'runserver' should appear on $URL\n" - echo -e "User $WHO ($WHOUID:$WHOGID)" - if [ -z "$MYSQLDIR" ]; then - docker run -ti -p $PORT:8000 -v "$HOME:/home/$WHO$CACHED" \ - -e USER="$WHO" -e DATADIR="${parent#$HOME/}/data" -e CWD="${PWD#$HOME/}" \ - -e TAG="$TAG" -e FILEDIR=${FILEDIR#$HOME} -e UID="$WHOUID" -e GID="$WHOGID" \ - "$REPO:$TAG" "$@" - else - docker run -ti -p $PORT:8000 -v "$HOME:/home/$WHO$CACHED" \ - -v "$MYSQLDIR:/var/lib/mysql" -e USER="$WHO" \ - -e DATADIR="${parent#$HOME/}/data" -e CWD="${PWD#$HOME/}" -e TAG="$TAG" \ - -e FILEDIR=${FILEDIR#$HOME} -e UID="$WHOUID" -e GID="$WHOGID" \ - "$REPO:$TAG" "$@" - fi - - echo "" - echo "Committing changes in the container to an image:" - image=$( docker images -q $REPO:$TAG) - latest=$(docker ps -lq -f "ancestor=$image") - docker commit $latest $REPO:$TAG - - echo "" - echo "Cleaning up containers and images" - docker rm $latest - DANGLING=$( docker images -f dangling=true -q ) - if [ -n "$DANGLING" ]; then - echo "Dangling images: $DANGLING" - docker rmi -f $DANGLING - fi - -fi +echo "Starting a docker container for '$REPO:$TAG'." +mkdir -p "$MYSQLDIR" +docker run -ti -p "$PORT":8000 -p 33306:3306 \ + -v "$parent:/root/src$CACHED" \ + -v "$MYSQLDIR:/var/lib/mysql:delegated" \ + "$REPO:$TAG" "$@" \ No newline at end of file diff --git a/docker/setprompt b/docker/setprompt deleted file mode 100644 index 62ce46000..000000000 --- a/docker/setprompt +++ /dev/null @@ -1,26 +0,0 @@ - -# set a fancy prompt -PS_TIME="\A " - -if [ "$(whoami)" = "root" ]; then - PS_COLOUR='\[\033[1;31m\]' - PS_END="\[\033[m\]" -else - PS_COLOUR="\[\033[1;34m\]" - PS_END="\[\033[m\]" -fi - -if [ "$TERM" = xterm ]; then - PS_XTERM="\[\033]0;\]\h:\w\007" -else - PS_XTERM="" -fi -COLOUR_RED='\[\033[1;31m\]' -COLOUR_BLK='\[\033[1;30m\]' -if [ "$SHLVL" -gt 1 ]; then - PS_SHLVL="$(eval "printf '>%.0s' {2..$SHLVL}") " -else - PS_SHLVL="" -fi - -PS1="$PS_XTERM\n$COLOUR_RED$PS_SHLVL$PS_COLOUR$PS_TIME$COLOUR_RED$PS_COLOUR${VIRTUAL_ENV:+$COLOUR_RED($(basename $VIRTUAL_ENV))$PS_COLOUR }\w\n\u @ $COLOUR_BLK\h$PS_COLOUR \\$ $PS_END" diff --git a/docker/setupdb b/docker/setupdb deleted file mode 100755 index 9d517d044..000000000 --- a/docker/setupdb +++ /dev/null @@ -1,114 +0,0 @@ -#!/bin/bash - -version=0.10 -program=${0##*/} -progdir=${0%/*} -if [ "$progdir" = "$program" ]; then progdir="."; fi -if [ "$progdir" = "." ]; then progdir="$PWD"; fi -parent=$(dirname $progdir) -if [ "$parent" = "." ]; then parent="$PWD"; fi - -# ---------------------------------------------------------------------- -function usage() { - cat <<EOF -NAME - $program - Set up a local copy of the IETF database MySQL files - -SYNOPSIS - $program [OPTIONS] ARGS - -DESCRIPTION - - This script downloads a prebuilt copy of the IETF database MySQL - files, ready for mapping into the /var/lib/mysql/ directory of the - ietf/database-environment Docker image. - -EOF - echo -e "OPTIONS" - if [ "$(uname)" = "Linux" ]; then - egrep "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" $0 | tr -s "\t|" "\t," | sed -r -e 's/\)[ \t]+([A-Z]+)=\$2[^#]*#/=\1\t/' -e 's/\)[^#]*#/\t/' - else - egrep "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" $0 | sed 's/\|.*\$2[^#]*#/ /'| sed -E 's/\|.*\)[^#]*#/ /' - fi - cat <<EOF - -FILES - -AUTHOR - Written by Henrik Levkowetz, <henrik@levkowetz.com> - -COPYRIGHT - - Copyright (c) 2015 IETF Trust and the persons identified as authors of - the code. All rights reserved. License 'Simplified BSD', as specified - in http://opensource.org/licenses/BSD-3-Clause. - -EOF - -} - -# ---------------------------------------------------------------------- -function die() { - echo -e "\n$program: error: $*" >&2 - exit 1 -} - -function note() { - if [ -n "$VERBOSE" ]; then echo -e "$*"; fi -} - -# ---------------------------------------------------------------------- -function version() { - echo -e "$program $version" -} - -# ---------------------------------------------------------------------- -trap 'echo "$program($LINENO): Command failed with error code $? ([$$] $0 $*)"; exit 1' ERR - - -# ---------------------------------------------------------------------- -# Option parsing - -# Options -shortopts=hvV -longopts=help,verbose,version - -if [ "$(uname)" = "Linux" ]; then - args=$(getopt -o "$shortopts" --long "$longopts" -n '$program' -- $SV "$@") - if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi - eval set -- "$args" - sed="sed -r" -else - # Darwin, BSDs - args=$(getopt -o$shortopts $SV $*) - if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi - set -- $args - sed="sed -E" -fi - -while true ; do - case "$1" in - -h| --help) usage; exit;; # Show this help, then exit - -v| --verbose) VERBOSE=1;; # Be more talkative - -V| --version) version; exit;; # Show program version, then exit - --) shift; break;; - *) die "Internal error, inconsistent option specification: '$1'";; - esac - shift -done - -# ---------------------------------------------------------------------- -# The program itself - -[ -n "$MYSQLDIR" ] || MYSQLDIR=$parent/data/mysql -[ -n "$URL" ] || URL=rsync.ietf.org::dev.db/ietf_utf8.bin.tar.bz2 - -cd $(dirname $MYSQLDIR) -echo "Downloading a MySQL database image ..." -set $(rsync --version | head -n1 | awk '{print $3}' | tr '.' ' ') -maj=$1; min=$2; fix=$3 -if [ "$maj" -ge "3" -a "$min" -ge "1" ]; then - rsync --info=progress2 $URL ./ && echo -e "\nUnpacking database ..." && tar xjf ietf_utf8.bin.tar.bz2 && echo "Fixing permissions ..." && chmod -R go+rwX mysql -else - rsync -v $URL ./ && echo -e "\nUnpacking database ..." && tar xjf ietf_utf8.bin.tar.bz2 && echo "Fixing permissions ..." && chmod -R go+rwX mysql -fi diff --git a/docker/updatedb b/docker/updatedb index 595f3f73d..b1fda05b5 100755 --- a/docker/updatedb +++ b/docker/updatedb @@ -1,12 +1,12 @@ #!/bin/bash # -*- indent-with-tabs: 1 -*- -version=0.10 +version=0.20 program=${0##*/} progdir=${0%/*} if [ "$progdir" = "$program" ]; then progdir="."; fi if [ "$progdir" = "." ]; then progdir="$PWD"; fi -parent=$(dirname $progdir) +parent=$(dirname "$progdir") if [ "$parent" = "." ]; then parent="$PWD"; fi # ---------------------------------------------------------------------- @@ -26,9 +26,9 @@ DESCRIPTION EOF echo -e "OPTIONS" if [ "$(uname)" = "Linux" ]; then - egrep "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" $0 | tr -s "\t|" "\t," | sed -r -e 's/\)[ \t]+([A-Z]+)=\$2[^#]*#/=\1\t/' -e 's/\)[^#]*#/\t/' + grep -E "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" "$0" | tr -s "\t|" "\t," | sed -r -e 's/\)[ \t]+([A-Z]+)=\$2[^#]*#/=\1\t/' -e 's/\)[^#]*#/\t/' else - egrep "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" $0 | sed 's/\|.*\$2[^#]*#/ /'| sed -E 's/\|.*\)[^#]*#/ /' + grep -E "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" "$0" | sed 's/\|.*\$2[^#]*#/ /'| sed -E 's/\|.*\)[^#]*#/ /' fi cat <<EOF @@ -42,21 +42,17 @@ COPYRIGHT Copyright (c) 2015 IETF Trust and the persons identified as authors of the code. All rights reserved. License 'Simplified BSD', as specified in http://opensource.org/licenses/BSD-3-Clause. - + EOF } # ---------------------------------------------------------------------- function die() { - echo -e "\n$program: error: $*" >&2 + echo -e "\n$program: error: $*" >&2 exit 1 } -function note() { - if [ -n "$VERBOSE" ]; then echo -e "$*"; fi -} - # ---------------------------------------------------------------------- function version() { echo -e "$program $version" @@ -71,24 +67,21 @@ trap 'echo "$program($LINENO): Command failed with error code $? ([$$] $0 $*)"; # Options shortopts=DLZhqvV -longopts=no-download,no-load,no-zap,help,quiet,verbose,version +longopts=no-download,no-load,no-zap,help,version LOAD=1 DOWNLOAD=1 DROP=1 -QUIET="" if [ "$(uname)" = "Linux" ]; then args=$(getopt -o "$shortopts" --long "$longopts" -n "$program" -- $SV "$@") if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi eval set -- "$args" - sed="sed -r" else # Darwin, BSDs args=$(getopt -o$shortopts $SV $*) if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi set -- $args - sed="sed -E" fi while true ; do @@ -97,8 +90,6 @@ while true ; do -L| --no-load) LOAD=""; ;; # Don't load the database -Z| --no-zap) DROP="";; # Don't drop new tables -h| --help) usage; exit;; # Show this help, then exit - -q| --quiet) QUIET="-q";; # Don't show progress - -v| --verbose) VERBOSE=1;; # Be more talkative -V| --version) version; exit;; # Show program version, then exit --) shift; break;; *) die "Internal error, inconsistent option specification: '$1'";; @@ -109,50 +100,27 @@ done # ---------------------------------------------------------------------- # The program itself -echo "Gathering info ..." -MYSQLDIR="$(/usr/sbin/mysqld --verbose --help 2>/dev/null | awk '$1 == "datadir" { print $2; exit }')" -MYSQLDIR=${MYSQLDIR%/} DATADIR=$parent/data -# echo "Checking if MySQL base data exists ..." -# if [ ! -d $MYSQLDIR/mysql ]; then -# die "Expected the directory $MYSQLDIR/mysql/ to exist -- have you downloaded and unpacked the IETF binary database tarball?" -# fi - - -# echo "Checking if the IETF database exists at $MYSQLDIR ..." -# if [ ! -d $MYSQLDIR/ietf_utf8 ]; then -# echo "Creating database ..." -# mysqladmin -u root --default-character-set=utf8 create ietf_utf8 -# -# echo "Setting up permissions ..." -# mysql -u root ietf_utf8 <<< "GRANT ALL PRIVILEGES ON ietf_utf8.* TO django@localhost IDENTIFIED BY 'RkTkDPFnKpko'; FLUSH PRIVILEGES;" -# fi - +DUMP=ietf_utf8.sql.gz if [ "$DOWNLOAD" ]; then - echo "Fetching database dump ..." - wget $QUIET -N -P $DATADIR https://www.ietf.org/lib/dt/sprint/ietf_utf8.sql.gz || die "No new data, quitting." + echo "Fetching database dump..." + rsync --info=progress2 rsync.ietf.org::dev.db/$DUMP "$DATADIR" fi if [ "$LOAD" ]; then - echo "Loading database ..." - if [ -z "$QUIET" ]; then - gunzip < $DATADIR/ietf_utf8.sql.gz \ - | pv --progress --bytes --rate --eta --size $(gzip --list --quiet $DATADIR/ietf_utf8.sql.gz | awk '{ print $2 }') \ - | sed -e 's/ENGINE=MyISAM/ENGINE=InnoDB/' \ - | $parent/ietf/manage.py dbshell - else - gunzip < $DATADIR/ietf_utf8.sql.gz \ - | sed -e 's/ENGINE=MyISAM/ENGINE=InnoDB/' \ - | $parent/ietf/manage.py dbshell - fi - + echo "Loading database..." + SIZE=$(pigz --list "$DATADIR/$DUMP" | tail -n 1 | awk '{ print $2 }') + pigz -d < "$DATADIR/$DUMP" \ + | pv --progress --bytes --rate --eta --size "$SIZE" \ + | sed -e 's/ENGINE=MyISAM/ENGINE=InnoDB/' \ + | "$parent/ietf/manage.py" dbshell fi if [ "$DROP" ]; then - echo "Dropping tables not in the dump (so migrations can succeed) ..." - diff <(zcat $DATADIR/ietf_utf8.sql.gz | grep '^DROP TABLE IF EXISTS' | tr -d '`;' | awk '{ print $5 }') \ - <($parent/ietf/manage.py dbshell <<< 'show tables;' | tail -n +2) \ + echo "Dropping tables not in the dump (so migrations can succeed)..." + diff <(pigz -d -c "$DATADIR/$DUMP" | grep '^DROP TABLE IF EXISTS' | tr -d '`;' | awk '{ print $5 }') \ + <("$parent/ietf/manage.py" dbshell <<< 'show tables;' | tail -n +2) \ | grep '^>' | awk '{print "drop table if exists", $2, ";";}' \ - | tee >(cat >&2) | $parent/ietf/manage.py dbshell + | tee >(cat >&2) | "$parent/ietf/manage.py" dbshell fi \ No newline at end of file From c4ba1ea75f78d96f90f11bcc4a5874338be98dc4 Mon Sep 17 00:00:00 2001 From: Lars Eggert <lars@eggert.org> Date: Wed, 13 Oct 2021 13:09:32 +0000 Subject: [PATCH 02/15] Rip out more unused stuff, and update copyright based on TLP - Legacy-Id: 19418 --- docker/README.md | 2 +- docker/build | 82 +++++++++++++---------------------- docker/copydb | 82 ++++++++++++----------------------- docker/rsync-extras | 101 +++++++++++++++++--------------------------- docker/run | 87 +++++++++++++++----------------------- docker/updatedb | 72 +++++++++++-------------------- 6 files changed, 153 insertions(+), 273 deletions(-) diff --git a/docker/README.md b/docker/README.md index e30940efa..19d363058 100644 --- a/docker/README.md +++ b/docker/README.md @@ -18,7 +18,7 @@ 4. **TEMPORARY:** Until Lars' changes have been merged and a docker image is available for download, you will need to build it locally: - docker/build -l + docker/build This will take a while, but only needs to be done once. diff --git a/docker/build b/docker/build index 2a9c4df63..29a903b92 100755 --- a/docker/build +++ b/docker/build @@ -1,6 +1,6 @@ #!/bin/bash -version=0.11 +version=0.20 program=${0##*/} progdir=${0%/*} if [ "$progdir" = "$program" ]; then progdir="."; fi @@ -8,104 +8,80 @@ if [ "$progdir" = "." ]; then progdir="$PWD"; fi parent=$(dirname "$progdir") if [ "$parent" = "." ]; then parent="$PWD"; fi -export LANG=C -# ---------------------------------------------------------------------- function usage() { cat <<EOF NAME - $program - Build a docker datatracker image. + $program - Build a datatracker docker image SYNOPSIS - $program [OPTIONS] ARGS + $program [OPTIONS] DESCRIPTION - - This script builds a debian-based docker image which has been + This script builds a Ubuntu-based docker image that has been set up with the dependencies needed to easily run the IETF datatracker in development mode. It uses docker/Dockerfile; i.e., the Dockerfile in the same directory as this script. - It assumes that the user has upload rights for the docker - ietf/datatracker-environment repository, in order to push the - image. +OPTIONS EOF - echo -e "OPTIONS" - if [ "$(uname)" = "Linux" ] || [ "$(uname)" = "Darwin" ]; then - grep -E "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" "$0" | tr -s "\t|" "\t," | sed -r -e 's/\)[ \t]+([A-Z]+)=\$2[^#]*#/=\1\t/' -e 's/\)[^#]*#/\t/' - else - grep -E "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" "$0" | sed 's/\|.*\$2[^#]*#/ /'| sed -E 's/\|.*\)[^#]*#/ /' - fi + sed -n -E "s|(^\s+-[A-Za-z])\).*#(.*)|\1 \2|p" "$0" cat <<EOF -FILES - AUTHOR - Written by Henrik Levkowetz, <henrik@levkowetz.com> + Written by: + Henrik Levkowetz, <henrik@levkowetz.com> + Lars Eggert, <lars@eggert.org> COPYRIGHT - Copyright (c) 2016 IETF Trust and the persons identified as authors of - the code. All rights reserved. License 'Simplified BSD', as specified - in http://opensource.org/licenses/BSD-3-Clause. + the code. All rights reserved. Redistribution and use in source and + binary forms, with or without modification, is permitted pursuant to, + and subject to the license terms contained in, the Revised BSD + License set forth in Section 4.c of the IETF Trust’s Legal Provisions + Relating to IETF Documents(https://trustee.ietf.org/license-info). EOF - } -# ---------------------------------------------------------------------- + function die() { echo -e "\n$program: error: $*" >&2 exit 1 } -# ---------------------------------------------------------------------- + function version() { echo -e "$program $version" } -# ---------------------------------------------------------------------- + trap 'echo "$program($LINENO): Command failed with error code $? ([$$] $0 $*)"; exit 1' ERR - -# ---------------------------------------------------------------------- -# Option parsing - -# Options -shortopts=hlt:vV -longopts=help,local,tag=,version - # Default values IMAGE=ietf/datatracker-environment TAG=$(basename "$(svn info "$parent" | grep ^URL | awk '{print $2}')") -LOCAL="" +LOCAL=1 -if [ "$(uname)" = "Linux" ]; then - args=$(getopt -o "$shortopts" --long "$longopts" -n '$program' -- $SV "$@") - if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi - eval set -- "$args" -else - # Darwin, BSDs - args=$(getopt -o$shortopts $SV $*) - if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi - set -- $args -fi +# Option parsing +shortopts=hut:V +args=$(getopt -o$shortopts $*) +if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi +set -- $args while true ; do case "$1" in - -h| --help) usage; exit;; # Show this help, then exit - -l| --local) LOCAL=1;; # Don't upload - -t| --tag) TAG=$2; shift;; # Use this docker image tag, instead of the latest svn tags name - -V| --version) version; exit;; # Show program version, then exit - --) shift; break;; - *) die "Internal error, inconsistent option specification: '$1'";; + -h) usage; exit;; # Show this help, then exit + -u) LOCAL=0;; # Upload image to repository after build + -t) TAG=$2; shift;; # Use this docker image tag + -V) version; exit;; # Show program version, then exit + --) shift; break;; + *) die "Internal error, inconsistent option specification: '$1'";; esac shift done -# ---------------------------------------------------------------------- # The program itself - docker rmi -f $IMAGE:trunk 2>/dev/null || true docker build --progress plain -t "$IMAGE:$TAG" docker/ docker tag "$(docker images -q $IMAGE | head -n 1)" $IMAGE:latest diff --git a/docker/copydb b/docker/copydb index 672319769..27ac3b395 100755 --- a/docker/copydb +++ b/docker/copydb @@ -1,118 +1,88 @@ #!/bin/bash -version=0.10 +version=0.11 program=${0##*/} progdir=${0%/*} if [ "$progdir" = "$program" ]; then progdir="."; fi if [ "$progdir" = "." ]; then progdir="$PWD"; fi -parent=$(dirname $progdir) +parent=$(dirname "$progdir") if [ "$parent" = "." ]; then parent="$PWD"; fi -export LANG=C -# ---------------------------------------------------------------------- function usage() { cat <<EOF NAME - $program - Make a tarball of the MySQL database files and upload it. + $program - Make a tarball of the MySQL database files and upload it SYNOPSIS $program [OPTIONS] DESCRIPTION - This script creates a compressed tarball from the MySQL database files on disk, and uploads it to the ietf datatracker developer area on www.ietf.org. It is intended to be used with the docker datatracker environment, after you have set up the database with docker/setupdb, started the docker - image with docker/run, and updated the database with docker/updatedb. + image with docker/run, and updated the database with docker/updatedb. To use it, exit from the docker container, to make sure that mysqldb isn't running and all the files consistent and available for copy. Then run docker/$program outside the docker container. You need to have ssh access to www.ietf.org in order for the scp command to succeed. +OPTIONS EOF - echo -e "OPTIONS" - if [ "$(uname)" = "Linux" ]; then - egrep "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" $0 | tr -s "\t|" "\t," | sed -r -e 's/\)[ \t]+([A-Z]+)=\$2[^#]*#/=\1\t/' -e 's/\)[^#]*#/\t/' - else - egrep "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" $0 | sed 's/\|.*\$2[^#]*#/ /'| sed -E 's/\|.*\)[^#]*#/ /' - fi + sed -n -E "s|(^\s+-[A-Za-z])\).*#(.*)|\1 \2|p" "$0" cat <<EOF -FILES - AUTHOR - Written by Henrik Levkowetz, <henrik@levkowetz.com> + Written by: + Henrik Levkowetz, <henrik@levkowetz.com> + Lars Eggert, <lars@eggert.org> COPYRIGHT - Copyright (c) 2016 IETF Trust and the persons identified as authors of - the code. All rights reserved. License 'Simplified BSD', as specified - in http://opensource.org/licenses/BSD-3-Clause. - -EOF + the code. All rights reserved. Redistribution and use in source and + binary forms, with or without modification, is permitted pursuant to, + and subject to the license terms contained in, the Revised BSD + License set forth in Section 4.c of the IETF Trust’s Legal Provisions + Relating to IETF Documents(https://trustee.ietf.org/license-info). +EOF } -# ---------------------------------------------------------------------- + function die() { echo -e "\n$program: error: $*" >&2 exit 1 } -function note() { - if [ -n "$VERBOSE" ]; then echo -e "$*"; fi -} -# ---------------------------------------------------------------------- function version() { echo -e "$program $version" } -# ---------------------------------------------------------------------- + trap 'echo "$program($LINENO): Command failed with error code $? ([$$] $0 $*)"; exit 1' ERR - -# ---------------------------------------------------------------------- # Option parsing - -# Options -shortopts=hvV -longopts=help,verbose,version - -# Default values - -if [ "$(uname)" = "Linux" ]; then - args=$(getopt -o "$shortopts" --long "$longopts" -n '$program' -- $SV "$@") - if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi - eval set -- "$args" - sed="sed -r" -else - # Darwin, BSDs - args=$(getopt -o$shortopts $SV $*) - if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi - set -- $args - sed="sed -E" -fi +shortopts=hV +args=$(getopt -o$shortopts $SV $*) +if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi +set -- $args while true ; do case "$1" in - -h| --help) usage; exit;; # Show this help, then exit - -v| --verbose) VERBOSE=1;; # Be more talkative - -V| --version) version; exit;; # Show program version, then exit - --) shift; break;; - *) die "Internal error, inconsistent option specification: '$1'";; + -h) usage; exit;; # Show this help, then exit + -V) version; exit;; # Show program version, then exit + --) shift; break;; + *) die "Internal error, inconsistent option specification: '$1'";; esac shift done -# ---------------------------------------------------------------------- # The program itself - if [ -e "/.dockerenv" -o -n "$(grep -s '/docker/' /proc/self/cgroup)" ]; then die "It looks as if you're running inside docker -- please quit docker first." fi @@ -123,4 +93,4 @@ cd $workdir echo "Building tarfile ..." tar cjf ietf_utf8.bin.tar.bz2 mysql echo "Copying tarfile to ietfa.amsl.com ..." -scp ietf_utf8.bin.tar.bz2 ietfa.amsl.com:/a/www/www6s/lib/dt/sprint/ +scp ietf_utf8.bin.tar.bz2 ietfa.amsl.com:/a/www/www6s/lib/dt/sprint/ \ No newline at end of file diff --git a/docker/rsync-extras b/docker/rsync-extras index 9353469da..5cc7f9624 100755 --- a/docker/rsync-extras +++ b/docker/rsync-extras @@ -1,127 +1,104 @@ #!/bin/bash -version=0.10 +version=0.11 program=${0##*/} progdir=${0%/*} if [ "$progdir" = "$program" ]; then progdir="."; fi if [ "$progdir" = "." ]; then progdir="$PWD"; fi -parent=$(dirname $progdir) +parent=$(dirname "$progdir") if [ "$parent" = "." ]; then parent="$PWD"; fi -export LANG=C -# ---------------------------------------------------------------------- function usage() { cat <<EOF NAME $program - Copy additional data files from the ietf server SYNOPSIS - $program [OPTIONS] + $program [OPTIONS] [DESTINATION] DESCRIPTION - This script copies additional data files used by the datatracker - from the ietf server to a local directory, for instance drafts, - charters, rfcs, agendas, minutes, etc. + from the ietf server to a local directory, for instance drafts, + charters, rfcs, agendas, minutes, etc. + If no destination is given, the default is data/developers. + +OPTIONS EOF - echo -e "OPTIONS" - if [ "$(uname)" = "Linux" ]; then - egrep "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" $0 | tr -s "\t|" "\t," | sed -r -e 's/\)[ \t]+([A-Z]+)=\$2[^#]*#/=\1\t/' -e 's/\)[^#]*#/\t/' - else - egrep "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" $0 | sed 's/\|.*\$2[^#]*#/ /'| sed -E 's/\|.*\)[^#]*#/ /' - fi + sed -n -E "s|(^\s+-[A-Za-z])\).*#(.*)|\1 \2|p" "$0" cat <<EOF -FILES - AUTHOR - Written by Henrik Levkowetz, <henrik@levkowetz.com> + Written by: + Henrik Levkowetz, <henrik@levkowetz.com> + Lars Eggert, <lars@eggert.org> COPYRIGHT - Copyright (c) 2016 IETF Trust and the persons identified as authors of - the code. All rights reserved. License 'Simplified BSD', as specified - in http://opensource.org/licenses/BSD-3-Clause. - -EOF + the code. All rights reserved. Redistribution and use in source and + binary forms, with or without modification, is permitted pursuant to, + and subject to the license terms contained in, the Revised BSD + License set forth in Section 4.c of the IETF Trust’s Legal Provisions + Relating to IETF Documents(https://trustee.ietf.org/license-info). +EOF } -# ---------------------------------------------------------------------- + function die() { echo -e "\n$program: error: $*" >&2 exit 1 } -function note() { - if [ -n "$VERBOSE" ]; then echo -e "$*"; fi -} -# ---------------------------------------------------------------------- function version() { echo -e "$program $version" } -# ---------------------------------------------------------------------- trap 'echo "$program($LINENO): Command failed with error code $? ([$$] $0 $*)"; exit 1' ERR -# ---------------------------------------------------------------------- # Option parsing - -# Options shortopts=hvV -longopts=help,verbose,version - -# Default values - -if [ "$(uname)" = "Linux" ]; then - args=$(getopt -o "$shortopts" --long "$longopts" -n '$program' -- $SV "$@") - if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi - eval set -- "$args" - sed="sed -r" -else - # Darwin, BSDs - args=$(getopt -o$shortopts $SV $*) - if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi - set -- $args - sed="sed -E" -fi +args=$(getopt -o$shortopts $SV $*) +if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi +set -- $args while true ; do case "$1" in - -h| --help) usage; exit;; # Show this help, then exit - -v| --verbose) VERBOSE=1;; # Be more talkative - -V| --version) version; exit;; # Show program version, then exit - --) shift; break;; - *) die "Internal error, inconsistent option specification: '$1'";; + -h) usage; exit;; # Show this help, then exit + -v) VERBOSE=1;; # Be more talkative + -V) version; exit;; # Show program version, then exit + --) shift; break;; + *) die "Internal error, inconsistent option specification: '$1'";; esac shift done -# ---------------------------------------------------------------------- # The program itself +if [ $# -lt 1 ]; then + DEST_ROOT=data/developers +else + DEST_ROOT="${1%/}" +fi +echo "Using destination $DEST_ROOT" -[ $# -lt 1 ] && die "Missing argument: rsync destination" - -DEST_ROOT="${1%/}" for dir in charter conflict-reviews internet-drafts review rfc slides status-changes yang; do dest="$DEST_ROOT/ietf-ftp/$dir" + mkdir -p "$dest" echo "Fetching $dest ..." rsync -auz ${VERBOSE:+--info=progress2} rsync.ietf.org::everything-ftp/$dir/ $dest/ done for dir in floor photo; do dest="$DEST_ROOT/media/$dir" + mkdir -p "$dest" echo "Fetching $dest ..." rsync -auz ${VERBOSE:+--info=progress2} rsync.ietf.org::dev.media/$dir/ $dest/ done - - dest="$DEST_ROOT/archive/id" - echo "Fetching $dest ..." - rsync -auz ${VERBOSE:+--info=progress2} rsync.ietf.org::id-archive/ $dest/ - - +dest="$DEST_ROOT/archive/id" +mkdir -p "$dest" +echo "Fetching $dest ..." +rsync -auz ${VERBOSE:+--info=progress2} rsync.ietf.org::id-archive/ $dest/ \ No newline at end of file diff --git a/docker/run b/docker/run index a24993598..87ab5511d 100755 --- a/docker/run +++ b/docker/run @@ -7,9 +7,8 @@ if [ "$progdir" = "$program" ]; then progdir="."; fi if [ "$progdir" = "." ]; then progdir="$PWD"; fi parent=$(dirname "$progdir") if [ "$parent" = "." ]; then parent="$PWD"; fi -if [[ $(uname) =~ CYGWIN.* ]]; then parent=$(echo "$parent" | sed -e 's/^\/cygdrive\/\(.\)/\1:/'); fi -# ---------------------------------------------------------------------- + function usage() { cat <<EOF NAME @@ -19,87 +18,69 @@ SYNOPSIS $program [OPTIONS] ARGS DESCRIPTION + This is a wrapper which runs an Ubuntu-based docker image which + has been set up with the dependencies needed to easily run the + IETF datatracker in development mode. - This is a wrapper which runs docker with suitable arguments on a - debian-based docker image which has been set up with the dependencies - needed to easily run the IETF datatracker in development mode. By - default, it expects to find the MySQL database files at - $parent/data/mysql, which is mapped inside the - container to /var/lib/mysql, and it will set up a home directory for - the current user ($USER) and map it to $HOME. + MySQL database files at data/mysql will be used; if they do not exist, + a database dump will be retrieved and restored on first run. +OPTIONS EOF - echo -e "OPTIONS" - if [ "$(uname)" = "Linux" ] || [ "$(uname)" = "Darwin" ]; then - grep -E "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" "$0" | tr -s "\t|" "\t," | sed -r -e 's/\)[ \t]+([A-Z]+)=\$2[^#]*#/=\1\t/' -e 's/\)[^#]*#/\t/' - else - grep -E "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" "$0" | sed 's/\|.*\$2[^#]*#/ /'| sed -E 's/\|.*\)[^#]*#/ /' - fi + sed -n -E "s|(^\s+-[A-Za-z])\).*#(.*)|\1 \2|p" "$0" cat <<EOF -FILES - AUTHOR - Written by Henrik Levkowetz, <henrik@levkowetz.com> + Written by: + Henrik Levkowetz, <henrik@levkowetz.com> + Lars Eggert, <lars@eggert.org> COPYRIGHT - - Copyright (c) 2015 IETF Trust and the persons identified as authors of - the code. All rights reserved. License 'Simplified BSD', as specified - in http://opensource.org/licenses/BSD-3-Clause. - + Copyright (c) 2016 IETF Trust and the persons identified as authors of + the code. All rights reserved. Redistribution and use in source and + binary forms, with or without modification, is permitted pursuant to, + and subject to the license terms contained in, the Revised BSD + License set forth in Section 4.c of the IETF Trust’s Legal Provisions + Relating to IETF Documents(https://trustee.ietf.org/license-info). EOF - } -# ---------------------------------------------------------------------- + function die() { echo -e "\n$program: error: $*" >&2 exit 1 } -# ---------------------------------------------------------------------- + function version() { echo -e "$program $version" } -# ---------------------------------------------------------------------- + trap 'echo "$program($LINENO): Command failed with error code $? ([$$] $0 $*)"; exit 1' ERR - -# ---------------------------------------------------------------------- -# Option parsing - -# Options -shortopts=hp:vVcC -longopts=help,port=,version,cached,no-cached - # Default values MYSQLDIR=$parent/data/mysql PORT=8000 REPO="ietf/datatracker-environment" CACHED=':cached' -if [ "$(uname)" = "Linux" ]; then - args=$(getopt -o "$shortopts" --long "$longopts" -n '$program' -- $SV "$@") - if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi - eval set -- "$args" -else - # Darwin, BSDs - args=$(getopt -o$shortopts $SV $*) - if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi - set -- $args -fi +# Option parsing +shortopts=hp:VcC + +args=$(getopt -o$shortopts $SV $*) +if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi +set -- $args while true ; do case "$1" in - -c| --cached) CACHED=':cached';; # Use cached disk access to reduce system load - -C| --no-cached) CACHED=':consistent';; # Use fully synchronized disk access - -h| --help) usage; exit;; # Show this help, then exit - -p| --port) PORT=$2; shift;; # Bind the container's port 8000 to external port PORT - -V| --version) version; exit;; # Show program version, then exit - --) shift; break;; - *) die "Internal error, inconsistent option specification: '$1'";; + -c) CACHED=':cached';; # Use cached disk access to reduce system load + -C) CACHED=':consistent';; # Use fully synchronized disk access + -h) usage; exit;; # Show this help, then exit + -p) PORT=$2; shift;; # Bind the container's port 8000 to external port PORT + -V) version; exit;; # Show program version, then exit + --) shift; break;; + *) die "Internal error, inconsistent option specification: '$1'";; esac shift done @@ -108,8 +89,6 @@ if [ -z "$TAG" ]; then TAG=$(basename "$(svn info "$parent" | grep ^URL | awk '{print $2}')") fi -# ---------------------------------------------------------------------- - echo "Starting a docker container for '$REPO:$TAG'." mkdir -p "$MYSQLDIR" docker run -ti -p "$PORT":8000 -p 33306:3306 \ diff --git a/docker/updatedb b/docker/updatedb index b1fda05b5..2fb9a7ed9 100755 --- a/docker/updatedb +++ b/docker/updatedb @@ -1,5 +1,4 @@ #!/bin/bash -# -*- indent-with-tabs: 1 -*- version=0.20 program=${0##*/} @@ -9,7 +8,7 @@ if [ "$progdir" = "." ]; then progdir="$PWD"; fi parent=$(dirname "$progdir") if [ "$parent" = "." ]; then parent="$PWD"; fi -# ---------------------------------------------------------------------- + function usage() { cat <<EOF NAME @@ -19,89 +18,68 @@ SYNOPSIS $program [OPTIONS] ARGS DESCRIPTION - This script downloads a dump of the IETF database and loads into the local sql server if it is newer than the current dump. +OPTIONS EOF - echo -e "OPTIONS" - if [ "$(uname)" = "Linux" ]; then - grep -E "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" "$0" | tr -s "\t|" "\t," | sed -r -e 's/\)[ \t]+([A-Z]+)=\$2[^#]*#/=\1\t/' -e 's/\)[^#]*#/\t/' - else - grep -E "^[ ]+[-][A-Za-z| -]+\*?\)[ ]+[A-Za-z].+#" "$0" | sed 's/\|.*\$2[^#]*#/ /'| sed -E 's/\|.*\)[^#]*#/ /' - fi + sed -n -E "s|(^\s+-[A-Za-z])\).*#(.*)|\1 \2|p" "$0" cat <<EOF -FILES - AUTHOR - Written by Henrik Levkowetz, <henrik@levkowetz.com> + Written by: + Henrik Levkowetz, <henrik@levkowetz.com> + Lars Eggert, <lars@eggert.org> COPYRIGHT - - Copyright (c) 2015 IETF Trust and the persons identified as authors of - the code. All rights reserved. License 'Simplified BSD', as specified - in http://opensource.org/licenses/BSD-3-Clause. - + Copyright (c) 2016 IETF Trust and the persons identified as authors of + the code. All rights reserved. Redistribution and use in source and + binary forms, with or without modification, is permitted pursuant to, + and subject to the license terms contained in, the Revised BSD + License set forth in Section 4.c of the IETF Trust’s Legal Provisions + Relating to IETF Documents(https://trustee.ietf.org/license-info). EOF - } -# ---------------------------------------------------------------------- + function die() { echo -e "\n$program: error: $*" >&2 exit 1 } -# ---------------------------------------------------------------------- + function version() { echo -e "$program $version" } -# ---------------------------------------------------------------------- + trap 'echo "$program($LINENO): Command failed with error code $? ([$$] $0 $*)"; exit 1' ERR - -# ---------------------------------------------------------------------- # Option parsing - -# Options shortopts=DLZhqvV -longopts=no-download,no-load,no-zap,help,version - LOAD=1 DOWNLOAD=1 DROP=1 -if [ "$(uname)" = "Linux" ]; then - args=$(getopt -o "$shortopts" --long "$longopts" -n "$program" -- $SV "$@") - if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi - eval set -- "$args" -else - # Darwin, BSDs - args=$(getopt -o$shortopts $SV $*) - if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi - set -- $args -fi +args=$(getopt -o$shortopts $SV $*) +if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi +set -- $args while true ; do case "$1" in - -D| --no-download) DOWNLOAD="";; #Don't download, use existing file - -L| --no-load) LOAD=""; ;; # Don't load the database - -Z| --no-zap) DROP="";; # Don't drop new tables - -h| --help) usage; exit;; # Show this help, then exit - -V| --version) version; exit;; # Show program version, then exit - --) shift; break;; - *) die "Internal error, inconsistent option specification: '$1'";; + -D) DOWNLOAD="";; # Don't download, use existing file + -L) LOAD=""; ;; # Don't load the database + -Z) DROP="";; # Don't drop new tables + -h) usage; exit;; # Show this help, then exit + -V) version; exit;; # Show program version, then exit + --) shift; break;; + *) die "Internal error, inconsistent option specification: '$1'";; esac shift done -# ---------------------------------------------------------------------- # The program itself - DATADIR=$parent/data - DUMP=ietf_utf8.sql.gz if [ "$DOWNLOAD" ]; then echo "Fetching database dump..." From d746348ea68fdd9e2315aad96eeccbef311d220e Mon Sep 17 00:00:00 2001 From: Lars Eggert <lars@eggert.org> Date: Wed, 13 Oct 2021 14:21:42 +0000 Subject: [PATCH 03/15] More explanations - Legacy-Id: 19419 --- docker/README.md | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/docker/README.md b/docker/README.md index 19d363058..fe5c9fff4 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,5 +1,7 @@ # Datatracker Development in Docker +## Getting started + 1. [Set up Docker](https://docs.docker.com/get-started/) on your preferred platform. @@ -12,11 +14,13 @@ svn co https://svn.ietf.org/svn/tools/ietfdb/trunk cd trunk -3. **TEMPORARY:** Replace the contents of the `docker` directory with Lars' - files. +3. **TEMPORARY:** Replace the contents of the `docker` directory with [Lars' + files](https://svn.ietf.org/svn/tools/ietfdb/personal/lars/7.39.1.dev0/docker/). -4. **TEMPORARY:** Until Lars' changes have been merged and a docker image is - available for download, you will need to build it locally: +4. **TEMPORARY:** Until [Lars' + changes](https://svn.ietf.org/svn/tools/ietfdb/personal/lars/7.39.1.dev0/docker/) + have been merged and a docker image is available for download, you will need + to build it locally: docker/build @@ -24,14 +28,29 @@ 5. Use the `docker/run` script to start the datatracker container. You will be dropped into a shell from which you can start the datatracker and execute - related commands as usual, for example: + related commands as usual, for example ietf/manage.py runserver 0.0.0.0:8000 + to start the datatracker. + + You can also pass additional arguments to `docker/run`, in which case they + will be executed in the container (instead of a shell being started.) + If you do not already have a copy of the IETF database available in the `data` directory, one will be downloaded and imported the first time you run `docker/run`. This will take some time. Once the datatracker has started, you should be able to open [http://localhost:8000](http://localhost:8000) in a browser and see the - landing page. \ No newline at end of file + landing page. + +## Troubleshooting + +- If the database fails to start, the cause is usually an incompatibility + between the database that last touched the files in `data/mysql` and the + database running inside the docker container. + + The solution is to blow away your existing database (`rm -rf data/mysql`). A + fresh copy will be retrieved and imported next time you do `docker/run`, which + should resolve this issue. \ No newline at end of file From a3f0b1533fee0b87d0f499d35ff6777bb4d066f3 Mon Sep 17 00:00:00 2001 From: Lars Eggert <lars@eggert.org> Date: Wed, 13 Oct 2021 14:22:19 +0000 Subject: [PATCH 04/15] Install some other packages that are not dependencies but make life easier - Legacy-Id: 19420 --- docker/Dockerfile | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index dc7710f4e..b082bdb8f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -5,12 +5,12 @@ # available. Do this in the top-level directory of your datatracker source # tree: # -# docker build --tag dt:latest . +# docker/build # # You can then execute the datatracker like this (also from the top-level # datatracker source directory): # -# docker run -ti -v $(pwd):/root/src dt:latest +# docker/run FROM ubuntu:hirsute LABEL maintainer="IETF Tools Team <tools-discuss@ietf.org>" @@ -21,9 +21,9 @@ EXPOSE 8000 # Default mysqld/mariadb port EXPOSE 3306 -# Install all dependencies that are available as packages ENV DEBIAN_FRONTEND=noninteractive RUN apt-get -y update && \ + # Install all dependencies that are available as packages apt-get -y install --no-install-recommends \ apache2-utils \ apt-file \ @@ -47,6 +47,13 @@ RUN apt-get -y update && \ rsyslog \ unzip \ yang-tools && \ + # Install some other packages that are not dependencies but make life easier + apt-get -y install --no-install-recommends \ + fish \ + less \ + nano \ + ripgrep \ + zsh && \ apt-get -y clean && \ rm -rf /var/lib/apt/lists/* From e0a739199cea9babd4cb5f093e3ca133c87946ee Mon Sep 17 00:00:00 2001 From: Lars Eggert <lars@eggert.org> Date: Thu, 14 Oct 2021 10:54:53 +0000 Subject: [PATCH 05/15] Various fixes from and inspired by Valery Smylov's testing - Legacy-Id: 19423 --- docker/build | 3 ++- docker/copydb | 3 ++- docker/rsync-extras | 3 ++- docker/run | 6 +++--- docker/updatedb | 5 +++-- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/docker/build b/docker/build index 29a903b92..5354fbda8 100755 --- a/docker/build +++ b/docker/build @@ -7,6 +7,7 @@ if [ "$progdir" = "$program" ]; then progdir="."; fi if [ "$progdir" = "." ]; then progdir="$PWD"; fi parent=$(dirname "$progdir") if [ "$parent" = "." ]; then parent="$PWD"; fi +if [[ $(uname) =~ CYGWIN.* ]]; then parent=$(echo "$parent" | sed -e 's/^\/cygdrive\/\(.\)/\1:/'); fi function usage() { @@ -60,7 +61,7 @@ trap 'echo "$program($LINENO): Command failed with error code $? ([$$] $0 $*)"; # Default values IMAGE=ietf/datatracker-environment -TAG=$(basename "$(svn info "$parent" | grep ^URL | awk '{print $2}')") +TAG=$(basename "$(svn info "$parent" | grep ^URL | awk '{print $2}') | tr -d '\r')") LOCAL=1 # Option parsing diff --git a/docker/copydb b/docker/copydb index 27ac3b395..da7d03955 100755 --- a/docker/copydb +++ b/docker/copydb @@ -7,6 +7,7 @@ if [ "$progdir" = "$program" ]; then progdir="."; fi if [ "$progdir" = "." ]; then progdir="$PWD"; fi parent=$(dirname "$progdir") if [ "$parent" = "." ]; then parent="$PWD"; fi +if [[ $(uname) =~ CYGWIN.* ]]; then parent=$(echo "$parent" | sed -e 's/^\/cygdrive\/\(.\)/\1:/'); fi function usage() { @@ -68,7 +69,7 @@ trap 'echo "$program($LINENO): Command failed with error code $? ([$$] $0 $*)"; # Option parsing shortopts=hV -args=$(getopt -o$shortopts $SV $*) +args=$(getopt -o$shortopts $*) if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi set -- $args diff --git a/docker/rsync-extras b/docker/rsync-extras index 5cc7f9624..5a00d2be7 100755 --- a/docker/rsync-extras +++ b/docker/rsync-extras @@ -7,6 +7,7 @@ if [ "$progdir" = "$program" ]; then progdir="."; fi if [ "$progdir" = "." ]; then progdir="$PWD"; fi parent=$(dirname "$progdir") if [ "$parent" = "." ]; then parent="$PWD"; fi +if [[ $(uname) =~ CYGWIN.* ]]; then parent=$(echo "$parent" | sed -e 's/^\/cygdrive\/\(.\)/\1:/'); fi function usage() { @@ -61,7 +62,7 @@ trap 'echo "$program($LINENO): Command failed with error code $? ([$$] $0 $*)"; # Option parsing shortopts=hvV -args=$(getopt -o$shortopts $SV $*) +args=$(getopt -o$shortopts $*) if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi set -- $args diff --git a/docker/run b/docker/run index 87ab5511d..93a53ac1e 100755 --- a/docker/run +++ b/docker/run @@ -7,6 +7,7 @@ if [ "$progdir" = "$program" ]; then progdir="."; fi if [ "$progdir" = "." ]; then progdir="$PWD"; fi parent=$(dirname "$progdir") if [ "$parent" = "." ]; then parent="$PWD"; fi +if [[ $(uname) =~ CYGWIN.* ]]; then parent=$(echo "$parent" | sed -e 's/^\/cygdrive\/\(.\)/\1:/'); fi function usage() { @@ -66,9 +67,8 @@ REPO="ietf/datatracker-environment" CACHED=':cached' # Option parsing -shortopts=hp:VcC - -args=$(getopt -o$shortopts $SV $*) +shortopts=cChp:V +args=$(getopt -o$shortopts $*) if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi set -- $args diff --git a/docker/updatedb b/docker/updatedb index 2fb9a7ed9..339c2911d 100755 --- a/docker/updatedb +++ b/docker/updatedb @@ -7,6 +7,7 @@ if [ "$progdir" = "$program" ]; then progdir="."; fi if [ "$progdir" = "." ]; then progdir="$PWD"; fi parent=$(dirname "$progdir") if [ "$parent" = "." ]; then parent="$PWD"; fi +if [[ $(uname) =~ CYGWIN.* ]]; then parent=$(echo "$parent" | sed -e 's/^\/cygdrive\/\(.\)/\1:/'); fi function usage() { @@ -56,12 +57,12 @@ function version() { trap 'echo "$program($LINENO): Command failed with error code $? ([$$] $0 $*)"; exit 1' ERR # Option parsing -shortopts=DLZhqvV +shortopts=DLZhV LOAD=1 DOWNLOAD=1 DROP=1 -args=$(getopt -o$shortopts $SV $*) +args=$(getopt -o$shortopts $*) if [ $? != 0 ] ; then die "Terminating..." >&2 ; exit 1 ; fi set -- $args From c46b0497b49bf9e4a51265b5a28c8039d7cbbe61 Mon Sep 17 00:00:00 2001 From: Lars Eggert <lars@eggert.org> Date: Thu, 14 Oct 2021 18:56:18 +0000 Subject: [PATCH 06/15] More fixes from Valery - Legacy-Id: 19425 --- docker/build | 2 +- docker/docker-init.sh | 10 +++++----- docker/run | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docker/build b/docker/build index 5354fbda8..658901717 100755 --- a/docker/build +++ b/docker/build @@ -61,7 +61,7 @@ trap 'echo "$program($LINENO): Command failed with error code $? ([$$] $0 $*)"; # Default values IMAGE=ietf/datatracker-environment -TAG=$(basename "$(svn info "$parent" | grep ^URL | awk '{print $2}') | tr -d '\r')") +TAG=$(basename "$(svn info "$parent" | grep ^URL | awk '{print $2}' | tr -d '\r')") LOCAL=1 # Option parsing diff --git a/docker/docker-init.sh b/docker/docker-init.sh index fdc0003a6..36cef7f40 100644 --- a/docker/docker-init.sh +++ b/docker/docker-init.sh @@ -22,6 +22,11 @@ if ! service mariadb status; then exit 1 fi +if [ ! -f /root/src/ietf/settings_local.py ]; then + echo "Setting up a default settings_local.py ..." + cp /root/src/docker/settings_local.py /root/src/ietf/settings_local.py +fi + if [ ! -d $MYSQLDIR/ietf_utf8 ]; then echo "WARNING: IETF database seems to be missing; populating it from dump." mysqladmin -u root --default-character-set=utf8 create ietf_utf8 @@ -67,11 +72,6 @@ for sub in \ fi done -if [ ! -f /root/src/ietf/settings_local.py ]; then - echo "Setting up a default settings_local.py ..." - cp /root/src/docker/settings_local.py /root/src/ietf/settings_local.py -fi - python -m smtpd -n -c DebuggingServer localhost:2025 & echo diff --git a/docker/run b/docker/run index 93a53ac1e..c0b4d2d3b 100755 --- a/docker/run +++ b/docker/run @@ -86,7 +86,7 @@ while true ; do done if [ -z "$TAG" ]; then - TAG=$(basename "$(svn info "$parent" | grep ^URL | awk '{print $2}')") + TAG=$(basename "$(svn info "$parent" | grep ^URL | awk '{print $2}' | tr -d '\r')") fi echo "Starting a docker container for '$REPO:$TAG'." From 2239ca2f3c881b40f22a710c0280d5fbae90580d Mon Sep 17 00:00:00 2001 From: Lars Eggert <lars@eggert.org> Date: Fri, 15 Oct 2021 13:56:44 +0000 Subject: [PATCH 07/15] MacOS X fix from Mark Donnelly - Legacy-Id: 19426 --- docker/build | 2 +- docker/copydb | 2 +- docker/rsync-extras | 2 +- docker/run | 2 +- docker/updatedb | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docker/build b/docker/build index 658901717..c84371092 100755 --- a/docker/build +++ b/docker/build @@ -26,7 +26,7 @@ DESCRIPTION OPTIONS EOF - sed -n -E "s|(^\s+-[A-Za-z])\).*#(.*)|\1 \2|p" "$0" + grep -E '^\s+-[a-zA-Z])' "$0" | sed -E -e 's/\)[^#]+#/ /' cat <<EOF AUTHOR diff --git a/docker/copydb b/docker/copydb index da7d03955..748189bd6 100755 --- a/docker/copydb +++ b/docker/copydb @@ -34,7 +34,7 @@ DESCRIPTION OPTIONS EOF - sed -n -E "s|(^\s+-[A-Za-z])\).*#(.*)|\1 \2|p" "$0" + grep -E '^\s+-[a-zA-Z])' "$0" | sed -E -e 's/\)[^#]+#/ /' cat <<EOF AUTHOR diff --git a/docker/rsync-extras b/docker/rsync-extras index 5a00d2be7..e81cf7928 100755 --- a/docker/rsync-extras +++ b/docker/rsync-extras @@ -27,7 +27,7 @@ DESCRIPTION OPTIONS EOF - sed -n -E "s|(^\s+-[A-Za-z])\).*#(.*)|\1 \2|p" "$0" + grep -E '^\s+-[a-zA-Z])' "$0" | sed -E -e 's/\)[^#]+#/ /' cat <<EOF AUTHOR diff --git a/docker/run b/docker/run index c0b4d2d3b..cc0719952 100755 --- a/docker/run +++ b/docker/run @@ -28,7 +28,7 @@ DESCRIPTION OPTIONS EOF - sed -n -E "s|(^\s+-[A-Za-z])\).*#(.*)|\1 \2|p" "$0" + grep -E '^\s+-[a-zA-Z])' "$0" | sed -E -e 's/\)[^#]+#/ /' cat <<EOF AUTHOR diff --git a/docker/updatedb b/docker/updatedb index 339c2911d..d6d548453 100755 --- a/docker/updatedb +++ b/docker/updatedb @@ -24,7 +24,7 @@ DESCRIPTION OPTIONS EOF - sed -n -E "s|(^\s+-[A-Za-z])\).*#(.*)|\1 \2|p" "$0" + grep -E '^\s+-[a-zA-Z])' "$0" | sed -E -e 's/\)[^#]+#/ /' cat <<EOF AUTHOR From 4d650bbba22ce4c71cbf15b2a67fb6330942e6b0 Mon Sep 17 00:00:00 2001 From: Lars Eggert <lars@eggert.org> Date: Mon, 18 Oct 2021 08:04:40 +0000 Subject: [PATCH 08/15] Install chromedriver in a way that works - Legacy-Id: 19431 --- docker/Dockerfile | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index b082bdb8f..da92655ad 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -28,12 +28,12 @@ RUN apt-get -y update && \ apache2-utils \ apt-file \ apt-utils \ - chromium-driver \ curl \ enscript \ gcc \ ghostscript \ git \ + gnupg \ graphviz \ libmagic-dev \ libmariadb-dev \ @@ -47,6 +47,17 @@ RUN apt-get -y update && \ rsyslog \ unzip \ yang-tools && \ + # Since snap doesn't work in Docker containers, install chromedriver per + # https://gist.github.com/varyonic/dea40abcf3dd891d204ef235c6e8dd79#gistcomment-3160722 + curl https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - && \ + echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list && \ + apt-get update -y && \ + apt-get install -y --no-install-recommends google-chrome-stable && \ + CHROMEVER=$(google-chrome --product-version | grep -o "[^\.]*\.[^\.]*\.[^\.]*") && \ + DRIVERVER=$(curl "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_$CHROMEVER") && \ + curl -L -O -C - "http://chromedriver.storage.googleapis.com/$DRIVERVER/chromedriver_linux64.zip" && \ + unzip chromedriver_linux64.zip -d /bin && \ + rm chromedriver_linux64.zip && \ # Install some other packages that are not dependencies but make life easier apt-get -y install --no-install-recommends \ fish \ @@ -54,6 +65,7 @@ RUN apt-get -y update && \ nano \ ripgrep \ zsh && \ + # Reduce image footprint (not that it matters given the size of the above) apt-get -y clean && \ rm -rf /var/lib/apt/lists/* From 715b0949cc391c4cae15946ebee16d87fe7e4d89 Mon Sep 17 00:00:00 2001 From: Lars Eggert <lars@eggert.org> Date: Mon, 18 Oct 2021 11:08:40 +0000 Subject: [PATCH 09/15] Fix things that selenium deprecated in version 4, and bump requirement - Legacy-Id: 19432 --- ietf/doc/tests_js.py | 28 ++--- ietf/group/tests_js.py | 18 +-- ietf/meeting/tests_js.py | 244 +++++++++++++++++++-------------------- ietf/utils/jstest.py | 21 ++-- requirements.txt | 2 +- 5 files changed, 159 insertions(+), 154 deletions(-) diff --git a/ietf/doc/tests_js.py b/ietf/doc/tests_js.py index 7f08b51c8..4fbdb3695 100644 --- a/ietf/doc/tests_js.py +++ b/ietf/doc/tests_js.py @@ -30,7 +30,7 @@ class EditAuthorsTests(IetfSeleniumTestCase): # To enter the person, type their name in the select2 search box, wait for the # search to offer the result, then press 'enter' to accept the result and close # the search input. - person_span = form_elt.find_element_by_class_name('select2-chosen') + person_span = form_elt.find_element(By.CLASS_NAME, 'select2-chosen') self.scroll_to_element(person_span) person_span.click() input = self.driver.switch_to.active_element @@ -46,16 +46,16 @@ class EditAuthorsTests(IetfSeleniumTestCase): # After the author is selected, the email select options will be populated. # Wait for that, then click on the option corresponding to the requested email. # This will only work if the email matches an address for the selected person. - email_select = form_elt.find_element_by_css_selector('select[name$="email"]') + email_select = form_elt.find_element(By.CSS_SELECTOR, 'select[name$="email"]') email_option = self.wait.until( presence_of_element_child_by_css_selector(email_select, 'option[value="{}"]'.format(email)) ) email_option.click() # select the email # Fill in the affiliation and country. Finally, simple text inputs! - affil_input = form_elt.find_element_by_css_selector('input[name$="affiliation"]') + affil_input = form_elt.find_element(By.CSS_SELECTOR, 'input[name$="affiliation"]') affil_input.send_keys(affiliation) - country_input = form_elt.find_element_by_css_selector('input[name$="country"]') + country_input = form_elt.find_element(By.CSS_SELECTOR, 'input[name$="country"]') country_input.send_keys(country) def _read_author_form(form_elt): @@ -63,10 +63,10 @@ class EditAuthorsTests(IetfSeleniumTestCase): Note: returns the Person instance named in the person field, not just their name. """ - hidden_person_input = form_elt.find_element_by_css_selector('input[type="text"][name$="person"]') - email_select = form_elt.find_element_by_css_selector('select[name$="email"]') - affil_input = form_elt.find_element_by_css_selector('input[name$="affiliation"]') - country_input = form_elt.find_element_by_css_selector('input[name$="country"]') + hidden_person_input = form_elt.find_element(By.CSS_SELECTOR, 'input[type="text"][name$="person"]') + email_select = form_elt.find_element(By.CSS_SELECTOR, 'select[name$="email"]') + affil_input = form_elt.find_element(By.CSS_SELECTOR, 'input[name$="affiliation"]') + country_input = form_elt.find_element(By.CSS_SELECTOR, 'input[name$="country"]') return ( Person.objects.get(pk=hidden_person_input.get_attribute('value')), email_select.get_attribute('value'), @@ -87,16 +87,16 @@ class EditAuthorsTests(IetfSeleniumTestCase): self.driver.get(url) # The draft has one author to start with. Find the list and check the count. - authors_list = self.driver.find_element_by_id('authors-list') - author_forms = authors_list.find_elements_by_class_name('author-panel') + authors_list = self.driver.find_element(By.ID, 'authors-list') + author_forms = authors_list.find_elements(By.CLASS_NAME, 'author-panel') self.assertEqual(len(author_forms), 1) # get the "add author" button so we can add blank author forms - add_author_button = self.driver.find_element_by_id('add-author-button') + add_author_button = self.driver.find_element(By.ID, 'add-author-button') for index, auth in enumerate(authors): self.scroll_to_element(add_author_button) # Can only click if it's in view! add_author_button.click() # Create a new form. Automatically scrolls to it. - author_forms = authors_list.find_elements_by_class_name('author-panel') + author_forms = authors_list.find_elements(By.CLASS_NAME, 'author-panel') authors_added = index + 1 self.assertEqual(len(author_forms), authors_added + 1) # Started with 1 author, hence +1 _fill_in_author_form(author_forms[index + 1], auth.name, str(auth.email()), orgs[index], countries[index]) @@ -114,9 +114,9 @@ class EditAuthorsTests(IetfSeleniumTestCase): ) # Must provide a "basis" (change reason) - self.driver.find_element_by_id('id_basis').send_keys('change testing') + self.driver.find_element(By.ID, 'id_basis').send_keys('change testing') # Now click the 'submit' button and check that the update was accepted. - submit_button = self.driver.find_element_by_css_selector('button[type="submit"]') + submit_button = self.driver.find_element(By.CSS_SELECTOR, 'button[type="submit"]') self.scroll_to_element(submit_button) submit_button.click() # Wait for redirect to the document_main view diff --git a/ietf/group/tests_js.py b/ietf/group/tests_js.py index f73121b08..b10bc5c34 100644 --- a/ietf/group/tests_js.py +++ b/ietf/group/tests_js.py @@ -34,7 +34,7 @@ class MilestoneTests(IetfSeleniumTestCase): (By.CSS_SELECTOR, result_selector), draft.name )) - results = self.driver.find_elements_by_css_selector(result_selector) + results = self.driver.find_elements(By.CSS_SELECTOR, result_selector) matching_results = [r for r in results if draft.name in r.text] self.assertEqual(len(matching_results), 1) return matching_results[0] @@ -61,7 +61,7 @@ class MilestoneTests(IetfSeleniumTestCase): except TimeoutException: found_expected_text = False self.assertTrue(found_expected_text, 'Milestone never marked as "changed"') - return self.driver.find_element_by_css_selector(milestone_selector) + return self.driver.find_element(By.CSS_SELECTOR, milestone_selector) def test_add_milestone(self): draft = WgDraftFactory() @@ -89,9 +89,9 @@ class MilestoneTests(IetfSeleniumTestCase): (By.CSS_SELECTOR, 'form#milestones-form div.edit-milestone') )) - desc_input = edit_div.find_element_by_css_selector('input[id$="_desc"]') - due_input = edit_div.find_element_by_css_selector('input[id$="_due"]') - draft_input = edit_div.find_element_by_css_selector( + desc_input = edit_div.find_element(By.CSS_SELECTOR, 'input[id$="_desc"]') + due_input = edit_div.find_element(By.CSS_SELECTOR, 'input[id$="_due"]') + draft_input = edit_div.find_element(By.CSS_SELECTOR, 'div.select2-container[id$="id_docs"] input.select2-input' ) @@ -149,9 +149,9 @@ class MilestoneTests(IetfSeleniumTestCase): # Get the prefix used to identify inputs related to this milestone prefix = desc_field.get_attribute('id')[:-4] # -4 to strip off 'desc', leave '-' - due_field = self.driver.find_element_by_id(prefix + 'due') - hidden_drafts_field = self.driver.find_element_by_id(prefix + 'docs') - draft_input = self.driver.find_element_by_css_selector( + due_field = self.driver.find_element(By.ID, prefix + 'due') + hidden_drafts_field = self.driver.find_element(By.ID, prefix + 'docs') + draft_input = self.driver.find_element(By.CSS_SELECTOR, 'div.select2-container[id*="%s"] input.select2-input' % prefix ) self.assertEqual(due_field.get_attribute('value'), milestone.due.strftime('%B %Y')) @@ -185,4 +185,4 @@ class MilestoneTests(IetfSeleniumTestCase): gms = self.group.groupmilestone_set.first() self.assertEqual(gms.desc, expected_desc) self.assertEqual(gms.due.strftime('%m %Y'), expected_due_date) - self.assertCountEqual(expected_docs, gms.docs.all()) + self.assertCountEqual(expected_docs, gms.docs.all()) \ No newline at end of file diff --git a/ietf/meeting/tests_js.py b/ietf/meeting/tests_js.py index 228fa21f7..39c2a3c56 100644 --- a/ietf/meeting/tests_js.py +++ b/ietf/meeting/tests_js.py @@ -110,34 +110,34 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): WebDriverWait(self.driver, 2).until(expected_conditions.presence_of_element_located((By.CSS_SELECTOR, '.edit-meeting-schedule'))) - self.assertEqual(len(self.driver.find_elements_by_css_selector('.session')), 3) + self.assertEqual(len(self.driver.find_elements(By.CSS_SELECTOR, '.session')), 3) # select - show session info - s2_element = self.driver.find_element_by_css_selector('#session{}'.format(s2.pk)) - s2b_element = self.driver.find_element_by_css_selector('#session{}'.format(s2b.pk)) + s2_element = self.driver.find_element(By.CSS_SELECTOR, '#session{}'.format(s2.pk)) + s2b_element = self.driver.find_element(By.CSS_SELECTOR, '#session{}'.format(s2b.pk)) self.assertNotIn('other-session-selected', s2b_element.get_attribute('class')) s2_element.click() # other session for group should be flagged for highlighting - s2b_element = self.driver.find_element_by_css_selector('#session{}'.format(s2b.pk)) + s2b_element = self.driver.find_element(By.CSS_SELECTOR, '#session{}'.format(s2b.pk)) self.assertIn('other-session-selected', s2b_element.get_attribute('class')) # other session for group should appear in the info panel - session_info_container = self.driver.find_element_by_css_selector('.session-info-container') - self.assertIn(s2.group.acronym, session_info_container.find_element_by_css_selector(".title").text) - self.assertEqual(session_info_container.find_element_by_css_selector(".other-session .time").text, "not yet scheduled") + session_info_container = self.driver.find_element(By.CSS_SELECTOR, '.session-info-container') + self.assertIn(s2.group.acronym, session_info_container.find_element(By.CSS_SELECTOR, ".title").text) + self.assertEqual(session_info_container.find_element(By.CSS_SELECTOR, ".other-session .time").text, "not yet scheduled") # deselect - self.driver.find_element_by_css_selector('.scheduling-panel').click() + self.driver.find_element(By.CSS_SELECTOR, '.scheduling-panel').click() - self.assertEqual(session_info_container.find_elements_by_css_selector(".title"), []) + self.assertEqual(session_info_container.find_elements(By.CSS_SELECTOR, ".title"), []) self.assertNotIn('other-session-selected', s2b_element.get_attribute('class')) # unschedule # we would like to do # - # unassigned_sessions_element = self.driver.find_element_by_css_selector('.unassigned-sessions') + # unassigned_sessions_element = self.driver.find_element(By.CSS_SELECTOR, '.unassigned-sessions') # ActionChains(self.driver).drag_and_drop(s2_element, unassigned_sessions_element).perform() # # but unfortunately, Selenium does not simulate drag and drop events, see @@ -158,20 +158,20 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): # sorting unassigned sorted_pks = [s.pk for s in sorted([s1, s2, s2b], key=lambda s: (s.group.acronym, s.requested_duration, s.pk))] - self.driver.find_element_by_css_selector('[name=sort_unassigned] option[value=name]').click() - self.assertTrue(self.driver.find_element_by_css_selector('.unassigned-sessions .drop-target #session{} + #session{} + #session{}'.format(*sorted_pks))) + self.driver.find_element(By.CSS_SELECTOR, '[name=sort_unassigned] option[value=name]').click() + self.assertTrue(self.driver.find_element(By.CSS_SELECTOR, '.unassigned-sessions .drop-target #session{} + #session{} + #session{}'.format(*sorted_pks))) sorted_pks = [s.pk for s in sorted([s1, s2, s2b], key=lambda s: (s.group.parent.acronym, s.group.acronym, s.requested_duration, s.pk))] - self.driver.find_element_by_css_selector('[name=sort_unassigned] option[value=parent]').click() - self.assertTrue(self.driver.find_element_by_css_selector('.unassigned-sessions .drop-target #session{} + #session{}'.format(*sorted_pks))) + self.driver.find_element(By.CSS_SELECTOR, '[name=sort_unassigned] option[value=parent]').click() + self.assertTrue(self.driver.find_element(By.CSS_SELECTOR, '.unassigned-sessions .drop-target #session{} + #session{}'.format(*sorted_pks))) sorted_pks = [s.pk for s in sorted([s1, s2, s2b], key=lambda s: (s.requested_duration, s.group.parent.acronym, s.group.acronym, s.pk))] - self.driver.find_element_by_css_selector('[name=sort_unassigned] option[value=duration]').click() - self.assertTrue(self.driver.find_element_by_css_selector('.unassigned-sessions .drop-target #session{} + #session{}'.format(*sorted_pks))) + self.driver.find_element(By.CSS_SELECTOR, '[name=sort_unassigned] option[value=duration]').click() + self.assertTrue(self.driver.find_element(By.CSS_SELECTOR, '.unassigned-sessions .drop-target #session{} + #session{}'.format(*sorted_pks))) sorted_pks = [s.pk for s in sorted([s1, s2, s2b], key=lambda s: (int(bool(s.comments)), s.group.parent.acronym, s.group.acronym, s.requested_duration, s.pk))] - self.driver.find_element_by_css_selector('[name=sort_unassigned] option[value=comments]').click() - self.assertTrue(self.driver.find_element_by_css_selector('.unassigned-sessions .drop-target #session{} + #session{}'.format(*sorted_pks))) + self.driver.find_element(By.CSS_SELECTOR, '[name=sort_unassigned] option[value=comments]').click() + self.assertTrue(self.driver.find_element(By.CSS_SELECTOR, '.unassigned-sessions .drop-target #session{} + #session{}'.format(*sorted_pks))) # schedule self.driver.execute_script("jQuery('#session{}').simulateDragDrop({{dropTarget: '#timeslot{} .drop-target'}});".format(s2.pk, slot1.pk)) @@ -182,30 +182,30 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): self.assertEqual(assignment.timeslot, slot1) # timeslot constraint hints when selected - s1_element = self.driver.find_element_by_css_selector('#session{}'.format(s1.pk)) + s1_element = self.driver.find_element(By.CSS_SELECTOR, '#session{}'.format(s1.pk)) s1_element.click() # violated due to constraints - both the timeslot and its timeslot label - self.assertTrue(self.driver.find_elements_by_css_selector('#timeslot{}.would-violate-hint'.format(slot1.pk))) + self.assertTrue(self.driver.find_elements(By.CSS_SELECTOR, '#timeslot{}.would-violate-hint'.format(slot1.pk))) # Find the timeslot label for slot1 - it's the first timeslot in the first room group - slot1_roomgroup_elt = self.driver.find_element_by_css_selector( + slot1_roomgroup_elt = self.driver.find_element(By.CSS_SELECTOR, '.day-flow .day:first-child .room-group:nth-child(2)' # count from 2 - first-child is the day label ) self.assertTrue( - slot1_roomgroup_elt.find_elements_by_css_selector( + slot1_roomgroup_elt.find_elements(By.CSS_SELECTOR, '.time-header > .time-label.would-violate-hint:first-child' ), 'Timeslot header label should show a would-violate hint for a constraint violation' ) # violated due to missing capacity - self.assertTrue(self.driver.find_elements_by_css_selector('#timeslot{}.would-violate-hint'.format(slot3.pk))) + self.assertTrue(self.driver.find_elements(By.CSS_SELECTOR, '#timeslot{}.would-violate-hint'.format(slot3.pk))) # Find the timeslot label for slot3 - it's the second timeslot in the second room group - slot3_roomgroup_elt = self.driver.find_element_by_css_selector( + slot3_roomgroup_elt = self.driver.find_element(By.CSS_SELECTOR, '.day-flow .day:first-child .room-group:nth-child(3)' # count from 2 - first-child is the day label ) self.assertFalse( - slot3_roomgroup_elt.find_elements_by_css_selector( + slot3_roomgroup_elt.find_elements(By.CSS_SELECTOR, '.time-header > .time-label.would-violate-hint:nth-child(2)' ), 'Timeslot header label should not show a would-violate hint for room capacity violation' @@ -220,15 +220,15 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): self.assertEqual(assignment.timeslot, slot2) # too many attendees warning - self.assertTrue(self.driver.find_elements_by_css_selector('#session{}.too-many-attendees'.format(s2.pk))) + self.assertTrue(self.driver.find_elements(By.CSS_SELECTOR, '#session{}.too-many-attendees'.format(s2.pk))) # overfull timeslot - self.assertTrue(self.driver.find_elements_by_css_selector('#timeslot{}.overfull'.format(slot2.pk))) + self.assertTrue(self.driver.find_elements(By.CSS_SELECTOR, '#timeslot{}.overfull'.format(slot2.pk))) # constraint hints s1_element.click() self.assertIn('would-violate-hint', s2_element.get_attribute('class')) - constraint_element = s2_element.find_element_by_css_selector(".constraints span[data-sessions=\"{}\"].would-violate-hint".format(s1.pk)) + constraint_element = s2_element.find_element(By.CSS_SELECTOR, ".constraints span[data-sessions=\"{}\"].would-violate-hint".format(s1.pk)) self.assertTrue(constraint_element.is_displayed()) # current constraint violations @@ -236,12 +236,12 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): WebDriverWait(self.driver, 2).until(expected_conditions.presence_of_element_located((By.CSS_SELECTOR, '#timeslot{} #session{}'.format(slot1.pk, s1.pk)))) - constraint_element = s2_element.find_element_by_css_selector(".constraints span[data-sessions=\"{}\"].violated-hint".format(s1.pk)) + constraint_element = s2_element.find_element(By.CSS_SELECTOR, ".constraints span[data-sessions=\"{}\"].violated-hint".format(s1.pk)) self.assertTrue(constraint_element.is_displayed()) # hide sessions in area self.assertTrue(s1_element.is_displayed()) - self.driver.find_element_by_css_selector(".session-parent-toggles [value=\"{}\"]".format(s1.group.parent.acronym)).click() + self.driver.find_element(By.CSS_SELECTOR, ".session-parent-toggles [value=\"{}\"]".format(s1.group.parent.acronym)).click() self.assertTrue(s1_element.is_displayed()) # should still be displayed self.assertIn('hidden-parent', s1_element.get_attribute('class'), 'Session should be hidden when parent disabled') @@ -249,7 +249,7 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): self.assertNotIn('selected', s1_element.get_attribute('class'), 'Session should not be selectable when parent disabled') - self.driver.find_element_by_css_selector(".session-parent-toggles [value=\"{}\"]".format(s1.group.parent.acronym)).click() + self.driver.find_element(By.CSS_SELECTOR, ".session-parent-toggles [value=\"{}\"]".format(s1.group.parent.acronym)).click() self.assertTrue(s1_element.is_displayed()) self.assertNotIn('hidden-parent', s1_element.get_attribute('class'), 'Session should not be hidden when parent enabled') @@ -258,32 +258,32 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): 'Session should be selectable when parent enabled') # hide timeslots - self.driver.find_element_by_css_selector(".timeslot-group-toggles button").click() - self.assertTrue(self.driver.find_element_by_css_selector("#timeslot-group-toggles-modal").is_displayed()) - self.driver.find_element_by_css_selector("#timeslot-group-toggles-modal [value=\"{}\"]".format("ts-group-{}-{}".format(slot2.time.strftime("%Y%m%d-%H%M"), int(slot2.duration.total_seconds() / 60)))).click() - self.driver.find_element_by_css_selector("#timeslot-group-toggles-modal [data-dismiss=\"modal\"]").click() - self.assertTrue(not self.driver.find_element_by_css_selector("#timeslot-group-toggles-modal").is_displayed()) + self.driver.find_element(By.CSS_SELECTOR, ".timeslot-group-toggles button").click() + self.assertTrue(self.driver.find_element(By.CSS_SELECTOR, "#timeslot-group-toggles-modal").is_displayed()) + self.driver.find_element(By.CSS_SELECTOR, "#timeslot-group-toggles-modal [value=\"{}\"]".format("ts-group-{}-{}".format(slot2.time.strftime("%Y%m%d-%H%M"), int(slot2.duration.total_seconds() / 60)))).click() + self.driver.find_element(By.CSS_SELECTOR, "#timeslot-group-toggles-modal [data-dismiss=\"modal\"]").click() + self.assertTrue(not self.driver.find_element(By.CSS_SELECTOR, "#timeslot-group-toggles-modal").is_displayed()) # swap days - self.driver.find_element_by_css_selector(".day .swap-days[data-dayid=\"{}\"]".format(slot4.time.date().isoformat())).click() - self.assertTrue(self.driver.find_element_by_css_selector("#swap-days-modal").is_displayed()) - self.driver.find_element_by_css_selector("#swap-days-modal input[name=\"target_day\"][value=\"{}\"]".format(slot1.time.date().isoformat())).click() - self.driver.find_element_by_css_selector("#swap-days-modal button[type=\"submit\"]").click() + self.driver.find_element(By.CSS_SELECTOR, ".day .swap-days[data-dayid=\"{}\"]".format(slot4.time.date().isoformat())).click() + self.assertTrue(self.driver.find_element(By.CSS_SELECTOR, "#swap-days-modal").is_displayed()) + self.driver.find_element(By.CSS_SELECTOR, "#swap-days-modal input[name=\"target_day\"][value=\"{}\"]".format(slot1.time.date().isoformat())).click() + self.driver.find_element(By.CSS_SELECTOR, "#swap-days-modal button[type=\"submit\"]").click() - self.assertTrue(self.driver.find_elements_by_css_selector('#timeslot{} #session{}'.format(slot4.pk, s1.pk)), + self.assertTrue(self.driver.find_elements(By.CSS_SELECTOR, '#timeslot{} #session{}'.format(slot4.pk, s1.pk)), 'Session s1 should have moved to second meeting day') # swap timeslot column - put session in a differently-timed timeslot - self.driver.find_element_by_css_selector( + self.driver.find_element(By.CSS_SELECTOR, '.day .swap-timeslot-col[data-timeslot-pk="{}"]'.format(slot1b.pk) ).click() # open modal on the second timeslot for room1 - self.assertTrue(self.driver.find_element_by_css_selector("#swap-timeslot-col-modal").is_displayed()) - self.driver.find_element_by_css_selector( + self.assertTrue(self.driver.find_element(By.CSS_SELECTOR, "#swap-timeslot-col-modal").is_displayed()) + self.driver.find_element(By.CSS_SELECTOR, '#swap-timeslot-col-modal input[name="target_timeslot"][value="{}"]'.format(slot4.pk) ).click() # select room1 timeslot that has a session in it - self.driver.find_element_by_css_selector('#swap-timeslot-col-modal button[type="submit"]').click() + self.driver.find_element(By.CSS_SELECTOR, '#swap-timeslot-col-modal button[type="submit"]').click() - self.assertTrue(self.driver.find_elements_by_css_selector('#timeslot{} #session{}'.format(slot1b.pk, s1.pk)), + self.assertTrue(self.driver.find_elements(By.CSS_SELECTOR, '#timeslot{} #session{}'.format(slot1b.pk, s1.pk)), 'Session s1 should have moved to second timeslot on first meeting day') def test_past_flags(self): @@ -351,19 +351,19 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): self.login(username=meeting.schedule.owner.user.username) self.driver.get(url) - past_flags = self.driver.find_elements_by_css_selector( + past_flags = self.driver.find_elements(By.CSS_SELECTOR, ','.join('#timeslot{} .past-flag'.format(ts.pk) for ts in past_timeslots) ) self.assertGreaterEqual(len(past_flags), len(past_timeslots) + len(past_sessions), 'Expected at least one flag for each past timeslot and session') - now_flags = self.driver.find_elements_by_css_selector( + now_flags = self.driver.find_elements(By.CSS_SELECTOR, ','.join('#timeslot{} .past-flag'.format(ts.pk) for ts in now_timeslots) ) self.assertGreaterEqual(len(now_flags), len(now_timeslots) + len(now_sessions), 'Expected at least one flag for each "now" timeslot and session') - future_flags = self.driver.find_elements_by_css_selector( + future_flags = self.driver.find_elements(By.CSS_SELECTOR, ','.join('#timeslot{} .past-flag'.format(ts.pk) for ts in future_timeslots) ) self.assertGreaterEqual(len(future_flags), len(future_timeslots) + len(future_sessions), @@ -417,21 +417,21 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): self.login(username=meeting.schedule.owner.user.username) self.driver.get(url) - past_swap_days_buttons = self.driver.find_elements_by_css_selector( + past_swap_days_buttons = self.driver.find_elements(By.CSS_SELECTOR, ','.join( '.swap-days[data-start="{}"]'.format(ts.time.date().isoformat()) for ts in past_timeslots ) ) self.assertEqual(len(past_swap_days_buttons), len(past_timeslots), 'Missing past swap days buttons') - future_swap_days_buttons = self.driver.find_elements_by_css_selector( + future_swap_days_buttons = self.driver.find_elements(By.CSS_SELECTOR, ','.join( '.swap-days[data-start="{}"]'.format(ts.time.date().isoformat()) for ts in future_timeslots ) ) self.assertEqual(len(future_swap_days_buttons), len(future_timeslots), 'Missing future swap days buttons') - now_swap_days_buttons = self.driver.find_elements_by_css_selector( + now_swap_days_buttons = self.driver.find_elements(By.CSS_SELECTOR, ','.join( '.swap-days[data-start="{}"]'.format(ts.time.date().isoformat()) for ts in now_timeslots ) @@ -475,7 +475,7 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): self.fail('Modal never appeared') self.assertFalse( any(radio.is_enabled() - for radio in modal.find_elements_by_css_selector(','.join( + for radio in modal.find_elements(By.CSS_SELECTOR, ','.join( 'input[name="target_day"][value="{}"]'.format(ts.time.date().isoformat()) for ts in past_timeslots) )), 'Past day is enabled in swap-days modal for official schedule', @@ -484,14 +484,14 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): enabled_timeslots = (ts for ts in future_timeslots if ts != future_timeslots[clicked_index]) self.assertTrue( all(radio.is_enabled() - for radio in modal.find_elements_by_css_selector(','.join( + for radio in modal.find_elements(By.CSS_SELECTOR, ','.join( 'input[name="target_day"][value="{}"]'.format(ts.time.date().isoformat()) for ts in enabled_timeslots) )), 'Future day is not enabled in swap-days modal for official schedule', ) self.assertFalse( any(radio.is_enabled() - for radio in modal.find_elements_by_css_selector(','.join( + for radio in modal.find_elements(By.CSS_SELECTOR, ','.join( 'input[name="target_day"][value="{}"]'.format(ts.time.date().isoformat()) for ts in now_timeslots) )), '"Now" day is enabled in swap-days modal for official schedule', @@ -533,21 +533,21 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): self.login(username=meeting.schedule.owner.user.username) self.driver.get(url) - past_swap_ts_buttons = self.driver.find_elements_by_css_selector( + past_swap_ts_buttons = self.driver.find_elements(By.CSS_SELECTOR, ','.join( '.swap-timeslot-col[data-start="{}"]'.format(ts.utc_start_time().isoformat()) for ts in past_timeslots ) ) self.assertEqual(len(past_swap_ts_buttons), len(past_timeslots), 'Missing past swap timeslot col buttons') - future_swap_ts_buttons = self.driver.find_elements_by_css_selector( + future_swap_ts_buttons = self.driver.find_elements(By.CSS_SELECTOR, ','.join( '.swap-timeslot-col[data-start="{}"]'.format(ts.utc_start_time().isoformat()) for ts in future_timeslots ) ) self.assertEqual(len(future_swap_ts_buttons), len(future_timeslots), 'Missing future swap timeslot col buttons') - now_swap_ts_buttons = self.driver.find_elements_by_css_selector( + now_swap_ts_buttons = self.driver.find_elements(By.CSS_SELECTOR, ','.join( '.swap-timeslot-col[data-start="{}"]'.format(ts.utc_start_time().isoformat()) for ts in now_timeslots ) @@ -590,7 +590,7 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): self.fail('Modal never appeared') self.assertFalse( any(radio.is_enabled() - for radio in modal.find_elements_by_css_selector(','.join( + for radio in modal.find_elements(By.CSS_SELECTOR, ','.join( 'input[name="target_timeslot"][value="{}"]'.format(ts.pk) for ts in past_timeslots) )), 'Past timeslot is enabled in swap-timeslot-col modal for official schedule', @@ -599,14 +599,14 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): enabled_timeslots = (ts for ts in future_timeslots if ts != future_timeslots[clicked_index]) self.assertTrue( all(radio.is_enabled() - for radio in modal.find_elements_by_css_selector(','.join( + for radio in modal.find_elements(By.CSS_SELECTOR, ','.join( 'input[name="target_timeslot"][value="{}"]'.format(ts.pk) for ts in enabled_timeslots) )), 'Future timeslot is not enabled in swap-timeslot-col modal for official schedule', ) self.assertFalse( any(radio.is_enabled() - for radio in modal.find_elements_by_css_selector(','.join( + for radio in modal.find_elements(By.CSS_SELECTOR, ','.join( 'input[name="target_timeslot"][value="{}"]'.format(ts.pk) for ts in now_timeslots) )), '"Now" timeslot is enabled in swap-timeslot-col modal for official schedule', @@ -625,7 +625,7 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): def sort_by_position(driver, sessions): """Helper to sort sessions by the position of their session element in the unscheduled box""" def _sort_key(sess): - elt = driver.find_element_by_id('session{}'.format(sess.pk)) + elt = driver.find_element(By.ID, 'session{}'.format(sess.pk)) return (elt.location['y'], elt.location['x']) return sorted(sessions, key=_sort_key) @@ -687,10 +687,10 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): self.login('secretary') self.driver.get(url) - select = self.driver.find_element_by_name('sort_unassigned') + select = self.driver.find_element(By.NAME, 'sort_unassigned') options = { opt.get_attribute('value'): opt - for opt in select.find_elements_by_tag_name('option') + for opt in select.find_elements(By.TAG_NAME, 'option') } # check sorting by name @@ -782,10 +782,10 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): self.driver.get(url) # Check that the drop target for unassigned sessions is actually empty - drop_target = self.driver.find_element_by_css_selector( + drop_target = self.driver.find_element(By.CSS_SELECTOR, '.unassigned-sessions .drop-target' ) - self.assertEqual(len(drop_target.find_elements_by_class_name('session')), 0, + self.assertEqual(len(drop_target.find_elements(By.CLASS_NAME, 'session')), 0, 'Unassigned sessions box is not empty, test is broken') # Check that the drop target has non-zero size @@ -829,7 +829,7 @@ class EditMeetingScheduleTests(IetfSeleniumTestCase): kwargs=dict(num=meeting.number, owner=schedule.owner.email(), name=schedule.name)) self.login(schedule.owner.user.username) self.driver.get(url) - session_elements = [self.driver.find_element_by_css_selector(f'#session{sess.pk}') for sess in sessions] + session_elements = [self.driver.find_element(By.CSS_SELECTOR, f'#session{sess.pk}') for sess in sessions] session_elements[0].click() # All conflicting sessions should be flagged with the would-violate-hint class. @@ -864,20 +864,20 @@ class ScheduleEditTests(IetfSeleniumTestCase): # driver.get() will wait for scripts to finish, but not ajax # requests. Wait for completion of the permissions check: - read_only_note = self.driver.find_element_by_id('read_only') + read_only_note = self.driver.find_element(By.ID, 'read_only') WebDriverWait(self.driver, 10).until(expected_conditions.invisibility_of_element(read_only_note), "Read-only schedule") s1 = Session.objects.filter(group__acronym='mars', meeting=meeting).first() selector = "#session_{}".format(s1.pk) WebDriverWait(self.driver, 30).until(expected_conditions.presence_of_element_located((By.CSS_SELECTOR, selector)), "Did not find %s"%selector) - self.assertEqual(self.driver.find_elements_by_css_selector("#sortable-list #session_{}".format(s1.pk)), []) + self.assertEqual(self.driver.find_elements(By.CSS_SELECTOR, "#sortable-list #session_{}".format(s1.pk)), []) - element = self.driver.find_element_by_id('session_{}'.format(s1.pk)) - target = self.driver.find_element_by_id('sortable-list') + element = self.driver.find_element(By.ID, 'session_{}'.format(s1.pk)) + target = self.driver.find_element(By.ID, 'sortable-list') ActionChains(self.driver).drag_and_drop(element,target).perform() - self.assertTrue(self.driver.find_elements_by_css_selector("#sortable-list #session_{}".format(s1.pk))) + self.assertTrue(self.driver.find_elements(By.CSS_SELECTOR, "#sortable-list #session_{}".format(s1.pk))) time.sleep(0.1) # The API that modifies the database runs async @@ -905,8 +905,8 @@ class SlideReorderTests(IetfSeleniumTestCase): self.secr_login() self.driver.get(url) #debug.show('unicode(self.driver.page_source)') - second = self.driver.find_element_by_css_selector('#slides tr:nth-child(2)') - third = self.driver.find_element_by_css_selector('#slides tr:nth-child(3)') + second = self.driver.find_element(By.CSS_SELECTOR, '#slides tr:nth-child(2)') + third = self.driver.find_element(By.CSS_SELECTOR, '#slides tr:nth-child(3)') ActionChains(self.driver).drag_and_drop(second,third).perform() time.sleep(0.1) # The API that modifies the database runs async @@ -1003,7 +1003,7 @@ class AgendaTests(IetfSeleniumTestCase): self.driver.get(self.absreverse('ietf.meeting.views.agenda') + querystring) self.assert_agenda_item_visibility(visible_groups) self.assert_agenda_view_filter_matches_ics_filter(querystring) - weekview_iframe = self.driver.find_element_by_id('weekview') + weekview_iframe = self.driver.find_element(By.ID, 'weekview') if len(querystring) == 0: self.assertFalse(weekview_iframe.is_displayed(), 'Weekview should be hidden when filters off') else: @@ -1184,7 +1184,7 @@ class AgendaTests(IetfSeleniumTestCase): for item in self.get_expected_items(): row_id = self.row_id_for_item(item) try: - item_row = self.driver.find_element_by_id(row_id) + item_row = self.driver.find_element(By.ID, row_id) except NoSuchElementException: item_row = None self.assertIsNotNone(item_row, 'No row for schedule item "%s"' % row_id) @@ -1205,7 +1205,7 @@ class AgendaTests(IetfSeleniumTestCase): label = 'Free Slot' try: - item_div = self.driver.find_element_by_xpath('//div/span[contains(text(),"%s")]/..' % label) + item_div = self.driver.find_element(By.XPATH, '//div/span[contains(text(),"%s")]/..' % label) except NoSuchElementException: item_div = None @@ -1479,7 +1479,7 @@ class AgendaTests(IetfSeleniumTestCase): ics_url = self.absreverse('ietf.meeting.views.agenda_ical') # parse out the events - agenda_rows = self.driver.find_elements_by_css_selector('[id^="row-"]') + agenda_rows = self.driver.find_elements(By.CSS_SELECTOR, '[id^="row-"]') visible_rows = [r for r in agenda_rows if r.is_displayed()] sessions = [self.session_from_agenda_row_id(row.get_attribute("id")) for row in visible_rows] @@ -1509,7 +1509,7 @@ class AgendaTests(IetfSeleniumTestCase): self.driver.get(url) # modal should start hidden - modal_div = self.driver.find_element_by_css_selector('div#modal-%s' % slug) + modal_div = self.driver.find_element(By.CSS_SELECTOR, 'div#modal-%s' % slug) self.assertFalse(modal_div.is_displayed()) # Click the 'materials' button @@ -1534,7 +1534,7 @@ class AgendaTests(IetfSeleniumTestCase): ) self.assertGreater(not_deleted_slides.count(), 0) # make sure this isn't a pointless test for slide in not_deleted_slides: - anchor = self.driver.find_element_by_xpath('//a[text()="%s"]' % slide.title) + anchor = self.driver.find_element(By.XPATH, '//a[text()="%s"]' % slide.title) self.assertIsNotNone(anchor) deleted_slides = session.materials.filter( @@ -1543,7 +1543,7 @@ class AgendaTests(IetfSeleniumTestCase): self.assertGreater(deleted_slides.count(), 0) # make sure this isn't a pointless test for slide in deleted_slides: with self.assertRaises(NoSuchElementException): - self.driver.find_element_by_xpath('//a[text()="%s"]' % slide.title) + self.driver.find_element(By.XPATH, '//a[text()="%s"]' % slide.title) # Now close the modal close_modal_button = WebDriverWait(self.driver, 2).until( @@ -1588,7 +1588,7 @@ class AgendaTests(IetfSeleniumTestCase): self.assertNotIn(newly_deleted_slide, not_deleted_slides) self.assertIn(newly_undeleted_slide, not_deleted_slides) for slide in not_deleted_slides: - anchor = self.driver.find_element_by_xpath('//a[text()="%s"]' % slide.title) + anchor = self.driver.find_element(By.XPATH, '//a[text()="%s"]' % slide.title) self.assertIsNotNone(anchor) deleted_slides = session.materials.filter( @@ -1598,7 +1598,7 @@ class AgendaTests(IetfSeleniumTestCase): self.assertNotIn(newly_undeleted_slide, deleted_slides) for slide in deleted_slides: with self.assertRaises(NoSuchElementException): - self.driver.find_element_by_xpath('//a[text()="%s"]' % slide.title) + self.driver.find_element(By.XPATH, '//a[text()="%s"]' % slide.title) def test_agenda_time_zone_selection(self): self.assertNotEqual(self.meeting.time_zone, 'UTC', 'Meeting time zone must not be UTC') @@ -1615,14 +1615,14 @@ class AgendaTests(IetfSeleniumTestCase): ) ) - tz_select_input = self.driver.find_element_by_id('timezone-select') - meeting_tz_link = self.driver.find_element_by_id('meeting-timezone') - local_tz_link = self.driver.find_element_by_id('local-timezone') - utc_tz_link = self.driver.find_element_by_id('utc-timezone') - tz_displays = self.driver.find_elements_by_css_selector('.current-tz') + tz_select_input = self.driver.find_element(By.ID, 'timezone-select') + meeting_tz_link = self.driver.find_element(By.ID, 'meeting-timezone') + local_tz_link = self.driver.find_element(By.ID, 'local-timezone') + utc_tz_link = self.driver.find_element(By.ID, 'utc-timezone') + tz_displays = self.driver.find_elements(By.CSS_SELECTOR, '.current-tz') self.assertGreaterEqual(len(tz_displays), 1) # we'll check that all current-tz elements are updated, but first check that at least one is in the nav sidebar - self.assertIsNotNone(self.driver.find_element_by_css_selector('.nav .current-tz')) + self.assertIsNotNone(self.driver.find_element(By.CSS_SELECTOR, '.nav .current-tz')) # Moment.js guesses local time zone based on the behavior of Selenium's web client. This seems # to inherit Django's settings.TIME_ZONE but I don't know whether that's guaranteed to be consistent. @@ -1631,9 +1631,9 @@ class AgendaTests(IetfSeleniumTestCase): self.assertNotEqual(self.meeting.time_zone, local_tz, 'Meeting time zone must not be local time zone') self.assertNotEqual(local_tz, 'UTC', 'Local time zone must not be UTC') - meeting_tz_opt = tz_select_input.find_element_by_css_selector('option[value="%s"]' % self.meeting.time_zone) - local_tz_opt = tz_select_input.find_element_by_css_selector('option[value="%s"]' % local_tz) - utc_tz_opt = tz_select_input.find_element_by_css_selector('option[value="UTC"]') + meeting_tz_opt = tz_select_input.find_element(By.CSS_SELECTOR, 'option[value="%s"]' % self.meeting.time_zone) + local_tz_opt = tz_select_input.find_element(By.CSS_SELECTOR, 'option[value="%s"]' % local_tz) + utc_tz_opt = tz_select_input.find_element(By.CSS_SELECTOR, 'option[value="UTC"]') # Should start off in meeting time zone self.assertTrue(meeting_tz_opt.is_selected()) @@ -1742,20 +1742,20 @@ class AgendaTests(IetfSeleniumTestCase): # Verify that elements are all updated when the filters change. That the correct elements # have the appropriate classes is a separate test. - elements_to_check = self.driver.find_elements_by_css_selector('.agenda-link.filterable') + elements_to_check = self.driver.find_elements(By.CSS_SELECTOR, '.agenda-link.filterable') self.assertGreater(len(elements_to_check), 0, 'No elements with agenda links to update were found') self.assertFalse( any(checkbox.is_selected() - for checkbox in self.driver.find_elements_by_css_selector( + for checkbox in self.driver.find_elements(By.CSS_SELECTOR, 'input.checkbox[name="selected-sessions"]')), 'Sessions were selected before being clicked', ) - mars_checkbox = self.driver.find_element_by_css_selector('input[type="checkbox"][name="selected-sessions"][data-filter-item="mars"]') - break_checkbox = self.driver.find_element_by_css_selector('input[type="checkbox"][name="selected-sessions"][data-filter-item="secretariat-sessb"]') - registration_checkbox = self.driver.find_element_by_css_selector('input[type="checkbox"][name="selected-sessions"][data-filter-item="secretariat-sessa"]') - secretariat_button = self.driver.find_element_by_css_selector('button[data-filter-item="secretariat"]') + mars_checkbox = self.driver.find_element(By.CSS_SELECTOR, 'input[type="checkbox"][name="selected-sessions"][data-filter-item="mars"]') + break_checkbox = self.driver.find_element(By.CSS_SELECTOR, 'input[type="checkbox"][name="selected-sessions"][data-filter-item="secretariat-sessb"]') + registration_checkbox = self.driver.find_element(By.CSS_SELECTOR, 'input[type="checkbox"][name="selected-sessions"][data-filter-item="secretariat-sessa"]') + secretariat_button = self.driver.find_element(By.CSS_SELECTOR, 'button[data-filter-item="secretariat"]') mars_checkbox.click() # select mars session try: @@ -1868,9 +1868,9 @@ class WeekviewTests(IetfSeleniumTestCase): def _assert_wrapped(displayed, expected_time_string): self.assertEqual(len(displayed), 2) first = displayed[0] - first_parent = first.find_element_by_xpath('..') + first_parent = first.find_element(By.XPATH, '..') second = displayed[1] - second_parent = second.find_element_by_xpath('..') + second_parent = second.find_element(By.XPATH, '..') self.assertNotIn('continued', first.text) self.assertIn(expected_time_string, first_parent.text) self.assertIn('continued', second.text) @@ -1879,7 +1879,7 @@ class WeekviewTests(IetfSeleniumTestCase): def _assert_not_wrapped(displayed, expected_time_string): self.assertEqual(len(displayed), 1) first = displayed[0] - first_parent = first.find_element_by_xpath('..') + first_parent = first.find_element(By.XPATH, '..') self.assertNotIn('continued', first.text) self.assertIn(expected_time_string, first_parent.text) @@ -2053,7 +2053,7 @@ class InterimTests(IetfSeleniumTestCase): return meetings def find_upcoming_meeting_entries(self): - return self.driver.find_elements_by_css_selector( + return self.driver.find_elements(By.CSS_SELECTOR, 'table#upcoming-meeting-table a.ietf-meeting-link, table#upcoming-meeting-table a.interim-meeting-link' ) @@ -2103,7 +2103,7 @@ class InterimTests(IetfSeleniumTestCase): # 12 in order to check the starting month of the following year, which # will usually contain the day 1 year from the start date. for _ in range(13): - entries = self.driver.find_elements_by_css_selector( + entries = self.driver.find_elements(By.CSS_SELECTOR, 'div#calendar div.fc-content' ) for entry in entries: @@ -2134,9 +2134,9 @@ class InterimTests(IetfSeleniumTestCase): if simplified_querystring in ['?show=', '?hide=', '?show=&hide=']: simplified_querystring = '' # these empty querystrings will be dropped (not an exhaustive list) - ics_link = self.driver.find_element_by_link_text('Download as .ics') + ics_link = self.driver.find_element(By.LINK_TEXT, 'Download as .ics') self.assertIn(simplified_querystring, ics_link.get_attribute('href')) - webcal_link = self.driver.find_element_by_link_text('Subscribe with webcal') + webcal_link = self.driver.find_element(By.LINK_TEXT, 'Subscribe with webcal') self.assertIn(simplified_querystring, webcal_link.get_attribute('href')) def assert_upcoming_view_filter_matches_ics_filter(self, filter_string): @@ -2298,8 +2298,8 @@ class InterimTests(IetfSeleniumTestCase): ts = session.official_timeslotassignment().timeslot start = ts.utc_start_time().astimezone(zone).strftime('%Y-%m-%d %H:%M') end = ts.utc_end_time().astimezone(zone).strftime('%H:%M') - meeting_link = self.driver.find_element_by_link_text(session.meeting.number) - time_td = meeting_link.find_element_by_xpath('../../td[@class="session-time"]') + meeting_link = self.driver.find_element(By.LINK_TEXT, session.meeting.number) + time_td = meeting_link.find_element(By.XPATH, '../../td[@class="session-time"]') self.assertIn('%s - %s' % (start, end), time_td.text) def _assert_ietf_tz_correct(meetings, tz): @@ -2317,8 +2317,8 @@ class InterimTests(IetfSeleniumTestCase): start = start_dt.astimezone(zone).strftime('%Y-%m-%d') end = end_dt.astimezone(zone).strftime('%Y-%m-%d') - meeting_link = self.driver.find_element_by_link_text("IETF " + meeting.number) - time_td = meeting_link.find_element_by_xpath('../../td[@class="meeting-time"]') + meeting_link = self.driver.find_element(By.LINK_TEXT, "IETF " + meeting.number) + time_td = meeting_link.find_element(By.XPATH, '../../td[@class="meeting-time"]') self.assertIn('%s - %s' % (start, end), time_td.text) sessions = [m.session_set.first() for m in self.displayed_interims()] @@ -2327,12 +2327,12 @@ class InterimTests(IetfSeleniumTestCase): self.assertGreater(len(ietf_meetings), 0) self.driver.get(self.absreverse('ietf.meeting.views.upcoming')) - tz_select_input = self.driver.find_element_by_id('timezone-select') - tz_select_bottom_input = self.driver.find_element_by_id('timezone-select-bottom') - local_tz_link = self.driver.find_element_by_id('local-timezone') - utc_tz_link = self.driver.find_element_by_id('utc-timezone') - local_tz_bottom_link = self.driver.find_element_by_id('local-timezone-bottom') - utc_tz_bottom_link = self.driver.find_element_by_id('utc-timezone-bottom') + tz_select_input = self.driver.find_element(By.ID, 'timezone-select') + tz_select_bottom_input = self.driver.find_element(By.ID, 'timezone-select-bottom') + local_tz_link = self.driver.find_element(By.ID, 'local-timezone') + utc_tz_link = self.driver.find_element(By.ID, 'utc-timezone') + local_tz_bottom_link = self.driver.find_element(By.ID, 'local-timezone-bottom') + utc_tz_bottom_link = self.driver.find_element(By.ID, 'utc-timezone-bottom') # wait for the select box to be updated - look for an arbitrary time zone to be in # its options list to detect this @@ -2342,18 +2342,18 @@ class InterimTests(IetfSeleniumTestCase): (By.CSS_SELECTOR, '#timezone-select > option[value="%s"]' % arbitrary_tz) ) ) - arbitrary_tz_bottom_opt = tz_select_bottom_input.find_element_by_css_selector( + arbitrary_tz_bottom_opt = tz_select_bottom_input.find_element(By.CSS_SELECTOR, 'option[value="%s"]' % arbitrary_tz) - utc_tz_opt = tz_select_input.find_element_by_css_selector('option[value="UTC"]') - utc_tz_bottom_opt= tz_select_bottom_input.find_element_by_css_selector('option[value="UTC"]') + utc_tz_opt = tz_select_input.find_element(By.CSS_SELECTOR, 'option[value="UTC"]') + utc_tz_bottom_opt= tz_select_bottom_input.find_element(By.CSS_SELECTOR, 'option[value="UTC"]') # Moment.js guesses local time zone based on the behavior of Selenium's web client. This seems # to inherit Django's settings.TIME_ZONE but I don't know whether that's guaranteed to be consistent. # To avoid test fragility, ask Moment what it considers local and expect that. local_tz = self.driver.execute_script('return moment.tz.guess();') - local_tz_opt = tz_select_input.find_element_by_css_selector('option[value=%s]' % local_tz) - local_tz_bottom_opt = tz_select_bottom_input.find_element_by_css_selector('option[value="%s"]' % local_tz) + local_tz_opt = tz_select_input.find_element(By.CSS_SELECTOR, 'option[value=%s]' % local_tz) + local_tz_bottom_opt = tz_select_bottom_input.find_element(By.CSS_SELECTOR, 'option[value="%s"]' % local_tz) # Should start off in local time zone self.assertTrue(local_tz_opt.is_selected()) @@ -2450,7 +2450,7 @@ class InterimTests(IetfSeleniumTestCase): slug = assignment.slug() # modal should start hidden - modal_div = self.driver.find_element_by_css_selector('div#modal-%s' % slug) + modal_div = self.driver.find_element(By.CSS_SELECTOR, 'div#modal-%s' % slug) self.assertFalse(modal_div.is_displayed()) # Click the 'materials' button @@ -2600,4 +2600,4 @@ class ProceedingsMaterialTests(IetfSeleniumTestCase): # # def testOpenSchedule(self): # url = urlreverse('ietf.meeting.views.edit_schedule', kwargs=dict(num='72',name='test-schedule')) -# r = self.client.get(url) +# r = self.client.get(url) \ No newline at end of file diff --git a/ietf/utils/jstest.py b/ietf/utils/jstest.py index 2b919490c..851d491a5 100644 --- a/ietf/utils/jstest.py +++ b/ietf/utils/jstest.py @@ -8,7 +8,10 @@ skip_selenium = False skip_message = "" try: from selenium import webdriver + from selenium.webdriver.chrome.service import Service + from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.action_chains import ActionChains + from selenium.webdriver.common.by import By except ImportError as e: skip_selenium = True skip_message = "Skipping selenium tests: %s" % e @@ -18,7 +21,6 @@ from ietf.utils.pipe import pipe from ietf.utils.test_runner import IetfLiveServerTestCase from ietf import settings - executable_name = 'chromedriver' code, out, err = pipe('{} --version'.format(executable_name)) if code != 0: @@ -28,12 +30,15 @@ if skip_selenium: print(" "+skip_message) def start_web_driver(): - options = webdriver.ChromeOptions() + service = Service(executable_path="/usr/bin/chromedriver", + log_path=settings.TEST_GHOSTDRIVER_LOG_PATH) + service.start() + options = Options() options.add_argument("headless") options.add_argument("disable-extensions") options.add_argument("disable-gpu") # headless needs this options.add_argument("no-sandbox") # docker needs this - return webdriver.Chrome(options=options, service_log_path=settings.TEST_GHOSTDRIVER_LOG_PATH) + return webdriver.Chrome(service=service, options=options) def selenium_enabled(): @@ -69,9 +74,9 @@ class IetfSeleniumTestCase(IetfLiveServerTestCase): url = self.absreverse(self.login_view) password = '%s+password' % username self.driver.get(url) - self.driver.find_element_by_name('username').send_keys(username) - self.driver.find_element_by_name('password').send_keys(password) - self.driver.find_element_by_xpath('//button[@type="submit"]').click() + self.driver.find_element(By.NAME, 'username').send_keys(username) + self.driver.find_element(By.NAME, 'password').send_keys(password) + self.driver.find_element(By.XPATH, '//button[@type="submit"]').click() def scroll_to_element(self, element): """Scroll an element into view""" @@ -89,5 +94,5 @@ class presence_of_element_child_by_css_selector: self.child_selector = child_selector def __call__(self, driver): - child = self.element.find_element_by_css_selector(self.child_selector) - return child if child is not None else False + child = self.element.find_element(By.CSS_SELECTOR, self.child_selector) + return child if child is not None else False \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 177472edd..f1794e15f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -71,4 +71,4 @@ Unidecode>=0.4.18,<1.2.0 #wsgiref>=0.1.2 xml2rfc>=2.35.0 xym>=0.4.4,!=0.4.7,<1.0 -#zxcvbn-python>=4.4.14 # Not needed until we do back-end password entropy validation +#zxcvbn-python>=4.4.14 # Not needed until we do back-end password entropy validation \ No newline at end of file From 5f2fe1fd1d5076abb17dee90fe82baca14254c59 Mon Sep 17 00:00:00 2001 From: Lars Eggert <lars@eggert.org> Date: Wed, 20 Oct 2021 18:14:52 +0000 Subject: [PATCH 10/15] Make Cygwin tests work again - Legacy-Id: 19446 --- docker/run | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docker/run b/docker/run index cc0719952..79e7510cd 100755 --- a/docker/run +++ b/docker/run @@ -89,6 +89,19 @@ if [ -z "$TAG" ]; then TAG=$(basename "$(svn info "$parent" | grep ^URL | awk '{print $2}' | tr -d '\r')") fi +if [[ $(uname) =~ CYGWIN.* ]]; then + echo "Running under Cygwin, replacing symlinks with file copies" + ICSFILES=$(/usr/bin/find "$parent/vzic/zoneinfo/" -name '*.ics' -print) + for ICSFILE in $ICSFILES; do + LINK=$(head -n1 "$ICSFILE" | sed -e '/link .*/!d' -e 's/link \(.*\)/\1/') + if [ "$LINK" ]; then + WDIR=$(dirname "$ICSFILE") + echo "Replacing $(basename "$ICSFILE") with $LINK" + cp -f "$WDIR/$LINK" "$ICSFILE" + fi + done +fi + echo "Starting a docker container for '$REPO:$TAG'." mkdir -p "$MYSQLDIR" docker run -ti -p "$PORT":8000 -p 33306:3306 \ From de6cf5bd90d323d3a7b3619babeb5aebd069915b Mon Sep 17 00:00:00 2001 From: Lars Eggert <lars@eggert.org> Date: Thu, 21 Oct 2021 07:41:37 +0000 Subject: [PATCH 11/15] Fix Faker at 9.20 to address mypy issues - Legacy-Id: 19447 --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index f1794e15f..8ce87df70 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,7 +28,8 @@ docutils>=0.12,!=0.15 #factory-boy>=2.9.0,<3 #Faker>=0.8.8,!=0.8.9,!=0.8.10 # from factory-boy # Faker 0.8.9,0.8.10 sometimes return string names instead of unicode. factory-boy>=3 -Faker>=0.8.11 # from factory-boy # Faker 0.8.9,0.8.10 sometimes return string names instead of unicode. +#Faker>=0.8.11 # from factory-boy # Faker 0.8.9,0.8.10 sometimes return string names instead of unicode. +Faker==9.20 github3.py>=1.2 hashids>=1.1.0 html2text>=2019.8.11 From 76ee4bd0e23230071bae0cd8bab476ecf14982b6 Mon Sep 17 00:00:00 2001 From: Lars Eggert <lars@eggert.org> Date: Thu, 21 Oct 2021 07:42:10 +0000 Subject: [PATCH 12/15] Set locale; minor other changes - Legacy-Id: 19448 --- docker/Dockerfile | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index da92655ad..be15f479f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -23,6 +23,8 @@ EXPOSE 3306 ENV DEBIAN_FRONTEND=noninteractive RUN apt-get -y update && \ + # apt-get upgrade is normally not a good idea, but this is a dev container + apt-get upgrade && \ # Install all dependencies that are available as packages apt-get -y install --no-install-recommends \ apache2-utils \ @@ -37,6 +39,7 @@ RUN apt-get -y update && \ graphviz \ libmagic-dev \ libmariadb-dev \ + locales \ mariadb-server \ npm \ pigz \ @@ -69,6 +72,12 @@ RUN apt-get -y update && \ apt-get -y clean && \ rm -rf /var/lib/apt/lists/* +# Set locale to en_US.UTF-8 +RUN dpkg-reconfigure locales && \ + locale-gen en_US.UTF-8 && \ + update-locale LC_ALL en_US.UTF-8 +ENV LC_ALL en_US.UTF-8 + # Install bower RUN npm install -g bower @@ -95,6 +104,10 @@ RUN sed -i 's/\[mysqld\]/\[mysqld\]\nperformance_schema=ON/' /etc/mysql/mariadb. # This is the repo that has the PR: ADD https://github.com/grooverdan/mariadb-sys/archive/refs/heads/master.zip / RUN unzip /master.zip +RUN rm /master.zip + +# Colorize the bash shell +RUN sed -i 's/#force_color_prompt=/force_color_prompt=/' /root/.bashrc # Copy the startup file COPY docker-init.sh /docker-init.sh From 19409b9156f6692f08b51a6008d82c9930bc09ce Mon Sep 17 00:00:00 2001 From: Lars Eggert <lars@eggert.org> Date: Fri, 22 Oct 2021 06:20:35 +0000 Subject: [PATCH 13/15] Faker==9.2.0 - Legacy-Id: 19450 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 8ce87df70..1bcaa876f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -29,7 +29,7 @@ docutils>=0.12,!=0.15 #Faker>=0.8.8,!=0.8.9,!=0.8.10 # from factory-boy # Faker 0.8.9,0.8.10 sometimes return string names instead of unicode. factory-boy>=3 #Faker>=0.8.11 # from factory-boy # Faker 0.8.9,0.8.10 sometimes return string names instead of unicode. -Faker==9.20 +Faker==9.2.0 github3.py>=1.2 hashids>=1.1.0 html2text>=2019.8.11 From 3b3e662719cf06b7364ca8e8151f97db27aef0b3 Mon Sep 17 00:00:00 2001 From: Lars Eggert <lars@eggert.org> Date: Mon, 25 Oct 2021 13:21:52 +0000 Subject: [PATCH 14/15] Ship a database snapshot with the docker image, to speed up first use - Legacy-Id: 19451 --- docker/Dockerfile | 15 +++++++++++++++ docker/docker-init.sh | 24 +++++++++++------------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index be15f479f..d02970593 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -109,6 +109,21 @@ RUN rm /master.zip # Colorize the bash shell RUN sed -i 's/#force_color_prompt=/force_color_prompt=/' /root/.bashrc +# Make a database dump available as part of the image, for if a user doesn't +# have one installed locally yet - this saves a bunch of time then +ADD https://www.ietf.org/lib/dt/sprint/ietf_utf8.sql.gz / +RUN pigz -v -d /ietf_utf8.sql.gz && \ + sed -i -e 's/ENGINE=MyISAM/ENGINE=InnoDB/' /ietf_utf8.sql +RUN service mariadb start && \ + echo "This sequence will take a long time, please be patient" && \ + mysqladmin -u root --default-character-set=utf8 create ietf_utf8 && \ + bash -c "cd /mariadb-sys-master && mysql --user root < sys_10.sql" && \ + bash -c "mysql --user root ietf_utf8 <<< \"GRANT ALL PRIVILEGES ON *.* TO django@localhost IDENTIFIED BY 'RkTkDPFnKpko'; FLUSH PRIVILEGES;\"" && \ + bash -c "mysql --user=django --password=RkTkDPFnKpko -f ietf_utf8 < /ietf_utf8.sql" && \ + service mariadb stop && \ + rm -rf /ietf_utf8.sql /mariadb-sys-master && \ + mv /var/lib/mysql / + # Copy the startup file COPY docker-init.sh /docker-init.sh RUN chmod +x /docker-init.sh diff --git a/docker/docker-init.sh b/docker/docker-init.sh index 36cef7f40..a99f559a5 100644 --- a/docker/docker-init.sh +++ b/docker/docker-init.sh @@ -10,15 +10,23 @@ fi service rsyslog start if [ -z "$(ls -A $MYSQLDIR/mysql 2>/dev/null)" ]; then - echo "WARNING: Database seems to be empty." - mysql_install_db > /dev/null || exit 1 + can=$(date -r /mysql +%s) + now=$(date +%s) + age=$((($now - $can)/86400)) + echo "NOTE: Database empty; populating it from canned snapshot ($age days old)" + echo " This will take a little while..." + cp -r /mysql/* $MYSQLDIR fi service mariadb start if ! service mariadb status; then - echo "ERROR: MySQL isn't running." + echo "ERROR: MySQL didn't start. Here are some possible causes:" + echo "-------------------------------------------------------------------" grep mysqld /var/log/syslog + echo "-------------------------------------------------------------------" + echo "Such errors are usually due to a corrupt or outdated database." + echo "Remove your local database and let the image install a clean copy." exit 1 fi @@ -27,16 +35,6 @@ if [ ! -f /root/src/ietf/settings_local.py ]; then cp /root/src/docker/settings_local.py /root/src/ietf/settings_local.py fi -if [ ! -d $MYSQLDIR/ietf_utf8 ]; then - echo "WARNING: IETF database seems to be missing; populating it from dump." - mysqladmin -u root --default-character-set=utf8 create ietf_utf8 - pushd /mariadb-sys-master || exit - mysql -u root < sys_10.sql - popd || exit - mysql -u root ietf_utf8 <<< "GRANT ALL PRIVILEGES ON *.* TO django@localhost IDENTIFIED BY 'RkTkDPFnKpko'; FLUSH PRIVILEGES;" - /root/src/docker/updatedb -fi - for sub in \ test/id \ test/staging \ From 2b80cf7e1d0f4709e8f05ce59223f09d628431b1 Mon Sep 17 00:00:00 2001 From: Lars Eggert <lars@eggert.org> Date: Wed, 27 Oct 2021 07:57:10 +0000 Subject: [PATCH 15/15] Add some more explanatory/diagnostic text. Branch ready for merge. - Legacy-Id: 19465 --- docker/docker-init.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docker/docker-init.sh b/docker/docker-init.sh index a99f559a5..455a828c4 100644 --- a/docker/docker-init.sh +++ b/docker/docker-init.sh @@ -74,8 +74,16 @@ python -m smtpd -n -c DebuggingServer localhost:2025 & echo if [ -z "$*" ]; then + echo "You can execute arbitrary commands now, e.g.," + echo + echo " ietf/manage.py runserver 0.0.0.0:8000" + echo + echo "to start a development instance of the Datatracker." + echo bash else + echo "Executing \"$*\" and stopping container." + echo bash -c "$*" fi