dd7dcba780be9616a324471e981ec78a088befb4
configuration/archive_instance_setup/mountnvmeswap
| ... | ... | @@ -1,31 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | - |
|
| 3 | -# Script to deploy on an instance that has an ephemeral volume as /dev/nvme0n1 (adjust env var EPHEMERAL_VOLUME if different) |
|
| 4 | -# Ensures the partition is xfs-formatted, any existing partition contents will be overwritten if formatted otherwise. |
|
| 5 | -# An existing xfs partition will be left alone. |
|
| 6 | -EPHEMERAL_VOLUME_NAME=$( |
|
| 7 | - # List all block devices and find those named nvme... |
|
| 8 | - for i in $(lsblk | grep -o "nvme[0-9][0-9]\?n[0-9]" | sort -u); do |
|
| 9 | - # If they don't have any partitions, then... |
|
| 10 | - if ! lsblk | grep -o "${i}p[0-9]\+" 2>&1 >/dev/null; then |
|
| 11 | - # ...check whether they are EBS devices |
|
| 12 | - /sbin/ebsnvme-id -u "/dev/$i" >/dev/null |
|
| 13 | - # If not, list their name because then they must be ephemeral instance storage |
|
| 14 | - if [[ $? -ne 0 ]]; then |
|
| 15 | - echo "${i}" |
|
| 16 | - fi |
|
| 17 | - fi |
|
| 18 | - done 2>/dev/null | head -n 1 ) |
|
| 19 | -if [ -n "${EPHEMERAL_VOLUME_NAME}" ]; then |
|
| 20 | - EPHEMERAL_VOLUME=/dev/${EPHEMERAL_VOLUME_NAME} |
|
| 21 | - FSTYPE=$(blkid -p $EPHEMERAL_VOLUME -s TYPE -o value) |
|
| 22 | - if [ "$FSTYPE" = "" ]; then |
|
| 23 | - echo FSTYPE was empty, creating swap partition |
|
| 24 | - mkswap $EPHEMERAL_VOLUME |
|
| 25 | - swapon --priority 2 -a $EPHEMERAL_VOLUME |
|
| 26 | - else |
|
| 27 | - echo "FSTYPE was $FSTYPE, not touching" |
|
| 28 | - fi |
|
| 29 | -else |
|
| 30 | - echo "No ephemeral partition found. Not creating any swap partition." |
|
| 31 | -fi |
configuration/archive_instance_setup/mountnvmeswap
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/repo/usr/local/bin/mountnvmeswap |
|
| ... | ... | \ No newline at end of file |
configuration/archive_instance_setup/mountnvmeswap.service
| ... | ... | @@ -1,12 +0,0 @@ |
| 1 | -[Unit] |
|
| 2 | -Description=An unformatted /dev/nvme0n1 is turned into swap space |
|
| 3 | -Requires=-.mount |
|
| 4 | -After=-.mount |
|
| 5 | - |
|
| 6 | -[Install] |
|
| 7 | -RequiredBy=multi-user.target |
|
| 8 | - |
|
| 9 | -[Service] |
|
| 10 | -Type=oneshot |
|
| 11 | -RemainAfterExit=true |
|
| 12 | -ExecStart=/usr/local/bin/mountnvmeswap |
configuration/archive_instance_setup/mountnvmeswap.service
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/repo/etc/systemd/system/mountnvmeswap.service |
|
| ... | ... | \ No newline at end of file |
configuration/aws-automation/getLatestImageOfType.sh
| ... | ... | @@ -1,3 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | -imageType="$1" |
|
| 3 | -aws ec2 describe-images --filter Name=tag:image-type,Values=${imageType} | jq --raw-output '.Images | sort_by(.CreationDate) | .[].ImageId' | tail -n 1 |
configuration/aws-automation/getLatestImageOfType.sh
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/repo/usr/local/bin/getLatestImageOfType.sh |
|
| ... | ... | \ No newline at end of file |
configuration/buildAndUpdateProduct.sh
| ... | ... | @@ -747,9 +747,10 @@ if [[ "$@" == "build" ]] || [[ "$@" == "all" ]]; then |
| 747 | 747 | fi |
| 748 | 748 | |
| 749 | 749 | extra="$extra -P with-not-android-relevant,!with-mobile" |
| 750 | - echo "Building and installing forked GWT version..." |
|
| 751 | - JAVA_HOME="${JAVA8_HOME}" `dirname $0`/install-gwt "${PROJECT_HOME}" |
|
| 752 | - |
|
| 750 | + if [ $gwtcompile -eq 1 ]; then |
|
| 751 | + echo "Building and installing forked GWT version..." |
|
| 752 | + JAVA_HOME="${JAVA8_HOME}" `dirname $0`/install-gwt "${PROJECT_HOME}" |
|
| 753 | + fi |
|
| 753 | 754 | echo "Using following command: mvn $extra -DargLine=\"$APP_PARAMETERS\" -fae -s $MAVEN_SETTINGS $clean install" |
| 754 | 755 | echo "Maven version used: `mvn --version`" |
| 755 | 756 | echo "JAVA_HOME used: $JAVA_HOME" |
configuration/crontab
| ... | ... | @@ -1 +1,3 @@ |
| 1 | -* * * * * export PATH=/bin:/usr/bin:/usr/local/bin; sleep $(( $RANDOM * 60 / 32768 )); update_authorized_keys_for_landscape_managers_if_changed $( cat /root/ssh-key-reader.token ) https://security-service.sapsailing.com /root 2>&1 >>/var/log/sailing.err |
|
| ... | ... | \ No newline at end of file |
| 0 | +* * * * * export PATH=/bin:/usr/bin:/usr/local/bin; sleep $(( $RANDOM * 60 / 32768 )); update_authorized_keys_for_landscape_managers_if_changed $( cat /root/ssh-key-reader.token ) https://security-service.sapsailing.com /root 2>&1 >>/var/log/sailing.err |
|
| 1 | +# NOTICE: Please try to reference the customised crontabs at $GIT_HOME/configuration/crontabs or use |
|
| 2 | +# the build-crontab script. This file has been maintained for continuity, but is deprecated. |
|
| ... | ... | \ No newline at end of file |
configuration/crontabs/README
| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | +This is the crontab repo and it contains one line crontabs for all the different environments. |
|
| 2 | +These files are concatenated by the build_crontab script. Any time the crontab should contain |
|
| 3 | +a user's home directory, instead write PATH_OF_HOME_DIR_TO_REPLACE; if the crontab should |
|
| 4 | +contain the path to the git directory, instead write PATH_OF_GIT_HOME_DIR_TO_REPLACE. |
|
| 5 | +These are replaced by the build-crontab script. |
|
| 6 | + |
|
| 7 | +Note: theses files are symbolically linked to, so beware of the ramifications of changes. |
|
| ... | ... | \ No newline at end of file |
configuration/crontabs/crontab-docker-registry-gc
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +0 7 2 * * export PATH=/bin:/usr/bin:/usr/local/bin; docker exec -it registry-registry-1 registry garbage-collect /etc/docker/registry/config.yml |
|
| ... | ... | \ No newline at end of file |
configuration/crontabs/crontab-download-new-archived-trac-trac-events
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +15 12 * * * PATH_OF_GIT_HOME_DIR_TO_REPLACE/configuration/downloadNewArchivedTracTracEvents.sh PATH_OF_HOME_DIR_TO_REPLACE/static/TracTracTracks "PATH_OF_GIT_HOME_DIR_TO_REPLACE">PATH_OF_HOME_DIR_TO_REPLACE/downloadNewArchivedTracTracEvents.out 2>PATH_OF_HOME_DIR_TO_REPLACE/downloadNewArchivedTracTracEvents.err |
|
| ... | ... | \ No newline at end of file |
configuration/crontabs/crontab-mail-events-on-my
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +0 10 1 * * export PATH=/bin:/usr/bin:/usr/local/bin; mail-events-on-my >/dev/null 2>/dev/null |
|
| ... | ... | \ No newline at end of file |
configuration/crontabs/crontab-manage2sail-example
| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | +# If you'd like to receive e-mail notifications about new Manage2Sail results for an event, |
|
| 2 | +# adjust the following accordingly, making sure you also update the mailing list file |
|
| 3 | +# referenced by the notify... script. Adjust the Manage2Sail event ID in the script |
|
| 4 | +# to point to the event you'd like to observe. |
|
| 5 | +#* * * * * PATH_OF_HOME_DIR_TO_REPLACE/bin/notifyAbout49erEuros2023Updates 2>PATH_OF_HOME_DIR_TO_REPLACE/notifyAbout49erEuros2023Updates.err >PATH_OF_HOME_DIR_TO_REPLACE/notifyAbout49erEuros2023Updates.out |
configuration/crontabs/crontab-mongo-health-check
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +* * * * * PATH_OF_GIT_HOME_DIR_TO_REPLACE/configuration/notify-unhealthy-mongodb 2>PATH_OF_HOME_DIR_TO_REPLACE/notify-unhealthy-mongodb.err >PATH_OF_HOME_DIR_TO_REPLACE/notify-unhealthy-mongodb.out |
|
| ... | ... | \ No newline at end of file |
configuration/crontabs/crontab-switchoverArchive
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +* * * * * export PATH=/bin:/usr/bin:/usr/local/bin; sleep $(( $RANDOM * 60 / 32768 )); switchoverArchive.sh /etc/httpd/conf.d/000-macros.conf 2 9 |
|
| ... | ... | \ No newline at end of file |
configuration/crontabs/crontab-syncgit
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +*/10 * * * * export PATH=/bin:/usr/bin:/usr/local/bin; syncgit PATH_OF_GIT_HOME_DIR_TO_REPLACE |
|
| ... | ... | \ No newline at end of file |
configuration/crontabs/crontab-update-authorized-keys
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +* * * * * export PATH=/bin:/usr/bin:/usr/local/bin; sleep $(( $RANDOM * 60 / 32768 )); update_authorized_keys_for_landscape_managers_if_changed $( cat PATH_OF_HOME_DIR_TO_REPLACE/ssh-key-reader.token ) https://security-service.sapsailing.com PATH_OF_HOME_DIR_TO_REPLACE #2>&1 >>/var/log/sailing.err |
|
| ... | ... | \ No newline at end of file |
configuration/crontabs/crontab-update-landscape-managers-mailing-list
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +*/10 * * * * export PATH=/bin:/usr/bin:/usr/local/bin; sleep $(( $RANDOM * 60 / 32768 )); update_landscape_managers_mailing_list.sh $(cat /root/ssh-key-reader.token) /var/cache |
|
| ... | ... | \ No newline at end of file |
configuration/crontabs/crontab-update-trac-trac-urls
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +10 12 * * * PATH_OF_GIT_HOME_DIR_TO_REPLACE/configuration/update-tractrac-urls-to-archive.sh "PATH_OF_GIT_HOME_DIR_TO_REPLACE" >PATH_OF_HOME_DIR_TO_REPLACE/update-tractrac-urls-to-archive.out 2>PATH_OF_HOME_DIR_TO_REPLACE/update-tractrac-urls-to-archive.err |
|
| ... | ... | \ No newline at end of file |
configuration/downloadNewArchivedTracTracEvents.sh
| ... | ... | @@ -1,18 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | -GIT_ROOT=/home/wiki/gitwiki |
|
| 3 | -# Downloads all TracTrac event data based on ${GIT_ROOT}/configuration/tractrac-json-urls |
|
| 4 | -# into the target directory (specified as $1) for those event URLs whose specific folder |
|
| 5 | -# does not yet exist in the target directory. |
|
| 6 | -TARGET_DIR="${1}" |
|
| 7 | -JSON_URLS_FILE="${GIT_ROOT}/configuration/tractrac-json-urls" |
|
| 8 | -for i in `cat "${JSON_URLS_FILE}"`; do |
|
| 9 | - EVENT_DB="$( basename $( dirname ${i} ) )" |
|
| 10 | - if [ -d "${TARGET_DIR}/${EVENT_DB}" ]; then |
|
| 11 | - echo "Directory for event ${EVENT_DB} already found. Not downloading again. Use" |
|
| 12 | - echo " ${GIT_ROOT}/configuration/downloadTracTracEvent ${i} ${TARGET_DIR}" |
|
| 13 | - echo "to force an update." |
|
| 14 | - else |
|
| 15 | - echo "Did not find directory for event ${EVENT_DB} yet in ${TARGET_DIR}. Downloading..." |
|
| 16 | - ${GIT_ROOT}/configuration/downloadTracTracEvent "${i}" "${TARGET_DIR}" |
|
| 17 | - fi |
|
| 18 | -done |
configuration/downloadNewArchivedTracTracEvents.sh
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +environments_scripts/central_reverse_proxy/files/usr/local/bin/downloadNewArchivedTracTracEvents.sh |
|
| ... | ... | \ No newline at end of file |
configuration/environments/archive-server
| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | -MEMORY=400g |
|
| 1 | +MEMORY=500g |
|
| 2 | 2 | SERVER_NAME=ARCHIVE |
| 3 | 3 | REPLICATION_CHANNEL=sapsailinganalytics-archive |
| 4 | 4 | MONGODB_URI="mongodb://dbserver.internal.sapsailing.com:10201/winddb?replicaSet=archive&retryWrites=true&readPreference=secondaryPreferred" |
configuration/environments/dev-server
| ... | ... | @@ -1,13 +1,4 @@ |
| 1 | 1 | SERVER_NAME=DEV |
| 2 | 2 | REPLICATION_HOST=rabbit.internal.sapsailing.com |
| 3 | 3 | REPLICATION_CHANNEL=sapsailinganalytics-dev |
| 4 | -TELNET_PORT=14888 |
|
| 5 | -SERVER_PORT=8888 |
|
| 6 | -MONGODB_HOST=dbserver.internal.sapsailing.com |
|
| 7 | -MONGODB_PORT=10200 |
|
| 8 | -EXPEDITION_PORT=2010 |
|
| 9 | -REPLICATE_ON_START=False |
|
| 10 | -REPLICATE_MASTER_SERVLET_HOST= |
|
| 11 | -REPLICATE_MASTER_SERVLET_PORT= |
|
| 12 | -REPLICATE_MASTER_QUEUE_HOST= |
|
| 13 | -REPLICATE_MASTER_QUEUE_PORT= |
|
| 4 | +MONGODB_URI="mongodb://dbserver.internal.sapsailing.com:10202/dev?replicaSet=slow&retryWrites=true&readPreference=nearest" |
configuration/environments/security-service-master
| ... | ... | @@ -1,17 +1,3 @@ |
| 1 | -TELNET_PORT=14888 |
|
| 2 | -SERVER_PORT=8888 |
|
| 3 | -MONGODB_HOST=dbserver.internal.sapsailing.com |
|
| 4 | -MONGODB_PORT=10202 |
|
| 5 | -EXPEDITION_PORT=2010 |
|
| 6 | -DEPLOY_TO=security_service |
|
| 7 | -REPLICATE_MASTER_SERVLET_PORT=8888 |
|
| 8 | -REPLICATE_MASTER_QUEUE_HOST=rabbit.internal.sapsailing.com |
|
| 9 | -REPLICATE_MASTER_QUEUE_PORT=5672 |
|
| 10 | -REPLICATION_HOST=rabbit.internal.sapsailing.com |
|
| 11 | -ADDITIONAL_JAVA_ARGS="$ADDITIONAL_JAVA_ARGS -Dorg.eclipse.jetty.LEVEL=OFF -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -Drestore.tracked.races=true -Dpolardata.source.url=https://www.sapsailing.com -XX:ThreadPriorityPolicy=2" |
|
| 12 | -SERVER_STARTUP_NOTIFY=axel.uhl@sap.com |
|
| 13 | -BUILD_COMPLETE_NOTIFY=axel.uhl@sap.com |
|
| 14 | - |
|
| 15 | 1 | # To enable the use of the shared SecurityService and SharedSailingData from security-service.sapsailing.com: |
| 16 | 2 | ADDITIONAL_JAVA_ARGS="$ADDITIONAL_JAVA_ARGS -Dsecurity.sharedAcrossSubdomainsOf=sapsailing.com -Dsecurity.baseUrlForCrossDomainStorage=https://security-service.sapsailing.com -Dgwt.acceptableCrossDomainStorageRequestOriginRegexp=https?://(.*\.)?sapsailing\.com(:[0-9]*)?$" |
| 17 | 3 | # For the security-service master instance, don't activate replication from security-service |
configuration/environments_scripts/README
| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | +The environments_scripts directory contains more directories, which map to environment types, each holding scripts and files useful to that environment. Each environment type also contains a "users" folder, which contains subfolders for each user on the system. |
|
| 2 | +The build-crontab script uses the contents of these subfolders to create and install a customised crontab for each user in the folder. It does this by concatenating one liner crontabs into an uber crontab. |
|
| 3 | +Any time the crontab should contain a user's home directory, instead write PATH_OF_HOME_DIR_TO_REPLACE; if the crontab should |
|
| 4 | +contain the path to the git directory, instead write PATH_OF_GIT_HOME_DIR_TO_REPLACE. |
|
| 5 | +These are replaced by the build-crontab script with the correct paths |
|
| 6 | + |
|
| 7 | +environment_scripts (directory) |
|
| 8 | +| |
|
| 9 | +|_environement_type (directory) |
|
| 10 | + | |
|
| 11 | + |_usefulScripts |
|
| 12 | + |_users (directory) |
|
| 13 | + | |
|
| 14 | + |_user1 (directory) |
|
| 15 | + | |
|
| 16 | + |_symbolicLinks (to configuration/crontabs) |
configuration/environments_scripts/build-crontab
| ... | ... | @@ -0,0 +1,56 @@ |
| 1 | +#!/bin/bash |
|
| 2 | + |
|
| 3 | +# Purpose: The first parameter is an image type. This script iterates over the image types' USER folders, concatenating all the symbolic |
|
| 4 | +# links (referencing locations within configuration/crontabs) into one file for each user. It also takes the name of the user containing a copy of the git repo |
|
| 5 | +# as well as the name of the dir within that user to go to. The created files for each user go into that user's |
|
| 6 | +# home dir and are installed for that user too. Note: within these crontabs are certain regexes, which are replaced by the path to the git home dir and the matching |
|
| 7 | +# user's home. |
|
| 8 | +# Useful files are also copied across from the "files" dir within each image type. |
|
| 9 | + |
|
| 10 | +if [[ $# -ne 3 && $# -ne 4 && $# -ne 5 ]]; then |
|
| 11 | + echo "$0 <ENVIRONMENT_TYPE> <USER_WITH_COPY_OF_REPO> <RELATIVE_PATH_OF_GIT_DIR_WITHIN_USER> " |
|
| 12 | + echo "" |
|
| 13 | + echo "Where USER_WITH_COPY_OF_REPO is a user that contains a checked out copy of the main git." |
|
| 14 | + echo "And where RELATIVE_PATH_OF_GIT_DIR_WITHIN_USER is the path to the git repo from the USER_WITH_COPY_OF_REPO's home directory." |
|
| 15 | + echo "Use the s(imple) flag to only do the crontab and not copy any files across. The n(o install) flag can be used to setup the crontabs but not install them." |
|
| 16 | + exit 2 |
|
| 17 | +fi |
|
| 18 | +INSTALL_CRONTAB="true" |
|
| 19 | +options='sn' |
|
| 20 | +while getopts $options option |
|
| 21 | +do |
|
| 22 | + case $option in |
|
| 23 | + n) INSTALL_CRONTAB="false";; |
|
| 24 | + s) ONLY_CRONTAB="true";; |
|
| 25 | + \?) echo "Invalid option" |
|
| 26 | + exit 4;; |
|
| 27 | + esac |
|
| 28 | +done |
|
| 29 | +shift $((OPTIND-1)) # shift the arguments along so -s is no longer $1 |
|
| 30 | +ENV_TYPE="$1" |
|
| 31 | +GIT_USER="$2" |
|
| 32 | +RELATIVE_GIT_DIR_NAME="$3" |
|
| 33 | +cd "$(dirname "$0")/${ENV_TYPE}" |
|
| 34 | +if [[ -d "users" ]]; then |
|
| 35 | + cd "users" |
|
| 36 | + GIT_PATH="$(eval echo $(printf "~%q" "$GIT_USER"))/${RELATIVE_GIT_DIR_NAME}" # The path to the git repo that contains the files needed. |
|
| 37 | + for dir in $(ls -d */ ); do |
|
| 38 | + USERNAME=$(echo $dir | sed "s/\/$//") # Dirname is the username. The trailing slash is removed. |
|
| 39 | + HOME_DIR=$(eval echo $(printf "~%q" "$USERNAME")) # The path to the home dir of the user whose cronjob will be installed. |
|
| 40 | + > $HOME_DIR/crontab |
|
| 41 | + for crontab in $(ls ${USERNAME}/crontab*); do |
|
| 42 | + cat "$crontab" >> $HOME_DIR/crontab |
|
| 43 | + echo "" >> $HOME_DIR/crontab # Adds a newline |
|
| 44 | + done |
|
| 45 | + sed -i "s|PATH_OF_GIT_HOME_DIR_TO_REPLACE|${GIT_PATH}|g" $HOME_DIR/crontab # Sets correct path to the git repo within the crontab. |
|
| 46 | + sed -i "s|PATH_OF_HOME_DIR_TO_REPLACE|${HOME_DIR}|g" $HOME_DIR/crontab # Sets the correct path to the home dir of the user whose crontab will be installed. |
|
| 47 | + if [[ "$INSTALL_CRONTAB" == "true" ]]; then |
|
| 48 | + crontab -u ${USERNAME} $HOME_DIR/crontab # Install the crontab in the given user's home dir. |
|
| 49 | + fi |
|
| 50 | + done |
|
| 51 | + cd .. # exits users folder, which is essential for the next commands |
|
| 52 | +fi |
|
| 53 | +if [[ "$ONLY_CRONTAB" != "true" && -d "files" ]]; then |
|
| 54 | + cd "files" |
|
| 55 | + \cp -rL * / # copies all files accross, realising any symbolic links. The backslash escapes the alias cp -i. |
|
| 56 | +fi |
configuration/environments_scripts/build_server/files/etc/sysconfig/hudson
| ... | ... | @@ -0,0 +1,88 @@ |
| 1 | +## Path: Development/Hudson |
|
| 2 | +## Description: Configuration for the Hudson continuous build server |
|
| 3 | +## Type: string |
|
| 4 | +## Default: "/var/lib/hudson" |
|
| 5 | +## ServiceRestart: hudson |
|
| 6 | +# |
|
| 7 | +# Directory where Hudson store its configuration and working |
|
| 8 | +# files (checkouts, build reports, artifacts, ...). |
|
| 9 | +# |
|
| 10 | +HUDSON_HOME="/home/hudson/repo" |
|
| 11 | + |
|
| 12 | +## Type: string |
|
| 13 | +## Default: "" |
|
| 14 | +## ServiceRestart: hudson |
|
| 15 | +# |
|
| 16 | +# Java executable to run Hudson |
|
| 17 | +# When left empty, we'll try to find the suitable Java. |
|
| 18 | +# |
|
| 19 | + |
|
| 20 | +HUDSON_JAVA_CMD="/opt/sapjvm_8/bin/java" |
|
| 21 | + |
|
| 22 | +## Type: string |
|
| 23 | +## Default: "hudson" |
|
| 24 | +## ServiceRestart: hudson |
|
| 25 | +# |
|
| 26 | +# Unix user account that runs the Hudson daemon |
|
| 27 | +# Be careful when you change this, as you need to update |
|
| 28 | +# permissions of $HUDSON_HOME and /var/log/hudson. |
|
| 29 | +# |
|
| 30 | +HUDSON_USER="hudson" |
|
| 31 | + |
|
| 32 | +## Type: string |
|
| 33 | +## Default: "-Djava.awt.headless=true" |
|
| 34 | +## ServiceRestart: hudson |
|
| 35 | +# |
|
| 36 | +# Options to pass to java when running Hudson. |
|
| 37 | +# |
|
| 38 | +HUDSON_JAVA_OPTIONS="-Djava.awt.headless=true -Xmx2G -Dhudson.slaves.ChannelPinger.pingInterval=60 -Dhudson.slaves.ChannelPinger.pingIntervalSeconds=60 -Dhudson.slaves.ChannelPinger.pingTimeoutSeconds=60" |
|
| 39 | + |
|
| 40 | +## Type: integer(0:65535) |
|
| 41 | +## Default: 8080 |
|
| 42 | +## ServiceRestart: hudson |
|
| 43 | +# |
|
| 44 | +# Port Hudson is listening on. |
|
| 45 | +# |
|
| 46 | +HUDSON_PORT="8080" |
|
| 47 | + |
|
| 48 | +## Type: integer(1:9) |
|
| 49 | +## Default: 5 |
|
| 50 | +## ServiceRestart: hudson |
|
| 51 | +# |
|
| 52 | +# Debug level for logs -- the higher the value, the more verbose. |
|
| 53 | +# 5 is INFO. |
|
| 54 | +# |
|
| 55 | +HUDSON_DEBUG_LEVEL="5" |
|
| 56 | + |
|
| 57 | +## Type: yesno |
|
| 58 | +## Default: no |
|
| 59 | +## ServiceRestart: hudson |
|
| 60 | +# |
|
| 61 | +# Whether to enable access logging or not. |
|
| 62 | +# |
|
| 63 | +HUDSON_ENABLE_ACCESS_LOG="no" |
|
| 64 | + |
|
| 65 | +## Type: integer |
|
| 66 | +## Default: 100 |
|
| 67 | +## ServiceRestart: hudson |
|
| 68 | +# |
|
| 69 | +# Maximum number of HTTP worker threads. |
|
| 70 | +# |
|
| 71 | +HUDSON_HANDLER_MAX="100" |
|
| 72 | + |
|
| 73 | +## Type: integer |
|
| 74 | +## Default: 20 |
|
| 75 | +## ServiceRestart: hudson |
|
| 76 | +# |
|
| 77 | +# Maximum number of idle HTTP worker threads. |
|
| 78 | +# |
|
| 79 | +HUDSON_HANDLER_IDLE="20" |
|
| 80 | + |
|
| 81 | +## Type: string |
|
| 82 | +## Default: "" |
|
| 83 | +## ServiceRestart: hudson |
|
| 84 | +# |
|
| 85 | +# Pass arbitrary arguments to Hudson. |
|
| 86 | +# Full option list: java -jar hudson.war --help |
|
| 87 | +# |
|
| 88 | +HUDSON_ARGS="" |
configuration/environments_scripts/build_server/files/etc/systemd/system/hudson.service
| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | +[Unit] |
|
| 2 | +Description=The Hudson start-up / shut-down service |
|
| 3 | +Requires=-.mount |
|
| 4 | +After=-.mount |
|
| 5 | + |
|
| 6 | +[Install] |
|
| 7 | +RequiredBy=multi-user.target |
|
| 8 | + |
|
| 9 | +[Service] |
|
| 10 | +Type=oneshot |
|
| 11 | +RemainAfterExit=true |
|
| 12 | +ExecStart=/etc/init.d/hudson start |
|
| 13 | +ExecStop=/etc/init.d/hudson stop |
configuration/environments_scripts/build_server/files/etc/systemd/system/mountnvmeswap.service
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/etc/systemd/system/mountnvmeswap.service |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/build_server/files/usr/local/bin/getLatestImageOfType.sh
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/getLatestImageOfType.sh |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/build_server/files/usr/local/bin/hudson
| ... | ... | @@ -0,0 +1,129 @@ |
| 1 | +#!/bin/sh |
|
| 2 | +# Check for missing binaries (stale symlinks should not happen) |
|
| 3 | +HUDSON_WAR="/usr/lib/hudson/hudson.war" |
|
| 4 | +test -r "$HUDSON_WAR" || { echo "$HUDSON_WAR not installed"; |
|
| 5 | + if [ "$1" = "stop" ]; then exit 0; |
|
| 6 | + else exit 5; fi; } |
|
| 7 | + |
|
| 8 | +# Check for existence of needed config file and read it |
|
| 9 | +HUDSON_CONFIG=/etc/sysconfig/hudson |
|
| 10 | +test -e "$HUDSON_CONFIG" || { echo "$HUDSON_CONFIG not existing"; |
|
| 11 | + if [ "$1" = "stop" ]; then exit 0; |
|
| 12 | + else exit 6; fi; } |
|
| 13 | +test -r "$HUDSON_CONFIG" || { echo "$HUDSON_CONFIG not readable. Perhaps you forgot 'sudo'?"; |
|
| 14 | + if [ "$1" = "stop" ]; then exit 0; |
|
| 15 | + else exit 6; fi; } |
|
| 16 | + |
|
| 17 | +HUDSON_PID_FILE="/var/run/hudson.pid" |
|
| 18 | +HUDSON_USER="hudson" |
|
| 19 | +HUDSON_GROUP="hudson" |
|
| 20 | + |
|
| 21 | +# Source function library. |
|
| 22 | +. /etc/init.d/functions |
|
| 23 | + |
|
| 24 | +# Read config |
|
| 25 | +[ -f "$HUDSON_CONFIG" ] && . "$HUDSON_CONFIG" |
|
| 26 | + |
|
| 27 | +# Set up environment accordingly to the configuration settings |
|
| 28 | +[ -n "$HUDSON_HOME" ] || { echo "HUDSON_HOME not configured in $HUDSON_CONFIG"; |
|
| 29 | + if [ "$1" = "stop" ]; then exit 0; |
|
| 30 | + else exit 6; fi; } |
|
| 31 | +[ -d "$HUDSON_HOME" ] || { echo "HUDSON_HOME directory does not exist: $HUDSON_HOME"; |
|
| 32 | + if [ "$1" = "stop" ]; then exit 0; |
|
| 33 | + else exit 1; fi; } |
|
| 34 | +export HUDSON_HOME |
|
| 35 | + |
|
| 36 | +# Search usable Java. We do this because various reports indicated |
|
| 37 | +# that /usr/bin/java may not always point to Java 1.5 |
|
| 38 | +# see http://www.nabble.com/guinea-pigs-wanted-----Hudson-RPM-for-RedHat-Linux-td25673707.html |
|
| 39 | +for candidate in /usr/lib/jvm/java-1.6.0/bin/java /usr/lib/jvm/jre-1.6.0/bin/java /usr/lib/jvm/java-1.5.0/bin/java /usr/lib/jvm/jre-1.5.0/bin/java /usr/bin/java |
|
| 40 | +do |
|
| 41 | + [ -x "$HUDSON_JAVA_CMD" ] && break |
|
| 42 | + HUDSON_JAVA_CMD="$candidate" |
|
| 43 | +done |
|
| 44 | + |
|
| 45 | +JAVA_CMD="$HUDSON_JAVA_CMD $HUDSON_JAVA_OPTIONS -DHUDSON_HOME=$HUDSON_HOME -jar $HUDSON_WAR" |
|
| 46 | +PARAMS="--logfile=/var/log/hudson/hudson.log --daemon" |
|
| 47 | +[ -n "$HUDSON_PORT" ] && PARAMS="$PARAMS --httpPort=$HUDSON_PORT" |
|
| 48 | +[ -n "$HUDSON_DEBUG_LEVEL" ] && PARAMS="$PARAMS --debug=$HUDSON_DEBUG_LEVEL" |
|
| 49 | +[ -n "$HUDSON_HANDLER_STARTUP" ] && PARAMS="$PARAMS --handlerCountStartup=$HUDSON_HANDLER_STARTUP" |
|
| 50 | +[ -n "$HUDSON_HANDLER_MAX" ] && PARAMS="$PARAMS --handlerCountMax=$HUDSON_HANDLER_MAX" |
|
| 51 | +[ -n "$HUDSON_HANDLER_IDLE" ] && PARAMS="$PARAMS --handlerCountMaxIdle=$HUDSON_HANDLER_IDLE" |
|
| 52 | +[ -n "$HUDSON_ARGS" ] && PARAMS="$PARAMS $HUDSON_ARGS" |
|
| 53 | + |
|
| 54 | +if [ "$HUDSON_ENABLE_ACCESS_LOG" = "yes" ]; then |
|
| 55 | + PARAMS="$PARAMS --accessLoggerClassName=winstone.accesslog.SimpleAccessLogger --simpleAccessLogger.format=combined --simpleAccessLogger.file=/var/log/hudson/access_log" |
|
| 56 | +fi |
|
| 57 | + |
|
| 58 | +RETVAL=0 |
|
| 59 | + |
|
| 60 | +case "$1" in |
|
| 61 | + start) |
|
| 62 | + echo -n "Starting Hudson " |
|
| 63 | + daemon --user "$HUDSON_USER" --pidfile "$HUDSON_PID_FILE" "$JAVA_CMD" "$PARAMS" &> /var/tmp/hudson.log & |
|
| 64 | + RETVAL=$? |
|
| 65 | + if [ $RETVAL = 0 ]; then |
|
| 66 | + success |
|
| 67 | + echo > "$HUDSON_PID_FILE" # just in case we fail to find it |
|
| 68 | + MY_SESSION_ID=`/bin/ps h -o sess -p $$` |
|
| 69 | + # get PID |
|
| 70 | + /bin/ps hww -u "$HUDSON_USER" -o sess,ppid,pid,cmd | \ |
|
| 71 | + while read sess ppid pid cmd; do |
|
| 72 | + [ "$ppid" = 1 ] || continue |
|
| 73 | + # this test doesn't work because Hudson sets a new Session ID |
|
| 74 | + # [ "$sess" = "$MY_SESSION_ID" ] || continue |
|
| 75 | + echo "$cmd" | grep $HUDSON_WAR > /dev/null |
|
| 76 | + [ $? = 0 ] || continue |
|
| 77 | + # found a PID |
|
| 78 | + echo $pid > "$HUDSON_PID_FILE" |
|
| 79 | + done |
|
| 80 | + else |
|
| 81 | + failure |
|
| 82 | + fi |
|
| 83 | + echo |
|
| 84 | + ;; |
|
| 85 | + stop) |
|
| 86 | + echo -n "Shutting down Hudson " |
|
| 87 | + killproc hudson |
|
| 88 | + RETVAL=$? |
|
| 89 | + echo |
|
| 90 | + ;; |
|
| 91 | + try-restart|condrestart) |
|
| 92 | + if test "$1" = "condrestart"; then |
|
| 93 | + echo "${attn} Use try-restart ${done}(LSB)${attn} rather than condrestart ${warn}(RH)${norm}" |
|
| 94 | + fi |
|
| 95 | + $0 status |
|
| 96 | + if test $? = 0; then |
|
| 97 | + $0 restart |
|
| 98 | + else |
|
| 99 | + : # Not running is not a failure. |
|
| 100 | + fi |
|
| 101 | + ;; |
|
| 102 | + restart) |
|
| 103 | + $0 stop |
|
| 104 | + $0 start |
|
| 105 | + ;; |
|
| 106 | + force-reload) |
|
| 107 | + echo -n "Reload service Hudson " |
|
| 108 | + $0 try-restart |
|
| 109 | + ;; |
|
| 110 | + reload) |
|
| 111 | + $0 restart |
|
| 112 | + ;; |
|
| 113 | + status) |
|
| 114 | + status hudson |
|
| 115 | + RETVAL=$? |
|
| 116 | + ;; |
|
| 117 | + probe) |
|
| 118 | + ## Optional: Probe for the necessity of a reload, print out the |
|
| 119 | + ## argument to this init script which is required for a reload. |
|
| 120 | + ## Note: probe is not (yet) part of LSB (as of 1.9) |
|
| 121 | + |
|
| 122 | + test "$HUDSON_CONFIG" -nt "$HUDSON_PID_FILE" && echo reload |
|
| 123 | + ;; |
|
| 124 | + *) |
|
| 125 | + echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}" |
|
| 126 | + exit 1 |
|
| 127 | + ;; |
|
| 128 | +esac |
|
| 129 | +exit $RETVAL |
configuration/environments_scripts/build_server/files/usr/local/bin/launchhudsonslave
| ... | ... | @@ -0,0 +1,31 @@ |
| 1 | +#!/bin/bash |
|
| 2 | +# Enable sudo for user hudson for this script by adding the following to /etc/sudoers.d/hudsoncanlaunchec2instances: |
|
| 3 | +# hudson ALL = (root) NOPASSWD: /usr/local/bin/launchhudsonslave |
|
| 4 | +# hudson ALL = (root) NOPASSWD: /usr/local/bin/launchhudsonslave-java11 |
|
| 5 | +# hudson ALL = (root) NOPASSWD: /usr/local/bin/getLatestImageOfType.sh |
|
| 6 | +AWS=/bin/aws |
|
| 7 | +REGION=eu-west-1 |
|
| 8 | +HUDSON_SLAVE_AMI_ID=$( /usr/local/bin/getLatestImageOfType.sh hudson-slave ) |
|
| 9 | +echo Launching instance from AMI ${HUDSON_SLAVE_AMI_ID} ... |
|
| 10 | +instanceid=`$AWS ec2 run-instances --image-id $HUDSON_SLAVE_AMI_ID --count 1 --instance-type c5d.4xlarge --key-name Axel --security-groups "Sailing Analytics App" --region $REGION --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=Hudson Build Instance}]' --instance-initiated-shutdown-behavior terminate | tee /tmp/slavelaunch.out | jq .Instances[0].InstanceId | sed -e 's/"//g'` |
|
| 11 | +if [ "$instanceid" = "" ]; then |
|
| 12 | + echo Error launching instance |
|
| 13 | + exit 1 |
|
| 14 | +else |
|
| 15 | + echo Instance ID is $instanceid |
|
| 16 | + while [ "`$AWS ec2 describe-instances --region $REGION --instance-ids $instanceid | jq .Reservations[0].Instances[0].State.Name`" != "\"running\"" ]; do |
|
| 17 | + echo Instance $instanceid not running yet\; trying again... |
|
| 18 | + sleep 5 |
|
| 19 | + done |
|
| 20 | + echo Instance $instanceid seems running now |
|
| 21 | + private_ip=`$AWS ec2 describe-instances --region $REGION --instance-ids $instanceid | jq .Reservations[0].Instances[0].PrivateIpAddress | sed -e 's/"//g'` |
|
| 22 | + echo Probing for SSH on private IP $private_ip |
|
| 23 | + # Note: it's important to redirect stdin/stdout from/to /dev/null to ensure the Hudon master can properly connect stdin/stdout to the slave later |
|
| 24 | + while ! su - hudson -c "ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no hudson@$private_ip mkdir -p /home/hudson/workspace/___test___\; rmdir /home/hudson/workspace/___test___ </dev/null >/dev/null 2>/dev/null"; do |
|
| 25 | + echo SSH daemon not reachable yet. Trying again in a few seconds... |
|
| 26 | + sleep 10 |
|
| 27 | + done |
|
| 28 | + echo SSH daemon reached. State should be ready to connect to now. |
|
| 29 | + su - hudson -c "ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no hudson@$private_ip \"/opt/sapjvm_8/bin/java -jar slave.jar; sudo /sbin/shutdown -h now\"" |
|
| 30 | + $AWS ec2 terminate-instances --instance-ids $instanceid |
|
| 31 | +fi |
configuration/environments_scripts/build_server/files/usr/local/bin/launchhudsonslave-java11
| ... | ... | @@ -0,0 +1,31 @@ |
| 1 | +#!/bin/bash |
|
| 2 | +# Enable sudo for user hudson for this script by adding the following to /etc/sudoers.d/hudsoncanlaunchec2instances: |
|
| 3 | +# hudson ALL = (root) NOPASSWD: /usr/local/bin/launchhudsonslave |
|
| 4 | +# hudson ALL = (root) NOPASSWD: /usr/local/bin/launchhudsonslave-java11 |
|
| 5 | +# hudson ALL = (root) NOPASSWD: /usr/local/bin/getLatestImageOfType.sh |
|
| 6 | +AWS=/usr/bin/aws |
|
| 7 | +REGION=eu-west-1 |
|
| 8 | +HUDSON_SLAVE_AMI_ID=$( /usr/local/bin/getLatestImageOfType.sh hudson-slave-11 ) |
|
| 9 | +echo Launching instance from AMI ${HUDSON_SLAVE_AMI_ID} ... |
|
| 10 | +instanceid=`$AWS ec2 run-instances --image-id $HUDSON_SLAVE_AMI_ID --count 1 --instance-type c5d.4xlarge --key-name Axel --security-groups "Sailing Analytics App" --region $REGION --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=Hudson Ubuntu Slave Java 11}]' --instance-initiated-shutdown-behavior terminate | tee /tmp/slavelaunch.out | jq .Instances[0].InstanceId | sed -e 's/"//g'` |
|
| 11 | +if [ "$instanceid" = "" ]; then |
|
| 12 | + echo Error launching instance |
|
| 13 | + exit 1 |
|
| 14 | +else |
|
| 15 | + echo Instance ID is $instanceid |
|
| 16 | + while [ "`$AWS ec2 describe-instances --region $REGION --instance-ids $instanceid | jq .Reservations[0].Instances[0].State.Name`" != "\"running\"" ]; do |
|
| 17 | + echo Instance $instanceid not running yet\; trying again... |
|
| 18 | + sleep 5 |
|
| 19 | + done |
|
| 20 | + echo Instance $instanceid seems running now |
|
| 21 | + private_ip=`$AWS ec2 describe-instances --region $REGION --instance-ids $instanceid | jq .Reservations[0].Instances[0].PrivateIpAddress | sed -e 's/"//g'` |
|
| 22 | + echo Probing for SSH on private IP $private_ip |
|
| 23 | + # Note: it's important to redirect stdin/stdout from/to /dev/null to ensure the Hudon master can properly connect stdin/stdout to the slave later |
|
| 24 | + while ! su - hudson -c "ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no hudson@$private_ip mkdir -p /home/hudson/workspace/___test___\; rmdir /home/hudson/workspace/___test___ </dev/null >/dev/null 2>/dev/null"; do |
|
| 25 | + echo SSH daemon not reachable yet. Trying again in a few seconds... |
|
| 26 | + sleep 10 |
|
| 27 | + done |
|
| 28 | + echo SSH daemon reached. State should be ready to connect to now. |
|
| 29 | + su - hudson -c "ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no hudson@$private_ip \"/opt/sapjvm_8/bin/java -jar slave.jar; sudo /sbin/shutdown -h now\"" |
|
| 30 | + $AWS ec2 terminate-instances --instance-ids $instanceid |
|
| 31 | +fi |
configuration/environments_scripts/build_server/files/usr/local/bin/mountnvmeswap
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/mountnvmeswap |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/build_server/files/usr/local/bin/update_authorized_keys_for_landscape_managers
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/update_authorized_keys_for_landscape_managers |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/build_server/files/usr/local/bin/update_authorized_keys_for_landscape_managers_if_changed
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/update_authorized_keys_for_landscape_managers_if_changed |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/build_server/users/root/crontab-update-authorized-keys
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../crontabs/crontab-update-authorized-keys |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/files/etc/sudoers.d/trac_push_to_github_tools_sap
| ... | ... | @@ -0,0 +1,2 @@ |
| 1 | +# Enables the trac user, on all hosts, to execute commands as the root user. It can do this without a password but only to run the given command.
|
|
| 2 | +trac ALL = (root) NOPASSWD: /usr/local/bin/push_hyperspace_to_github_tools_sap |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/files/etc/sudoers.d/www-for-wiki-git
| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | +# Enables the apache user, on all hosts, to execute commands as the wiki user. This can be done without a password for
|
|
| 2 | +# running git, and all of its commands.
|
|
| 3 | +apache ALL = (wiki) NOPASSWD: /usr/bin/git |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/files/etc/systemd/system/mountnvmeswap.service
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/etc/systemd/system/mountnvmeswap.service |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/files/home/trac/bin/notify-unhealthy-mongodb
| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | +#!/bin/bash |
|
| 2 | +MONGODBS="mongodb://dbserver.internal.sapsailing.com:10201/?replicaSet=archive mongodb://dbserver.internal.sapsailing.com:10202/?replicaSet=slow mongodb://dbserver.internal.sapsailing.com:10203/?replicaSet=live" |
|
| 3 | +#MAILINGLIST="/home/trac/mailinglists/unhealthy-mongo-list" |
|
| 4 | +MAILINGLIST=$(cat /var/cache/landscapeManagersMailingList) |
|
| 5 | +for db in $MONGODBS; do |
|
| 6 | + echo "rs.status()" | mongo --quiet "$db" | grep '\("set"\)\|\("name"\)\|\("health"\)\|\("stateStr"\)' | sed -e 's/^.* : "\?//' -e 's/"\?,$//' | ( |
|
| 7 | + read replicaset |
|
| 8 | + while read server; do |
|
| 9 | + read health |
|
| 10 | + read state |
|
| 11 | + if [ "$health" != "1" ]; then |
|
| 12 | + echo "MongoDB replica set $replicaset unhealthy, node $server is in state $state\ |
|
| 13 | + |
|
| 14 | +Check status of $db urgently. To unsubscribe, change $MAILINGLIST on `hostname`." | mail -s "MongoDB Replica Set \"$replicaset\" Unhealthy" `cat "$MAILINGLIST"` |
|
| 15 | + fi |
|
| 16 | + done |
|
| 17 | + ) |
|
| 18 | +done |
configuration/environments_scripts/central_reverse_proxy/files/home/trac/bin/notifyAbout49erEuros2023Updates
| ... | ... | @@ -0,0 +1,62 @@ |
| 1 | +#!/usr/bin/env bash |
|
| 2 | + |
|
| 3 | +# set up some constants |
|
| 4 | +## Manage2Sail |
|
| 5 | +M2SURL="http://manage2sail.com" |
|
| 6 | +M2SEVENTID="fa9f5552-68a4-4712-b84b-ece0f36af8e8" |
|
| 7 | +M2SACCESSTOKEN="bDAv8CwsTM94ujZ" |
|
| 8 | +M2SSTRING=${M2SURL}"/api/public/links/event/"${M2SEVENTID}"?accesstoken="${M2SACCESSTOKEN}"&mediaType=json" |
|
| 9 | +## Local JSON |
|
| 10 | +LOCALJSON="/tmp/euros49er2023results.json" |
|
| 11 | +LOCALJSON_OLD=$LOCALJSON.old |
|
| 12 | +## Mails |
|
| 13 | +MAILINGLIST="/home/trac/mailinglists/euros49er2023" |
|
| 14 | +## Eventname |
|
| 15 | +EVENTNAME="49er Euros 2023" |
|
| 16 | +## Misc |
|
| 17 | +DOSENDMAIL=false |
|
| 18 | + |
|
| 19 | +### ROUTINE ### |
|
| 20 | +# get current json from manage2sail |
|
| 21 | +wget -O "$LOCALJSON" "$M2SSTRING" >/dev/null 2>&1 |
|
| 22 | +if [ -f "$LOCALJSON_OLD" ]; then |
|
| 23 | + # results previously downloaded; now compare |
|
| 24 | + # sort by manage2sail Regattas.Id |
|
| 25 | + echo Comparing $LOCALJSON and $LOCALJSON_OLD |
|
| 26 | + LOCALJSON_SORTED="${LOCALJSON}.sorted" |
|
| 27 | + LOCALJSON_OLD_SORTED="${LOCALJSON_OLD}.sorted" |
|
| 28 | + cat "$LOCALJSON" | jq -r '.Regattas|sort_by(.Name)' >"$LOCALJSON_SORTED" |
|
| 29 | + cat "$LOCALJSON_OLD" | jq -r '.Regattas|sort_by(.Name)' >"$LOCALJSON_OLD_SORTED" |
|
| 30 | + # filter only necessary values |
|
| 31 | + LOCALJSON_FORMATTED="${LOCALJSON}.formatted" |
|
| 32 | + LOCALJSON_OLD_FORMATTED="${LOCALJSON_OLD}.formatted" |
|
| 33 | + cat "$LOCALJSON_SORTED" | jq -r '.[]|{Final, LastPublishedRoundName, Published, Name, ClassName, Id}|select(.Published != null)' >"$LOCALJSON_FORMATTED" |
|
| 34 | + cat "$LOCALJSON_OLD_SORTED" | jq -r '.[]|{Final, LastPublishedRoundName, Published, Name, ClassName, Id}|select(.Published != null)' >"$LOCALJSON_OLD_FORMATTED" |
|
| 35 | + # diff sorted+formatted json |
|
| 36 | + diff --brief <(sort ${LOCALJSON_OLD_FORMATTED}) <(sort ${LOCALJSON_FORMATTED}) >/dev/null 2>&1 |
|
| 37 | + comp_value=$? |
|
| 38 | + if [ $comp_value -eq 1 ]; then |
|
| 39 | + echo "Found diff between $LOCALJSON_OLD_FORMATTED and $LOCALJSON_FORMATTED |
|
| 40 | + |
|
| 41 | +`diff -q "$LOCALJSON_OLD_FORMATTED" "$LOCALJSON_FORMATTED"` |
|
| 42 | + |
|
| 43 | +Sending mail." |
|
| 44 | + DOSENDMAIL=true |
|
| 45 | + fi |
|
| 46 | +fi |
|
| 47 | +if [ "$DOSENDMAIL" = "true" ]; then |
|
| 48 | + echo "`date` Sending mail to `cat "$MAILINGLIST"`" |
|
| 49 | + echo "$LOCALJSON has changed. Consider re-importing results for $EVENTNAME. Differences: |
|
| 50 | + |
|
| 51 | +------------------ |
|
| 52 | +`diff -U 6 "$LOCALJSON_OLD_FORMATTED" "$LOCALJSON_FORMATTED"` |
|
| 53 | +------------------ |
|
| 54 | + |
|
| 55 | +To unsubscribe, change $MAILINGLIST on `hostname`." | mail -s "SAP result import update: $EVENTNAME" `cat "$MAILINGLIST" | grep -v "^#"` |
|
| 56 | +fi |
|
| 57 | + |
|
| 58 | +# move to old |
|
| 59 | +mv "$LOCALJSON" "$LOCALJSON_OLD" |
|
| 60 | +# clean up temporary files |
|
| 61 | +#rm -rf "$LOCALJSON_SORTED" "$LOCALJSON_OLD_SORTED" "$LOCALJSON_FORMATTED" "$LOCALJSON_OLD_FORMATTED" |
|
| 62 | + |
configuration/environments_scripts/central_reverse_proxy/files/home/trac/git/hooks/post-receive
| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | +#!/bin/bash
|
|
| 2 | +# This hook executes once for the receive operation. It takes no arguments, but for each ref to be updated it receives on
|
|
| 3 | +# standard input a line of the format:
|
|
| 4 | +#
|
|
| 5 | +# <old-value> SP <new-value> SP <ref-name> LF
|
|
| 6 | +#
|
|
| 7 | +REFS=$( cat )
|
|
| 8 | +curl https://postreceive:ohfa7083.98@hudson.sapsailing.com/git/notifyCommit?url=ssh://trac@sapsailing.com/home/trac/git
|
|
| 9 | +if echo "$REFS" | awk '{ print $3; }' | grep "\<hyperspace\>"; then
|
|
| 10 | + push_hyperspace_to_github_tools_sap
|
|
| 11 | +fi
|
|
| 12 | +sudo /usr/local/bin/push_hyperspace_to_github_tools_sap
|
configuration/environments_scripts/central_reverse_proxy/files/home/trac/mailinglists/euros49er2023
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +axel.uhl@sap.com |
configuration/environments_scripts/central_reverse_proxy/files/usr/local/bin/downloadNewArchivedTracTracEvents.sh
| ... | ... | @@ -0,0 +1,23 @@ |
| 1 | +#!/bin/bash |
|
| 2 | + |
|
| 3 | +# Downloads all TracTrac event data based on ${GIT_ROOT}/configuration/tractrac-json-urls |
|
| 4 | +# into the target directory (specified as $1) for those event URLs whose specific folder |
|
| 5 | +# does not yet exist in the target directory. |
|
| 6 | +TARGET_DIR="${1}" |
|
| 7 | +if [[ $# -eq 1 ]]; then |
|
| 8 | + GIT_ROOT=/home/wiki/gitwiki |
|
| 9 | +else |
|
| 10 | + GIT_ROOT="${2}" |
|
| 11 | +fi |
|
| 12 | +JSON_URLS_FILE="${GIT_ROOT}/configuration/tractrac-json-urls" |
|
| 13 | +for i in `cat "${JSON_URLS_FILE}"`; do |
|
| 14 | + EVENT_DB="$( basename $( dirname ${i} ) )" |
|
| 15 | + if [ -d "${TARGET_DIR}/${EVENT_DB}" ]; then |
|
| 16 | + echo "Directory for event ${EVENT_DB} already found. Not downloading again. Use" |
|
| 17 | + echo " ${GIT_ROOT}/configuration/downloadTracTracEvent ${i} ${TARGET_DIR}" |
|
| 18 | + echo "to force an update." |
|
| 19 | + else |
|
| 20 | + echo "Did not find directory for event ${EVENT_DB} yet in ${TARGET_DIR}. Downloading..." |
|
| 21 | + ${GIT_ROOT}/configuration/downloadTracTracEvent "${i}" "${TARGET_DIR}" |
|
| 22 | + fi |
|
| 23 | +done |
configuration/environments_scripts/central_reverse_proxy/files/usr/local/bin/mail-events-on-my
| ... | ... | @@ -0,0 +1,2 @@ |
| 1 | +#!/bin/bash |
|
| 2 | +echo Number of events on my.sapsailing.com: $( ssh ec2-user@dbserver.internal.sapsailing.com "echo 'db.EVENTS.find().count()' | mongo --quiet 'mongodb://127.0.0.1:10203/my?replicaSet=live&retryWrites=true&readPreference=nearest' 2>/dev/null | tail -n 1" ) | mail -s "Events on my.sapsailing.com" axel.uhl@sap.com marcus.baur@sapsailing.com |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/files/usr/local/bin/mountnvmeswap
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/mountnvmeswap |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/files/usr/local/bin/notify-operators
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/notify-operators |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/files/usr/local/bin/push_hyperspace_to_github_tools_sap
| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | +#!/bin/bash
|
|
| 2 | +# Pushes the git bare to the hyperspace branch used for mobile builds. Ran as root to access the private
|
|
| 3 | +# access token but uses su to execute git commands as the trac user.
|
|
| 4 | +PAT=$( cat /root/github_tools_sap.pat )
|
|
| 5 | +su - trac -c "cd /home/trac/git; git push https://${PAT}@github.tools.sap/SAP-Sailing-Analytics/sapsailing hyperspace:hyperspace" |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/files/usr/local/bin/switchoverArchive.sh
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/switchoverArchive.sh |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/files/usr/local/bin/sync-repo-and-execute-cmd.sh
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/sync-repo-and-execute-cmd.sh |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/files/usr/local/bin/syncgit
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/syncgit |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/files/usr/local/bin/update-tractrac-urls-to-archive.sh
| ... | ... | @@ -0,0 +1,19 @@ |
| 1 | +#!/bin/bash |
|
| 2 | + |
|
| 3 | +if [[ $# -eq 0 ]]; then |
|
| 4 | + GIT_ROOT=/home/wiki/gitwiki |
|
| 5 | +else |
|
| 6 | + GIT_ROOT=$1 |
|
| 7 | +fi |
|
| 8 | +PATH_TO_TRAC_TRAC_URLS="configuration/tractrac-json-urls" |
|
| 9 | +urls=$(mongo --quiet "mongodb://dbserver.internal.sapsailing.com:10201/winddb?replicaSet=archive" --eval 'db.TRACTRAC_CONFIGURATIONS.find({}, {TT_CONFIG_JSON_URL : 1}).toArray()' | grep -v ObjectId | jq -r '.[].TT_CONFIG_JSON_URL' ) |
|
| 10 | +if [[ $urls == "null" || $? -ne 0 ]]; then |
|
| 11 | + echo "Mongo db returns null for tractrac url discovery" | notify-operators "MongoDB/tractrac urls issue" |
|
| 12 | + exit 1 |
|
| 13 | +else |
|
| 14 | + echo "${urls}" | sort -u >"${GIT_ROOT}/${PATH_TO_TRAC_TRAC_URLS}" |
|
| 15 | + cd "${GIT_ROOT}" |
|
| 16 | + git add "${GIT_ROOT}/${PATH_TO_TRAC_TRAC_URLS}" |
|
| 17 | + git commit -m "Updated tractrac-json-urls" |
|
| 18 | + git push |
|
| 19 | +fi |
configuration/environments_scripts/central_reverse_proxy/files/usr/local/bin/update_authorized_keys_for_landscape_managers
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/update_authorized_keys_for_landscape_managers |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/files/usr/local/bin/update_authorized_keys_for_landscape_managers_if_changed
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/update_authorized_keys_for_landscape_managers_if_changed |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/files/usr/local/bin/update_landscape_managers_mailing_list.sh
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/update_landscape_managers_mailing_list.sh |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/files/var/www/cgi-bin/github.cgi
| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | +#!/bin/bash |
|
| 2 | +echo " |
|
| 3 | +" |
|
| 4 | +BODY=$( cat ) |
|
| 5 | +echo "${BODY}" >/tmp/github-hook-body |
|
| 6 | +REF=$( echo "${BODY}" | jq -r '.ref' ) |
|
| 7 | +PUSHER=$( echo "${BODY}" | jq -r '.pusher.email' ) |
|
| 8 | +logger -t github-cgi "ref ia $REF, pusher was $PUSHER" |
|
| 9 | +# For testing: |
|
| 10 | +#if [ "${PUSHER}" = "axel.uhl@sap.com" -a "${REF}" = "refs/heads/translation" ]; then |
|
| 11 | +if [ "${PUSHER}" = "tmsatsls+github.tools.sap_service-tip-git@sap.com" -a "${REF}" = "refs/heads/translation" ]; then |
|
| 12 | + echo "Identified a push to refs/heads/translation by ${PUSHER}." |
|
| 13 | + echo "Fetching translation branch from github.tools.sap and pushing it to ssh://trac@sapsailing.com/home/trac/git" |
|
| 14 | + logger -t github-cgi "fetching translation branch from github.tools.sap and pushing it to ssh://trac@sapsailing.com/home/trac/git" |
|
| 15 | + cd /home/wiki/gitwiki |
|
| 16 | + sudo -u wiki git fetch sapsailing translation:translation 2>&1 |
|
| 17 | + sudo -u wiki git push origin translation:translation 2>&1 |
|
| 18 | +else |
|
| 19 | + echo "Either pusher was not tmsatsls+github.tools.sap_service-tip-git@sap.com or ref was not refs/heads/translation. Not triggering." |
|
| 20 | +fi |
configuration/environments_scripts/central_reverse_proxy/users/root/crontab-docker-registry-gc
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../crontabs/crontab-docker-registry-gc |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/users/root/crontab-mail-events-on-my
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../crontabs/crontab-mail-events-on-my |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/users/root/crontab-switchoverArchive
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../crontabs/crontab-switchoverArchive |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/users/root/crontab-update-authorized-keys
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../crontabs/crontab-update-authorized-keys |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/users/root/crontab-update-landscape-managers-mailing-list
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../crontabs/crontab-update-landscape-managers-mailing-list |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/users/trac/crontab-manage2sail-example
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../crontabs/crontab-manage2sail-example |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/users/trac/crontab-mongo-health-check
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../crontabs/crontab-mongo-health-check |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/users/wiki/crontab-download-new-archived-trac-trac-events
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../crontabs/crontab-download-new-archived-trac-trac-events |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/users/wiki/crontab-syncgit
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../crontabs/crontab-syncgit |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/central_reverse_proxy/users/wiki/crontab-update-trac-trac-urls
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../crontabs/crontab-update-trac-trac-urls |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/hudson_slave/README
| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | +Deploy the .mount and .service units to /etc/systemd/system. |
|
| 2 | +Deploy the imageupgrade script to /usr/local/bin, |
|
| 3 | +furthermore the ../imageupgrade_functions.sh has to go to /usr/local/bin, best as symbolic link to files in git. |
|
| 4 | + |
|
| 5 | +See the script in wiki/howto/development/ci.md that shows how to set up such an instance from scratch. |
configuration/environments_scripts/hudson_slave/files/etc/systemd/system/imageupgrade.service
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/etc/systemd/system/imageupgrade.service |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/hudson_slave/files/etc/systemd/system/mounthudsonworkspace.service
| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | +[Unit] |
|
| 2 | +Description=Mount Hudson Workspace |
|
| 3 | +After=ephemeral-data.mount |
|
| 4 | + |
|
| 5 | +[Service] |
|
| 6 | +Type=oneshot |
|
| 7 | +ExecStart=/usr/local/bin/mounthudsonworkspace |
|
| 8 | +RemainAfterExit=true |
|
| 9 | +StandardOutput=journal |
|
| 10 | + |
|
| 11 | +[Install] |
|
| 12 | +WantedBy=multi-user.target |
configuration/environments_scripts/hudson_slave/files/usr/local/bin/imageupgrade
| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | +#!/bin/bash |
|
| 2 | + |
|
| 3 | +# Script to deploy on an instance that has an ephemeral volume as /dev/nvme0n1 (adjust env var PARTITION if different) |
|
| 4 | +# Ensures the partition is xfs-formatted, any existing partition contents will be overwritten if formatted otherwise. |
|
| 5 | +# An existing xfs partition will be left alone. |
|
| 6 | +. imageupgrade_functions.sh |
|
| 7 | + |
|
| 8 | +get_ec2_user_data() { |
|
| 9 | + /usr/bin/ec2metadata --user |
|
| 10 | +} |
|
| 11 | + |
|
| 12 | +METADATA=$( get_ec2_user_data ) |
|
| 13 | +echo "Metadata: ${METADATA}" |
|
| 14 | +if echo "${METADATA}" | grep -q "^image-upgrade$"; then |
|
| 15 | + echo "Image upgrade..." |
|
| 16 | + LOGON_USER_HOME=/home/ubuntu |
|
| 17 | + run_apt_update_upgrade |
|
| 18 | + download_and_install_latest_sap_jvm_8 |
|
| 19 | + run_git_pull |
|
| 20 | + finalize |
|
| 21 | +fi |
configuration/environments_scripts/hudson_slave/files/usr/local/bin/mounthudsonworkspace
| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | +#!/bin/bash |
|
| 2 | +# Expects an ephemeral volume to have been mounted at /ephemeral/data and |
|
| 3 | +# mounts that with a "bind" mount to /home/hudson/workspace. |
|
| 4 | +# The directory is then chown/chgrp'ed to hudson |
|
| 5 | +if [ -e /dev/nvme1n1 ]; then |
|
| 6 | + mkfs.ext4 /dev/nvme1n1 |
|
| 7 | + mount /dev/nvme1n1 /home/hudson/workspace |
|
| 8 | +else |
|
| 9 | + mount -o bind /ephemeral/data /home/hudson/workspace |
|
| 10 | +fi |
|
| 11 | +chgrp hudson /home/hudson/workspace |
|
| 12 | +chown hudson /home/hudson/workspace |
configuration/environments_scripts/mongo_instance_setup/README
| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | +Deploy the .mount and .service units to /etc/systemd/system. |
|
| 2 | +Deploy the ephemeralvolume and the patch-mongo-replicaset-name-from-ec2-metadata script to /usr/local/bin, |
|
| 3 | +furthermore the ../imageupgrade_functions.sh has to go to /usr/local/bin. |
|
| 4 | +Deploy mongod.conf to /etc and make sure that /root has a+r and a+x permissions because |
|
| 5 | +otherwise the mongod user won't be able to read through the symbolic link |
|
| 6 | +Link mongodb to /etc/logrotate.d |
|
| 7 | +Link the crontab-mongo, in configuration/crontabs/environments, to /root/crontab and run "crontab crontab" as root. |
|
| 8 | + |
|
| 9 | +Run with optional EC2 user detail, e.g., as follows: |
|
| 10 | + |
|
| 11 | + REPLICA_SET_NAME=archive |
|
| 12 | + REPLICA_SET_PRIMARY=dbserver.internal.sapsailing.com:10201 |
|
| 13 | + |
|
| 14 | +This will automatically patch /etc/mongod.conf such that the replSetName property |
|
| 15 | +is set to the value of REPLICA_SET_NAME. Then, the instance will be added to |
|
| 16 | +the REPLICA_SET_PRIMARY's replica set. |
configuration/environments_scripts/mongo_instance_setup/files/etc/logrotate.d/mongodb
| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | +compress |
|
| 2 | +/var/log/mongodb/mongod.log |
|
| 3 | +{ |
|
| 4 | + rotate 5 |
|
| 5 | + weekly |
|
| 6 | + postrotate |
|
| 7 | + /usr/bin/killall -SIGUSR1 mongod |
|
| 8 | + endscript |
|
| 9 | +} |
configuration/environments_scripts/mongo_instance_setup/files/etc/mongod.conf
| ... | ... | @@ -0,0 +1,48 @@ |
| 1 | +# mongod.conf |
|
| 2 | + |
|
| 3 | +# for documentation of all options, see: |
|
| 4 | +# http://docs.mongodb.org/manual/reference/configuration-options/ |
|
| 5 | + |
|
| 6 | +# where to write logging data. |
|
| 7 | +systemLog: |
|
| 8 | + destination: file |
|
| 9 | + logAppend: true |
|
| 10 | + path: /var/log/mongodb/mongod.log |
|
| 11 | + |
|
| 12 | +# Where and how to store data. |
|
| 13 | +storage: |
|
| 14 | + dbPath: /var/lib/mongo |
|
| 15 | + journal: |
|
| 16 | + enabled: true |
|
| 17 | + directoryPerDB: true |
|
| 18 | +# engine: |
|
| 19 | +# mmapv1: |
|
| 20 | +# wiredTiger: |
|
| 21 | + |
|
| 22 | +# how the process runs |
|
| 23 | +processManagement: |
|
| 24 | + fork: true # fork and run in background |
|
| 25 | + pidFilePath: /var/run/mongodb/mongod.pid # location of pidfile |
|
| 26 | + timeZoneInfo: /usr/share/zoneinfo |
|
| 27 | + |
|
| 28 | +# network interfaces |
|
| 29 | +net: |
|
| 30 | + port: 27017 |
|
| 31 | +# bindIp: 127.0.0.1 # Listen to local interface only, comment to listen on all interfaces. |
|
| 32 | +# bindIp: 172.31.33.146 |
|
| 33 | + bindIp: 0.0.0.0 |
|
| 34 | + |
|
| 35 | +#security: |
|
| 36 | + |
|
| 37 | +#operationProfiling: |
|
| 38 | + |
|
| 39 | +replication: |
|
| 40 | + replSetName: "live" |
|
| 41 | + |
|
| 42 | +#sharding: |
|
| 43 | + |
|
| 44 | +## Enterprise-Only Options |
|
| 45 | + |
|
| 46 | +#auditLog: |
|
| 47 | + |
|
| 48 | +#snmp: |
configuration/environments_scripts/mongo_instance_setup/files/etc/systemd/system/chownvarlibmongo.service
| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | +[Unit] |
|
| 2 | +Description=Ensures all files under /var/lib/mongo are owned by mongod user/group |
|
| 3 | +Requires=ephemeralvolume.service |
|
| 4 | +After=ephemeralvolume.service |
|
| 5 | +Before=mongod.service |
|
| 6 | + |
|
| 7 | +[Install] |
|
| 8 | +RequiredBy=mongod.service |
|
| 9 | + |
|
| 10 | +[Service] |
|
| 11 | +Type=oneshot |
|
| 12 | +RemainAfterExit=true |
|
| 13 | +ExecStart=/bin/chown -R mongod /var/lib/mongo/ |
|
| 14 | +ExecStart=/bin/chgrp -R mongod /var/lib/mongo/ |
configuration/environments_scripts/mongo_instance_setup/files/etc/systemd/system/ephemeralvolume.service
| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | +[Unit] |
|
| 2 | +Description=Ensures /dev/nvme0n1 or /dev/xvdb is XFS-formatted |
|
| 3 | +Requires=-.mount cloud-init.service network.service |
|
| 4 | +After=-.mount cloud-init.service network.service |
|
| 5 | + |
|
| 6 | +[Install] |
|
| 7 | + |
|
| 8 | +[Service] |
|
| 9 | +Type=oneshot |
|
| 10 | +RemainAfterExit=true |
|
| 11 | +ExecStart=/usr/local/bin/ephemeralvolume |
configuration/environments_scripts/mongo_instance_setup/files/etc/systemd/system/mongo-replica-set.service
| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | +[Unit] |
|
| 2 | +Description=If REPLICA_SET_NAME EC2 user data is provided, add this node to the replica set of REPLICA_SET_PRIMARY |
|
| 3 | +Requires=mongod.service |
|
| 4 | +After=mongod.service |
|
| 5 | +Requires=cloud-init.service |
|
| 6 | +After=cloud-init.service |
|
| 7 | + |
|
| 8 | +[Install] |
|
| 9 | +WantedBy=multi-user.target |
|
| 10 | + |
|
| 11 | +[Service] |
|
| 12 | +Type=oneshot |
|
| 13 | +RemainAfterExit=true |
|
| 14 | +ExecStart=/usr/local/bin/add-as-replica |
|
| 15 | +ExecStop=/usr/local/bin/remove-as-replica |
|
| 16 | +TimeoutStopSec=120s |
configuration/environments_scripts/mongo_instance_setup/files/etc/systemd/system/patch-mongo-replicaset-name-from-ec2-metadata.service
| ... | ... | @@ -0,0 +1,15 @@ |
| 1 | +[Unit] |
|
| 2 | +Description=Check EC2 metadata for MongoDB Replica Set Name and patch /etc/mongod.conf accordingly |
|
| 3 | +Requires=ephemeralvolume.service |
|
| 4 | +After=ephemeralvolume.service |
|
| 5 | +Requires=cloud-init.service |
|
| 6 | +After=cloud-init.service |
|
| 7 | +Before=mongod.service |
|
| 8 | + |
|
| 9 | +[Install] |
|
| 10 | +RequiredBy=mongod.service |
|
| 11 | + |
|
| 12 | +[Service] |
|
| 13 | +Type=oneshot |
|
| 14 | +RemainAfterExit=true |
|
| 15 | +ExecStart=/usr/local/bin/patch-mongo-replicaset-name-from-ec2-metadata |
configuration/environments_scripts/mongo_instance_setup/files/usr/local/bin/add-as-replica
| ... | ... | @@ -0,0 +1,25 @@ |
| 1 | +#!/bin/bash |
|
| 2 | +user_data=$( ec2-metadata -d | sed -e 's/^user-data: //' ) |
|
| 3 | +if echo ${user_data} | grep -q "^image-upgrade$"; then |
|
| 4 | + echo "Image upgrade... didn't expect to get this far because ephemeralvolume should have triggered upgrade and shutdown. Not registering MongoDB replica" |
|
| 5 | +else |
|
| 6 | + eval ${user_data} |
|
| 7 | + if [ -z "$REPLICA_SET_NAME" ]; then |
|
| 8 | + REPLICA_SET_NAME=live |
|
| 9 | + fi |
|
| 10 | + if [ -z "$REPLICA_SET_PRIMARY" ]; then |
|
| 11 | + REPLICA_SET_PRIMARY=mongo0.internal.sapsailing.com:27017 |
|
| 12 | + fi |
|
| 13 | + if [ -z "$REPLICA_SET_PRIORITY" ]; then |
|
| 14 | + REPLICA_SET_PRIORITY=1 |
|
| 15 | + fi |
|
| 16 | + if [ -z "$REPLICA_SET_VOTES" ]; then |
|
| 17 | + REPLICA_SET_VOTES=0 |
|
| 18 | + fi |
|
| 19 | + if [ \! -z "REPLICA_SET_PRIMARY" ]; then |
|
| 20 | + IP=$(ec2-metadata -o | sed -e 's/^local-ipv4: //') |
|
| 21 | + echo "rs.add({host: \"$IP:27017\", priority: $REPLICA_SET_PRIORITY, votes: $REPLICA_SET_VOTES})" | mongo "mongodb://$REPLICA_SET_PRIMARY/?replicaSet=$REPLICA_SET_NAME&retryWrites=true" |
|
| 22 | + else |
|
| 23 | + echo "rs.initiate()" | mongo |
|
| 24 | + fi |
|
| 25 | +fi |
configuration/environments_scripts/mongo_instance_setup/files/usr/local/bin/ephemeralvolume
| ... | ... | @@ -0,0 +1,33 @@ |
| 1 | +#!/bin/bash |
|
| 2 | + |
|
| 3 | +# Script to deploy on an instance that has an ephemeral volume as /dev/nvme0n1 (adjust env var PARTITION if different) |
|
| 4 | +# Ensures the partition is xfs-formatted, any existing partition contents will be overwritten if formatted otherwise. |
|
| 5 | +# An existing xfs partition will be left alone. |
|
| 6 | + |
|
| 7 | +METADATA=$( /bin/ec2-metadata -d | sed -e 's/^user-data: //' ) |
|
| 8 | +echo "Metadata: ${METADATA}" |
|
| 9 | +if echo "${METADATA}" | grep -q "^image-upgrade$"; then |
|
| 10 | + echo "Image upgrade; not trying to mount/format ephemeral volume; calling imageupgrade.sh instead..." |
|
| 11 | + imageupgrade.sh |
|
| 12 | +else |
|
| 13 | + echo "No image upgrade; looking for ephemeral volume and trying to format with xfs..." |
|
| 14 | + PARTITION=/dev/nvme0n1 |
|
| 15 | + if [ \! -e $PARTITION ]; then |
|
| 16 | + PARTITION=/dev/xvdb |
|
| 17 | + fi |
|
| 18 | + if [ \! -e $PARTITION ]; then |
|
| 19 | + echo "Neither /dev/nvme0n1 nor /dev/xvdb partition found; not formatting/mounting ephemeral volume" |
|
| 20 | + elif cat /proc/mounts | awk '{print $1;}' | grep "${PARTITION}"; then |
|
| 21 | + echo "Partition ${PARTITION} already mounted; not formatting/mounting ephemeral volume" |
|
| 22 | + else |
|
| 23 | + FSTYPE=$(blkid -p $PARTITION -s TYPE -o value) |
|
| 24 | + if [ "$FSTYPE" != "xfs" ]; then |
|
| 25 | + echo FSTYPE was "$FSTYPE" but should have been xfs. Formatting $PARTITION... |
|
| 26 | + mkfs.xfs -f $PARTITION |
|
| 27 | + else |
|
| 28 | + echo FSTYPE was "$FSTYPE" which is just right :-\) |
|
| 29 | + fi |
|
| 30 | + # mount the thing to /var/lib/mongo |
|
| 31 | + mount $PARTITION /var/lib/mongo |
|
| 32 | + fi |
|
| 33 | +fi |
configuration/environments_scripts/mongo_instance_setup/files/usr/local/bin/imageupgrade.sh
| ... | ... | @@ -0,0 +1,25 @@ |
| 1 | +#!/bin/bash |
|
| 2 | + |
|
| 3 | +# Upgrades the AWS EC2 MongoDB instance that this script is assumed to be executed on. |
|
| 4 | +# The steps are as follows: |
|
| 5 | + |
|
| 6 | +. imageupgrade_functions.sh |
|
| 7 | + |
|
| 8 | +run_git_pull_root() { |
|
| 9 | + echo "Pulling git to /root/code" >>/var/log/sailing.err |
|
| 10 | + cd /root/code |
|
| 11 | + git pull |
|
| 12 | +} |
|
| 13 | + |
|
| 14 | +clean_mongo_pid() { |
|
| 15 | + rm -f /var/run/mongodb/mongod.pid |
|
| 16 | +} |
|
| 17 | + |
|
| 18 | +LOGON_USER_HOME=/home/ec2-user |
|
| 19 | + |
|
| 20 | +run_yum_update |
|
| 21 | +run_git_pull_root |
|
| 22 | +clean_startup_logs |
|
| 23 | +update_root_crontab |
|
| 24 | +clean_mongo_pid |
|
| 25 | +finalize |
configuration/environments_scripts/mongo_instance_setup/files/usr/local/bin/imageupgrade_functions.sh
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/imageupgrade_functions.sh |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/mongo_instance_setup/files/usr/local/bin/patch-mongo-replicaset-name-from-ec2-metadata
| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | +#!/bin/bash |
|
| 2 | +REPLICA_SET_NAME=$(ec2-metadata | grep REPLICA_SET_NAME | sed -e 's/^user-data: //' | sed -e 's/^REPLICA_SET_NAME=//') |
|
| 3 | +echo Replica set name: $REPLICA_SET_NAME |
|
| 4 | +if [ \! -z "$REPLICA_SET_NAME" ]; then |
|
| 5 | + echo "Not empty. Patching /etc/mongod.conf..." |
|
| 6 | + sed -i -e "s/replSetName: .*$/replSetName: $REPLICA_SET_NAME/" /etc/mongod.conf |
|
| 7 | + echo "Done" |
|
| 8 | +fi |
configuration/environments_scripts/mongo_instance_setup/files/usr/local/bin/remove-as-replica
| ... | ... | @@ -0,0 +1,6 @@ |
| 1 | +#!/bin/bash |
|
| 2 | +eval $( ec2-metadata -d | sed -e 's/^user-data: //' ) |
|
| 3 | +if [ \! -z "REPLICA_SET_PRIMARY" ]; then |
|
| 4 | + IP=$(ec2-metadata -o | sed -e 's/^local-ipv4: //') |
|
| 5 | + echo "rs.remove(\"$IP:27017\")" | mongo "mongodb://$REPLICA_SET_PRIMARY/?replicaSet=$REPLICA_SET_NAME&retryWrites=true" |
|
| 6 | +fi |
configuration/environments_scripts/mongo_instance_setup/files/usr/local/bin/update_authorized_keys_for_landscape_managers
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/update_authorized_keys_for_landscape_managers |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/mongo_instance_setup/files/usr/local/bin/update_authorized_keys_for_landscape_managers_if_changed
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/update_authorized_keys_for_landscape_managers_if_changed |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/mongo_instance_setup/users/root/crontab-update-authorized-keys
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../crontabs/crontab-update-authorized-keys |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/mysql_instance_setup/files/usr/local/bin/update_authorized_keys_for_landscape_managers
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/update_authorized_keys_for_landscape_managers |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/mysql_instance_setup/files/usr/local/bin/update_authorized_keys_for_landscape_managers_if_changed
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/update_authorized_keys_for_landscape_managers_if_changed |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/mysql_instance_setup/users/ec2-user/crontab-update-authorized-keys
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../crontabs/crontab-update-authorized-keys |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/rabbitmq_instance_setup/files/usr/local/bin/update_authorized_keys_for_landscape_managers
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/update_authorized_keys_for_landscape_managers |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/rabbitmq_instance_setup/files/usr/local/bin/update_authorized_keys_for_landscape_managers_if_changed
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/update_authorized_keys_for_landscape_managers_if_changed |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/rabbitmq_instance_setup/users/admin/crontab-update-authorized-keys
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../crontabs/crontab-update-authorized-keys |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/repo/etc/init.d
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +rc.d/init.d |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/repo/etc/rc.d/init.d/mountnvmeswap.initd
| ... | ... | @@ -0,0 +1,41 @@ |
| 1 | +#!/bin/bash |
|
| 2 | +# |
|
| 3 | +# mountnvmeswap Formats and mounts a yet unpartitioned NVMe volume as swap |
|
| 4 | +# |
|
| 5 | +# chkconfig: 2345 95 10 |
|
| 6 | +# description: Formats and mounts a yet unpartitioned NVMe volume as swap |
|
| 7 | +# |
|
| 8 | +### BEGIN INIT INFO |
|
| 9 | +# Provides: mountnvmeswap |
|
| 10 | +# Required-Start: $local_fs $network |
|
| 11 | +# Should-Start: |
|
| 12 | +# Required-Stop: |
|
| 13 | +# Should-Stop: |
|
| 14 | +# Default-Start: 2 3 4 5 |
|
| 15 | +# Default-Stop: 0 1 6 |
|
| 16 | +# Short-Description: Formats and mounts a yet unpartitioned NVMe volume as swap |
|
| 17 | +# Description: Formats and mounts a yet unpartitioned NVMe volume as swap |
|
| 18 | +### END INIT INFO |
|
| 19 | + |
|
| 20 | +# Source function library. |
|
| 21 | +. /etc/init.d/functions |
|
| 22 | + |
|
| 23 | +RETVAL=0 |
|
| 24 | + |
|
| 25 | +# See how we were called. |
|
| 26 | +case "$1" in |
|
| 27 | + start) |
|
| 28 | + /usr/local/bin/mountnvmeswap |
|
| 29 | + ;; |
|
| 30 | + stop) |
|
| 31 | + ;; |
|
| 32 | + status) |
|
| 33 | + swapon -s | grep /dev/nvme0n1 |
|
| 34 | + RETVAL=$? |
|
| 35 | + ;; |
|
| 36 | + *) |
|
| 37 | + echo $"Usage: $0 {start|status|stop}" |
|
| 38 | + RETVAL=3 |
|
| 39 | +esac |
|
| 40 | + |
|
| 41 | +exit $RETVAL |
configuration/environments_scripts/repo/etc/systemd/system/imageupgrade.service
| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | +[Unit] |
|
| 2 | +Description=Check for image-upgrade EC2 user data and triggers an image upgrade if found |
|
| 3 | +Requires=-.mount cloud-init.service |
|
| 4 | +After=-.mount cloud-init.service networking.service systemd-networkd.service |
|
| 5 | + |
|
| 6 | +[Install] |
|
| 7 | +WantedBy=multi-user.target |
|
| 8 | + |
|
| 9 | +[Service] |
|
| 10 | +Type=oneshot |
|
| 11 | +RemainAfterExit=true |
|
| 12 | +ExecStart=/usr/local/bin/imageupgrade |
configuration/environments_scripts/repo/etc/systemd/system/mountnvmeswap.service
| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | +[Unit] |
|
| 2 | +Description=An unformatted /dev/nvme0n1 is turned into swap space |
|
| 3 | +Requires=-.mount |
|
| 4 | +After=-.mount |
|
| 5 | + |
|
| 6 | +[Install] |
|
| 7 | +RequiredBy=multi-user.target |
|
| 8 | + |
|
| 9 | +[Service] |
|
| 10 | +Type=oneshot |
|
| 11 | +RemainAfterExit=true |
|
| 12 | +ExecStart=/usr/local/bin/mountnvmeswap |
configuration/environments_scripts/repo/usr/local/bin/getLatestImageOfType.sh
| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | +#!/bin/bash |
|
| 2 | +imageType="$1" |
|
| 3 | +aws ec2 describe-images --filter Name=tag:image-type,Values=${imageType} | jq --raw-output '.Images | sort_by(.CreationDate) | .[].ImageId' | tail -n 1 |
configuration/environments_scripts/repo/usr/local/bin/imageupgrade_functions.sh
| ... | ... | @@ -0,0 +1,103 @@ |
| 1 | +#!/bin/bash |
|
| 2 | + |
|
| 3 | +# Upgrades the AWS EC2 instance that this script is assumed to be executed on. |
|
| 4 | +# The steps are as follows: |
|
| 5 | + |
|
| 6 | +REBOOT_INDICATOR=/var/lib/sailing/is-rebooted |
|
| 7 | +LOGON_USER_HOME=/root |
|
| 8 | + |
|
| 9 | +run_yum_update() { |
|
| 10 | + echo "Updating packages using yum" >>/var/log/sailing.err |
|
| 11 | + yum -y update |
|
| 12 | +} |
|
| 13 | + |
|
| 14 | +run_apt_update_upgrade() { |
|
| 15 | + echo "Updating packages using apt" >>/var/log/sailing.err |
|
| 16 | + apt-get -y update; apt-get -y upgrade |
|
| 17 | + apt-get -y install linux-image-cloud-amd64 |
|
| 18 | + apt-get -y autoremove |
|
| 19 | +} |
|
| 20 | + |
|
| 21 | +run_git_pull() { |
|
| 22 | + echo "Pulling git to /home/sailing/code" >>/var/log/sailing.err |
|
| 23 | + su - sailing -c "cd code; git pull" |
|
| 24 | +} |
|
| 25 | + |
|
| 26 | +download_and_install_latest_sap_jvm_8() { |
|
| 27 | + echo "Downloading and installing latest SAP JVM 8 to /opt/sapjvm_8" >>/var/log/sailing.err |
|
| 28 | + vmpath=$( curl -s --cookie eula_3_1_agreed=tools.hana.ondemand.com/developer-license-3_1.txt https://tools.hana.ondemand.com | grep additional/sapjvm-8\..*-linux-x64.zip | head -1 | sed -e 's/^.*a href="\(additional\/sapjvm-8\..*-linux-x64\.zip\)".*/\1/' ) |
|
| 29 | + if [ -n "${vmpath}" ]; then |
|
| 30 | + echo "Found VM version ${vmpath}; upgrading installation at /opt/sapjvm_8" >>/var/log/sailing.err |
|
| 31 | + if [ -z "${TMP}" ]; then |
|
| 32 | + TMP=/tmp |
|
| 33 | + fi |
|
| 34 | + echo "Downloading SAP JVM 8 as ZIP file to ${TMP}/sapjvm8-linux-x64.zip" >>/var/log/sailing.err |
|
| 35 | + curl --cookie eula_3_1_agreed=tools.hana.ondemand.com/developer-license-3_1.txt "https://tools.hana.ondemand.com/${vmpath}" > ${TMP}/sapjvm8-linux-x64.zip 2>>/var/log/sailing.err |
|
| 36 | + cd /opt |
|
| 37 | + rm -rf sapjvm_8 |
|
| 38 | + if [ -f SIGNATURE.SMF ]; then |
|
| 39 | + rm -f SIGNATURE.SMF |
|
| 40 | + fi |
|
| 41 | + unzip ${TMP}/sapjvm8-linux-x64.zip >>/var/log/sailing.err |
|
| 42 | + rm -f ${TMP}/sapjvm8-linux-x64.zip |
|
| 43 | + rm -f SIGNATURE.SMF |
|
| 44 | + else |
|
| 45 | + echo "Did not find SAP JVM 8 at tools.hana.ondemand.com; not trying to upgrade" >>/var/log/sailing.err |
|
| 46 | + fi |
|
| 47 | +} |
|
| 48 | + |
|
| 49 | +clean_logrotate_target() { |
|
| 50 | + echo "Clearing logrorate-targets" >>/var/log/sailing.err |
|
| 51 | + rm -rf /var/log/logrotate-target/* |
|
| 52 | +} |
|
| 53 | + |
|
| 54 | +clean_httpd_logs() { |
|
| 55 | + echo "Clearing httpd logs" >>/var/log/sailing.err |
|
| 56 | + service httpd stop |
|
| 57 | + rm -rf /var/log/httpd/* |
|
| 58 | + rm -f /etc/httpd/conf.d/001-internals.conf |
|
| 59 | +} |
|
| 60 | + |
|
| 61 | +clean_startup_logs() { |
|
| 62 | + echo "Clearing bootstrap logs" >>/var/log/sailing.err |
|
| 63 | + rm -f /var/log/sailing* |
|
| 64 | + # Ensure that upon the next boot the reboot indicator is not present, indicating that it's the first boot |
|
| 65 | + rm "${REBOOT_INDICATOR}" |
|
| 66 | +} |
|
| 67 | + |
|
| 68 | +clean_servers_dir() { |
|
| 69 | + rm -rf /home/sailing/servers/* |
|
| 70 | +} |
|
| 71 | + |
|
| 72 | +#DEPRECATED |
|
| 73 | +update_root_crontab() { |
|
| 74 | + # The following assumes that /root/crontab is a symbolic link to /home/sailing/code/configuration/crontabs/<the crontab appropriate |
|
| 75 | + # to the environment or user> |
|
| 76 | + # which has previously been updated by a git pull: |
|
| 77 | + cd /root |
|
| 78 | + crontab crontab |
|
| 79 | +} |
|
| 80 | + |
|
| 81 | +clean_root_ssh_dir_and_tmp() { |
|
| 82 | + echo "Cleaning up ${LOGON_USER_HOME}/.ssh" >>/var/log/sailing.err |
|
| 83 | + rm -rf ${LOGON_USER_HOME}/.ssh/* |
|
| 84 | + rm -f /var/run/last_change_aws_landscape_managers_ssh_keys |
|
| 85 | + rm -rf /tmp/image-upgrade-finished |
|
| 86 | +} |
|
| 87 | + |
|
| 88 | +get_ec2_user_data() { |
|
| 89 | + /opt/aws/bin/ec2-metadata -d | sed -e 's/^user-data: //' |
|
| 90 | +} |
|
| 91 | + |
|
| 92 | +finalize() { |
|
| 93 | + # Finally, shut down the node unless "no-shutdown" was provided in the user data, so that a new AMI can be constructed cleanly |
|
| 94 | + if get_ec2_user_data | grep "^no-shutdown$"; then |
|
| 95 | + echo "Shutdown disabled by no-shutdown option in user data. Remember to clean /root/.ssh when done." |
|
| 96 | + touch /tmp/image-upgrade-finished |
|
| 97 | + else |
|
| 98 | + # Only clean ${LOGON_USER_HOME}/.ssh directory and /tmp/image-upgrade-finished if the next step is shutdown / image creation |
|
| 99 | + clean_root_ssh_dir_and_tmp |
|
| 100 | + rm -f /var/log/sailing.err |
|
| 101 | + shutdown -h now & |
|
| 102 | + fi |
|
| 103 | +} |
configuration/environments_scripts/repo/usr/local/bin/mountnvmeswap
| ... | ... | @@ -0,0 +1,31 @@ |
| 1 | +#!/bin/bash |
|
| 2 | + |
|
| 3 | +# Script to deploy on an instance that has an ephemeral volume as /dev/nvme0n1 (adjust env var EPHEMERAL_VOLUME if different) |
|
| 4 | +# Ensures the partition is xfs-formatted, any existing partition contents will be overwritten if formatted otherwise. |
|
| 5 | +# An existing xfs partition will be left alone. |
|
| 6 | +EPHEMERAL_VOLUME_NAME=$( |
|
| 7 | + # List all block devices and find those named nvme... |
|
| 8 | + for i in $(lsblk | grep -o "nvme[0-9][0-9]\?n[0-9]" | sort -u); do |
|
| 9 | + # If they don't have any partitions, then... |
|
| 10 | + if ! lsblk | grep -o "${i}p[0-9]\+" 2>&1 >/dev/null; then |
|
| 11 | + # ...check whether they are EBS devices |
|
| 12 | + /sbin/ebsnvme-id -u "/dev/$i" >/dev/null |
|
| 13 | + # If not, list their name because then they must be ephemeral instance storage |
|
| 14 | + if [[ $? -ne 0 ]]; then |
|
| 15 | + echo "${i}" |
|
| 16 | + fi |
|
| 17 | + fi |
|
| 18 | + done 2>/dev/null | head -n 1 ) |
|
| 19 | +if [ -n "${EPHEMERAL_VOLUME_NAME}" ]; then |
|
| 20 | + EPHEMERAL_VOLUME=/dev/${EPHEMERAL_VOLUME_NAME} |
|
| 21 | + FSTYPE=$(blkid -p $EPHEMERAL_VOLUME -s TYPE -o value) |
|
| 22 | + if [ "$FSTYPE" = "" ]; then |
|
| 23 | + echo FSTYPE was empty, creating swap partition |
|
| 24 | + mkswap $EPHEMERAL_VOLUME |
|
| 25 | + swapon --priority 2 -a $EPHEMERAL_VOLUME |
|
| 26 | + else |
|
| 27 | + echo "FSTYPE was $FSTYPE, not touching" |
|
| 28 | + fi |
|
| 29 | +else |
|
| 30 | + echo "No ephemeral partition found. Not creating any swap partition." |
|
| 31 | +fi |
configuration/environments_scripts/repo/usr/local/bin/notify-operators
| ... | ... | @@ -0,0 +1,4 @@ |
| 1 | +#!/bin/bash |
|
| 2 | +OPERATORS=$(cat /var/cache/landscapeManagersMailingList) |
|
| 3 | +logger -t sailing "Sending notification e-mail with subject $1 to ${OPERATORS}" |
|
| 4 | +mail -s "$1" ${OPERATORS} # This doesn't include the body, so if using programatically, pipe a body into this script. |
configuration/environments_scripts/repo/usr/local/bin/switchoverArchive.sh
| ... | ... | @@ -0,0 +1,114 @@ |
| 1 | +#!/bin/bash |
|
| 2 | + |
|
| 3 | +# Purpose: Script is used to switch to the failover archive if the primary is unhealthy, by altering the macros |
|
| 4 | +# file and then reloading Httpd. |
|
| 5 | +# Crontab for every minute: * * * * * /path/to/switchoverArchive.sh |
|
| 6 | +help() { |
|
| 7 | + echo "$0 PATH_TO_HTTPD_MACROS_FILE TIMEOUT_FIRST_CURL_SECONDS TIMEOUT_SECOND_CURL_SECONDS" |
|
| 8 | + echo "" |
|
| 9 | + echo "Script used to automatically update the archive location (to the failover) in httpd if the primary is down." |
|
| 10 | + echo "Pass in the path to the macros file containing the archive definitions;" |
|
| 11 | + echo "the timeout of the first curl check in seconds; and the timeout of the second curl check, also in seconds." |
|
| 12 | + echo "Make sure the combined time taken is not longer than the crontab." |
|
| 13 | + exit 2 |
|
| 14 | +} |
|
| 15 | +# $# is the number of arguments |
|
| 16 | +if [ $# -eq 0 ]; then |
|
| 17 | + help |
|
| 18 | +fi |
|
| 19 | +#The names of the variables in the macros file. |
|
| 20 | +ARCHIVE_IP_NAME="ARCHIVE_IP" |
|
| 21 | +ARCHIVE_FAILOVER_IP_NAME="ARCHIVE_FAILOVER_IP" |
|
| 22 | +PRODUCTION_ARCHIVE_NAME="PRODUCTION_ARCHIVE" |
|
| 23 | +ARCHIVE_PORT=8888 |
|
| 24 | +MACROS_PATH=$1 |
|
| 25 | +# The amount of time (in seconds) that must have elapsed, since the last httpd macros email, before notifying operators again. |
|
| 26 | +TIME_CHECK_SECONDS=$((15*60)) |
|
| 27 | +# Connection timeouts for curl requests (the time waited for a connection to be established). The second should be longer |
|
| 28 | +# as we want to be confident the main archive is in fact "down" before switching. |
|
| 29 | +TIMEOUT1_IN_SECONDS=$2 |
|
| 30 | +TIMEOUT2_IN_SECONDS=$3 |
|
| 31 | +CACHE_LOCATION="/var/cache/lastIncorrectMacroUnixTime" |
|
| 32 | +# The following line checks if all the strings in "search" are present at the beginning of their own line. Note: grep uses BRE by default, |
|
| 33 | +# so the plus symbol must be escaped to refer to "one or more" of the previous character. |
|
| 34 | +for i in "^Define ${PRODUCTION_ARCHIVE_NAME}\>" \ |
|
| 35 | + "^Define ${ARCHIVE_IP_NAME} [0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+$" \ |
|
| 36 | + "^Define ${ARCHIVE_FAILOVER_IP_NAME} [0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+$" |
|
| 37 | +do |
|
| 38 | + if ! grep -q "${i}" "${MACROS_PATH}"; then |
|
| 39 | + currentUnixTime=$(date +"%s") |
|
| 40 | + if [[ ! -f ${CACHE_LOCATION} || $((currentUnixTime - $(cat "${CACHE_LOCATION}") )) -gt "$TIME_CHECK_SECONDS" ]]; then |
|
| 41 | + date +"%s" > "${CACHE_LOCATION}" |
|
| 42 | + echo "Macros file does not contain proper definitions for the archive and failover IPs. Expression ${i} not matched." | notify-operators "Incorrect httpd macros" |
|
| 43 | + fi |
|
| 44 | + logger -t archive "Necessary variable assignment pattern ${i} not found in macros" |
|
| 45 | + exit 1 |
|
| 46 | + fi |
|
| 47 | +done |
|
| 48 | +# These next lines get the current ip values for the archive and failover, plus they store the value of production, |
|
| 49 | +# which is a variable pointing to either the primary or failover value. |
|
| 50 | +archiveIp="$(sed -n -e "s/^Define ${ARCHIVE_IP_NAME} \(.*\)/\1/p" ${MACROS_PATH} | tr -d '[:space:]')" |
|
| 51 | +failoverIp="$(sed -n -e "s/^Define ${ARCHIVE_FAILOVER_IP_NAME} \(.*\)/\1/p" ${MACROS_PATH} | tr -d '[:space:]')" |
|
| 52 | +productionIp="$(sed -n -e "s/^Define ${PRODUCTION_ARCHIVE_NAME} \(.*\)/\1/p" ${MACROS_PATH} | tr -d '[:space:]')" |
|
| 53 | +# Checks if the macro.conf is set as healthy or unhealthy currently. |
|
| 54 | +if [[ "${productionIp}" == "\${${ARCHIVE_IP_NAME}}" ]] |
|
| 55 | +then |
|
| 56 | + alreadyHealthy=1 |
|
| 57 | + logger -t archive "currently healthy" |
|
| 58 | +else |
|
| 59 | + alreadyHealthy=0 |
|
| 60 | + logger -t archive "currently unhealthy" |
|
| 61 | +fi |
|
| 62 | + |
|
| 63 | +setProduction() { |
|
| 64 | + # parameter $1: the name of the variable holding the IP of the archive instance to switch to |
|
| 65 | + sed -i -e "s/^Define ${PRODUCTION_ARCHIVE_NAME}\>.*$/Define ${PRODUCTION_ARCHIVE_NAME} \${${1}}/" ${MACROS_PATH} |
|
| 66 | +} |
|
| 67 | + |
|
| 68 | +# Sets the production value to point to the variable defining the main archive IP, provided it isn't already set. |
|
| 69 | +setProductionMainIfNotSet() { |
|
| 70 | + if [[ $alreadyHealthy -eq 0 ]] |
|
| 71 | + then |
|
| 72 | + # currently unhealthy |
|
| 73 | + # set production to archive |
|
| 74 | + logger -t archive "Healthy: setting production to main archive" |
|
| 75 | + setProduction ${ARCHIVE_IP_NAME} |
|
| 76 | + service httpd reload |
|
| 77 | + echo "The main archive server is healthy again. Switching to it." | notify-operators "Healthy: main archive online" |
|
| 78 | + else |
|
| 79 | + # If already healthy then no reload or notification occurs. |
|
| 80 | + logger -t archive "Healthy: already set, no change needed" |
|
| 81 | + fi |
|
| 82 | +} |
|
| 83 | + |
|
| 84 | +setFailoverIfNotSet() { |
|
| 85 | + if [[ $alreadyHealthy -eq 1 ]] |
|
| 86 | + then |
|
| 87 | + # Set production to failover if not already. Separate if statement in case the curl statement |
|
| 88 | + # fails but the production is already set to point to the backup |
|
| 89 | + setProduction ${ARCHIVE_FAILOVER_IP_NAME} |
|
| 90 | + logger -t archive "Unhealthy: second check failed, switching to failover" |
|
| 91 | + service httpd reload |
|
| 92 | + echo "Main archive is unhealthy. Switching to failover. Please urgently take a look at ${archiveIp}." | notify-operators "Unhealthy: main archive offline, failover in place" |
|
| 93 | + else |
|
| 94 | + logger -t archive "Unhealthy: second check still fails, failover already in use" |
|
| 95 | + fi |
|
| 96 | +} |
|
| 97 | + |
|
| 98 | +logger -t archive "begin check" |
|
| 99 | +# --fail option ensures that, if a server error is returned (ie. 5xx/4xx status code), then the status code (stored in $?) will be non zero. |
|
| 100 | +# -L follows redirects |
|
| 101 | +curl -s -L --fail --connect-timeout ${TIMEOUT1_IN_SECONDS} "http://${archiveIp}:${ARCHIVE_PORT}/gwt/status" >> /dev/null |
|
| 102 | +if [[ $? -ne 0 ]] |
|
| 103 | +then |
|
| 104 | + logger -t archive "first check failed" |
|
| 105 | + curl -s -L --fail --connect-timeout ${TIMEOUT2_IN_SECONDS} "http://${archiveIp}:${ARCHIVE_PORT}/gwt/status" >> /dev/null |
|
| 106 | + if [[ $? -ne 0 ]] |
|
| 107 | + then |
|
| 108 | + setFailoverIfNotSet |
|
| 109 | + else |
|
| 110 | + setProductionMainIfNotSet |
|
| 111 | + fi |
|
| 112 | +else |
|
| 113 | + setProductionMainIfNotSet |
|
| 114 | +fi |
configuration/environments_scripts/repo/usr/local/bin/sync-repo-and-execute-cmd.sh
| ... | ... | @@ -0,0 +1,48 @@ |
| 1 | +#!/bin/bash |
|
| 2 | + |
|
| 3 | +# Purpose: This script goes to a given git dir (eg. httpd); fetches any new commits to |
|
| 4 | +# the repo; and - if new commits are found - merges them into the branch and runs a command. |
|
| 5 | + |
|
| 6 | +if [ $# -eq 0 ]; then |
|
| 7 | + echo "$0 PATH_TO_GIT_REPO COMMAND_TO_RUN_ON_COMPLETION_IN_REPO" |
|
| 8 | + echo "" |
|
| 9 | + echo "EXAMPLE: sync-repo-and-execute-cmd.sh \"/etc/httpd\" \"sudo service httpd reload\"" |
|
| 10 | + echo "This script is used to automatically fetch from a git repo and, if there are new commits, merge the changes." |
|
| 11 | + echo "And then run a command, passed as an argument." |
|
| 12 | + exit 2 |
|
| 13 | +fi |
|
| 14 | + |
|
| 15 | +GIT_PATH=$1 |
|
| 16 | +COMMAND_ON_COMPLETION=$2 |
|
| 17 | +cd ${GIT_PATH} |
|
| 18 | +# Rev-parse gets the commit hash of given reference. |
|
| 19 | +CURRENT_HEAD=$(git rev-parse HEAD) |
|
| 20 | +git fetch |
|
| 21 | +if [[ $CURRENT_HEAD != $(git rev-parse origin/main) ]] # Checks if there are new commits |
|
| 22 | +then |
|
| 23 | + logger -t httpd "Changes found; merging now" |
|
| 24 | + cd ${GIT_PATH} && git merge origin/main |
|
| 25 | + if [[ $? -eq 0 ]]; then |
|
| 26 | + logger -t httpdMerge "Merge succeeded: different files edited." |
|
| 27 | + else |
|
| 28 | + logger -t httpdMerge "First merge unsuccessful: same file modified." |
|
| 29 | + git merge --abort # Returns to pre-merge state. |
|
| 30 | + git stash |
|
| 31 | + git merge origin/main # This should be a fast-forward merge. |
|
| 32 | + git stash apply # Keeps stash on top of stack, in case the apply fails. |
|
| 33 | + if [[ $? -eq 0 ]]; then |
|
| 34 | + logger -t httpdMerge "Second merge success: merge of httpd remote to local successful, and previous working directory changes restored." |
|
| 35 | + git stash drop # Removes successful stash from stash stack. |
|
| 36 | + else |
|
| 37 | + logger -t httpdMerge "Second merge unsuccessful: same sections modified" |
|
| 38 | + echo "Merging issue at commit $(git rev-parse HEAD). Currently at last safe commit." | notify-operators "Merge conflict on httpd instance. Manual intervention required." |
|
| 39 | + # Returns to pre-pull state and then pops |
|
| 40 | + git reset --hard "${CURRENT_HEAD}" |
|
| 41 | + git stash pop |
|
| 42 | + exit 1 |
|
| 43 | + fi |
|
| 44 | + fi |
|
| 45 | + sleep 2 |
|
| 46 | + $($COMMAND_ON_COMPLETION) |
|
| 47 | +fi |
|
| 48 | + |
configuration/environments_scripts/repo/usr/local/bin/syncgit
| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | +#!/bin/sh |
|
| 2 | +ADMIN_EMAIL="axel.uhl@sap.com jan.hamann@sapsailing.com" |
|
| 3 | +if [ $# -eq 0 ]; then |
|
| 4 | + GIT_PATH="/home/wiki/gitwiki" |
|
| 5 | +else |
|
| 6 | + GIT_PATH=$1 |
|
| 7 | +fi |
|
| 8 | +cd $GIT_PATH |
|
| 9 | +git pull >/tmp/wiki-git.out 2>/tmp/wiki-git.err |
|
| 10 | +if [ "$?" != "0" ]; then |
|
| 11 | + cat /tmp/wiki-git.out /tmp/wiki-git.err | mail -s "Wiki git problem" $ADMIN_EMAIL |
|
| 12 | +fi |
|
| 13 | +git push >>/tmp/wiki-git.out 2>/tmp/wiki-git.err |
|
| 14 | +if [ "$?" != "0" ]; then |
|
| 15 | + cat /tmp/wiki-git.out /tmp/wiki-git.err | mail -s "Wiki git problem" $ADMIN_EMAIL |
|
| 16 | +fi |
configuration/environments_scripts/repo/usr/local/bin/update_authorized_keys_for_landscape_managers
| ... | ... | @@ -0,0 +1,48 @@ |
| 1 | +#!/bin/bash |
|
| 2 | +BEARER_TOKEN="$1" |
|
| 3 | +BASE_URL="$2" |
|
| 4 | +LOGON_USER_HOME="$3" |
|
| 5 | +SSH_DIR="$3/.ssh" |
|
| 6 | +EXIT_CODE=0 |
|
| 7 | +# |
|
| 8 | +curl_output=$( curl -H 'X-SAPSSE-Forward-Request-To: master' -H 'Authorization: Bearer '${BEARER_TOKEN} "${BASE_URL}/security/api/restsecurity/users_with_permission?permission=LANDSCAPE:MANAGE:AWS" 2>/dev/null ) |
|
| 9 | +curl_exit_code=$? |
|
| 10 | +if [ "${curl_exit_code}" = "0" ]; then |
|
| 11 | + users=$( echo "${curl_output}" | jq -r '.[]' ) |
|
| 12 | + jq_exit_code=$? |
|
| 13 | + if [ "${jq_exit_code}" = "0" ]; then |
|
| 14 | + logger -t sailing "Users with LANDSCAPE:MANAGE:AWS permission: ${users}" |
|
| 15 | + public_keys=$( for user in ${users}; do |
|
| 16 | + ssh_key_curl_output=$(curl -H 'X-SAPSSE-Forward-Request-To: master' -H 'Authorization: Bearer '${BEARER_TOKEN} "${BASE_URL}/landscape/api/landscape/get_ssh_keys_owned_by_user?username[]=${user}" 2>/dev/null ) |
|
| 17 | + ssh_key_curl_exit_code=$? |
|
| 18 | + if [ "${ssh_key_curl_exit_code}" = "0" ]; then |
|
| 19 | + echo "${ssh_key_curl_output}" | jq -r '.[].publicKey' |
|
| 20 | + ssh_key_jq_exit_code=$? |
|
| 21 | + if [ "${ssh_key_jq_exit_code}" != "0" ]; then |
|
| 22 | + EXIT_CODE=${ssh_key_jq_exit_code} |
|
| 23 | + logger -t sailing "Couldn't parse response of get_ssh_keys_owned_by_user; jq exit code ${ssh_key_jq_exit_code}" |
|
| 24 | + fi |
|
| 25 | + else |
|
| 26 | + EXIT_CODE=${ssh_key_curl_exit_code} |
|
| 27 | + logger -t sailing "Couldn't get response of get_ssh_keys_owned_by_user; curl exit code ${ssh_key_corl_exit_code}" |
|
| 28 | + fi |
|
| 29 | + done | sort -u ) |
|
| 30 | + logger -t sailing "Obtained public keys: ${public_keys}" |
|
| 31 | + if [ ! -f ${SSH_DIR}/authorized_keys.org ]; then |
|
| 32 | + # Create a copy of the original authorized_keys file as generated by AWS from the start-up key: |
|
| 33 | + logger -t sailing "Saving original authorized_keys file from ${SSH_DIR}" |
|
| 34 | + cp ${SSH_DIR}/authorized_keys ${SSH_DIR}/authorized_keys.org |
|
| 35 | + fi |
|
| 36 | + # Start out with the original AWS-generated authorized_keys file |
|
| 37 | + # and append the public SSH keys of all users having LANDSCAPE:MANAGE:AWS permission: |
|
| 38 | + echo "$( cat ${SSH_DIR}/authorized_keys.org ) |
|
| 39 | +${public_keys}" | sort -u >${SSH_DIR}/authorized_keys |
|
| 40 | + else |
|
| 41 | + EXIT_CODE=${jq_exit_code} |
|
| 42 | + logger -t sailing "Couldn't parse response of users_with_permission; jq exit code ${jq_exit_code}" |
|
| 43 | + fi |
|
| 44 | +else |
|
| 45 | + EXIT_CODE=${curl_exit_code} |
|
| 46 | + logger -t sailing "Couldn't get response of users_with_permission; curl exit code ${curl_exit_code}" |
|
| 47 | +fi |
|
| 48 | +exit ${EXIT_CODE} |
configuration/environments_scripts/repo/usr/local/bin/update_authorized_keys_for_landscape_managers_if_changed
| ... | ... | @@ -0,0 +1,42 @@ |
| 1 | +#!/bin/bash |
|
| 2 | +BEARER_TOKEN="$1" |
|
| 3 | +BASE_URL="$2" |
|
| 4 | +LOGON_USER_HOME="$3" |
|
| 5 | +LAST_CHANGE_FILE=/var/run/last_change_aws_landscape_managers_ssh_keys |
|
| 6 | +# Uncomment the following for production use, with no error output |
|
| 7 | +curl_output=$( curl -H 'X-SAPSSE-Forward-Request-To: master' -H 'Authorization: Bearer '${BEARER_TOKEN} "${BASE_URL}/landscape/api/landscape/get_time_point_of_last_change_in_ssh_keys_of_aws_landscape_managers" 2>/dev/null ) |
|
| 8 | +curl_exit_code=$? |
|
| 9 | +if [ "${curl_exit_code}" = "0" ]; then |
|
| 10 | + last_change_millis=$( echo "${curl_output}" | jq -r '."timePointOfLastChangeOfSetOfLandscapeManagers-millis"' ) |
|
| 11 | + jq_exit_code=$? |
|
| 12 | + if [ "${jq_exit_code}" = "0" ]; then |
|
| 13 | + if [ -f "${LAST_CHANGE_FILE}" ]; then |
|
| 14 | + PREVIOUS_CHANGE=$(cat "${LAST_CHANGE_FILE}") |
|
| 15 | + if [ -z ${PREVIOUS_CHANGE} ]; then |
|
| 16 | + PREVIOUS_CHANGE=0 |
|
| 17 | + fi |
|
| 18 | + else |
|
| 19 | + PREVIOUS_CHANGE=0 |
|
| 20 | + fi |
|
| 21 | + if [ -z ${last_change_millis} ]; then |
|
| 22 | + logger -t sailing "Empty response from get_time_point_of_last_change_in_ssh_keys_of_aws_landscape_managers; exiting" |
|
| 23 | + exit 1 |
|
| 24 | + else |
|
| 25 | + if [ ${PREVIOUS_CHANGE} -lt ${last_change_millis} ]; then |
|
| 26 | + logger -t sailing "New SSH key changes for landscape managers (${last_change_millis} newer than ${PREVIOUS_CHANGE})" |
|
| 27 | + if update_authorized_keys_for_landscape_managers "${BEARER_TOKEN}" "${BASE_URL}" "${LOGON_USER_HOME}" ; then |
|
| 28 | + logger -t sailing "Updating SSH keys for landscape managers successful; updating ${LAST_CHANGE_FILE}" |
|
| 29 | + echo ${last_change_millis} >${LAST_CHANGE_FILE} |
|
| 30 | + else |
|
| 31 | + logger -t sailing "Updating SSH keys for landscape managers failed with exit code $?; not updating ${LAST_CHANGE_FILE}" |
|
| 32 | + fi |
|
| 33 | + fi |
|
| 34 | + fi |
|
| 35 | + else |
|
| 36 | + logger -t sailing "Parsing response of get_time_point_of_last_change_in_ssh_keys_of_aws_landscape_managers failed with exit code ${jq_exit_code}" |
|
| 37 | + exit ${jq_exit_code} |
|
| 38 | + fi |
|
| 39 | +else |
|
| 40 | + logger -t sailing "Getting response of get_time_point_of_last_change_in_ssh_keys_of_aws_landscape_managers failed with exit code ${curl_exit_code}" |
|
| 41 | + exit ${curl_exit_code} |
|
| 42 | +fi |
configuration/environments_scripts/repo/usr/local/bin/update_landscape_managers_mailing_list.sh
| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | +#!/bin/bash |
|
| 2 | + |
|
| 3 | +# Purpose: Create a mailing list in PATH_TO_STORE/landscapeManagersMailingList that contains the emails of all landscape managers, ie. those who |
|
| 4 | +# have complete admin privileges in the aws environment. |
|
| 5 | +BEARER_TOKEN="$1" |
|
| 6 | +PATH_TO_STORE="$2" |
|
| 7 | +NAME_TO_STORE_IN="landscapeManagersMailingList" |
|
| 8 | +BASE_URL="https://security-service.sapsailing.com" |
|
| 9 | +curl_output=$( curl -H 'X-SAPSSE-Forward-Request-To: master' -H 'Authorization: Bearer '${BEARER_TOKEN} "${BASE_URL}/security/api/restsecurity/users_with_permission?permission=LANDSCAPE:MANAGE:AWS" 2>/dev/null ) |
|
| 10 | +if [[ -f "${PATH_TO_STORE}/${NAME_TO_STORE_IN}" ]]; then |
|
| 11 | + mv -f ${PATH_TO_STORE}/${NAME_TO_STORE_IN} ${PATH_TO_STORE}/${NAME_TO_STORE_IN}.bak |
|
| 12 | +fi |
|
| 13 | +touch ${PATH_TO_STORE}/${NAME_TO_STORE_IN} |
|
| 14 | +echo $curl_output | jq -r .[] | while read user; do |
|
| 15 | + email=$(curl -H 'X-SAPSSE-Forward-Request-To: master' -H 'Authorization: Bearer '${BEARER_TOKEN} "${BASE_URL}/security/api/restsecurity/user?username=$user" 2>/dev/null| jq -r '.email' ) |
|
| 16 | + echo $email >> ${PATH_TO_STORE}/${NAME_TO_STORE_IN} |
|
| 17 | +done |
configuration/environments_scripts/reverse_proxy/files/etc/systemd/system/mountnvmeswap.service
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/etc/systemd/system/mountnvmeswap.service |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/reverse_proxy/files/usr/local/bin/imageupgrade_functions.sh
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/imageupgrade_functions.sh |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/reverse_proxy/files/usr/local/bin/mountnvmeswap
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/mountnvmeswap |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/reverse_proxy/files/usr/local/bin/notify-operators
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/notify-operators |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/reverse_proxy/files/usr/local/bin/switchoverArchive.sh
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/switchoverArchive.sh |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/reverse_proxy/files/usr/local/bin/sync-repo-and-execute-cmd.sh
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/sync-repo-and-execute-cmd.sh |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/reverse_proxy/files/usr/local/bin/syncgit
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/syncgit |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/reverse_proxy/files/usr/local/bin/update_authorized_keys_for_landscape_managers
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/update_authorized_keys_for_landscape_managers |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/reverse_proxy/files/usr/local/bin/update_authorized_keys_for_landscape_managers_if_changed
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/update_authorized_keys_for_landscape_managers_if_changed |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/reverse_proxy/files/usr/local/bin/update_landscape_managers_mailing_list.sh
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/update_landscape_managers_mailing_list.sh |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/reverse_proxy/users/root/crontab-switchoverArchive
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../crontabs/crontab-switchoverArchive |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/reverse_proxy/users/root/crontab-update-authorized-keys
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../crontabs/crontab-update-authorized-keys |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/reverse_proxy/users/root/crontab-update-landscape-managers-mailing-list
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../crontabs/crontab-update-landscape-managers-mailing-list |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/reverse_proxy/users/trac/crontab-syncgit
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../crontabs/crontab-syncgit |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/sailing_server/files/etc/profile.d/sailing.sh
| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | +# Script to be linked from /etc/profile.d |
|
| 2 | +# Appends to PATH, sets DISPLAY for VNC running on :2, exports JAVA_HOME and Amazon EC2 variables |
|
| 3 | + |
|
| 4 | +ulimit -n 100000 |
|
| 5 | +ulimit -u 40000 |
|
| 6 | + |
|
| 7 | +# SAP JVM |
|
| 8 | +export JAVA_HOME=/opt/sapjvm_8 |
|
| 9 | +# JDK 11.0.1: |
|
| 10 | +#export JAVA_HOME=/opt/jdk-11.0.1+13 |
|
| 11 | +#export JAVA_HOME=/opt/jdk1.8.0_45 |
|
| 12 | +export ANDROID_HOME=/opt/android-sdk-linux |
|
| 13 | +export PATH=$PATH:$JAVA_HOME/bin |
|
| 14 | +export DISPLAY=:2.0 |
configuration/environments_scripts/sailing_server/files/etc/systemd/system/mountnvmeswap.service
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/etc/systemd/system/mountnvmeswap.service |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/sailing_server/files/etc/systemd/system/sailing.service
| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | +[Unit] |
|
| 2 | +Description=The sailing start-up service reading through EC2 userdata and acting accordingly |
|
| 3 | +Requires=-.mount mongod.service |
|
| 4 | +After=-.mount mongod.service |
|
| 5 | + |
|
| 6 | +[Install] |
|
| 7 | +RequiredBy=multi-user.target |
|
| 8 | + |
|
| 9 | +[Service] |
|
| 10 | +Type=oneshot |
|
| 11 | +RemainAfterExit=true |
|
| 12 | +ExecStart=/etc/init.d/sailing start |
|
| 13 | +ExecStop=/etc/init.d/sailing stop |
configuration/environments_scripts/sailing_server/files/usr/local/bin/imageupgrade.sh
| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | +#!/bin/bash |
|
| 2 | + |
|
| 3 | +# Upgrades the AWS EC2 instance that this script is assumed to be executed on. |
|
| 4 | +# The steps are as follows: |
|
| 5 | + |
|
| 6 | +. `dirname $0`/imageupgrade_functions.sh |
|
| 7 | + |
|
| 8 | +run_yum_update |
|
| 9 | +run_git_pull |
|
| 10 | +download_and_install_latest_sap_jvm_8 |
|
| 11 | +clean_logrotate_target |
|
| 12 | +clean_httpd_logs |
|
| 13 | +clean_servers_dir |
|
| 14 | +clean_startup_logs |
|
| 15 | +update_root_crontab |
|
| 16 | +finalize |
configuration/environments_scripts/sailing_server/files/usr/local/bin/imageupgrade_functions.sh
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/imageupgrade_functions.sh |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/sailing_server/files/usr/local/bin/mountnvmeswap
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/mountnvmeswap |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/sailing_server/files/usr/local/bin/sailing
| ... | ... | @@ -0,0 +1,124 @@ |
| 1 | +#!/bin/bash |
|
| 2 | +# |
|
| 3 | +# sailing Starts sailing services |
|
| 4 | +# |
|
| 5 | +# chkconfig: 2345 95 10 |
|
| 6 | +# description: Sailing contains all sailing services |
|
| 7 | +# |
|
| 8 | + |
|
| 9 | + |
|
| 10 | +# Source function library. |
|
| 11 | +. /etc/init.d/functions |
|
| 12 | + |
|
| 13 | +RETVAL=0 |
|
| 14 | + |
|
| 15 | +SERVERS_DIR=/home/sailing/servers |
|
| 16 | +cd "${SERVERS_DIR}" |
|
| 17 | +JAVA_START_INSTANCES="$(find * -type d -prune)" |
|
| 18 | +GIT_REPOSITORY=/home/sailing/code |
|
| 19 | +if [ -x /bin/ec2-metadata ]; then |
|
| 20 | + EC2_METADATA_CMD=/bin/ec2-metadata |
|
| 21 | +elif [ -x /usr/bin/ec2-metadata ]; then |
|
| 22 | + EC2_METADATA_CMD=/usr/bin/ec2-metadata |
|
| 23 | +else |
|
| 24 | + EC2_METADATA_CMD=/opt/aws/bin/ec2-metadata |
|
| 25 | +fi |
|
| 26 | +REBOOT_INDICATOR=/var/lib/sailing/is-rebooted |
|
| 27 | +mkdir --parents $( dirname ${REBOOT_INDICATOR} ) |
|
| 28 | +SSH_KEY_READER_BEARER_TOKEN=/root/ssh-key-reader.token |
|
| 29 | + |
|
| 30 | +echo "Executing with $1 at `date`" >>/var/log/sailing.err |
|
| 31 | + |
|
| 32 | +start_servers() { |
|
| 33 | + /usr/local/bin/update_authorized_keys_for_landscape_managers $( cat ${SSH_KEY_READER_BEARER_TOKEN} ) https://security-service.sapsailing.com /root 2>&1 >>/var/log/sailing.err |
|
| 34 | + cp /home/sailing/code/configuration/cp_root_mail_properties /usr/local/bin |
|
| 35 | + chown root /usr/local/bin/cp_root_mail_properties |
|
| 36 | + chgrp root /usr/local/bin/cp_root_mail_properties |
|
| 37 | + chmod 755 /usr/local/bin/cp_root_mail_properties |
|
| 38 | + cp /home/sailing/code/configuration/cp_root_mail_properties_sudoers /etc/sudoers.d |
|
| 39 | + if which $EC2_METADATA_CMD && $EC2_METADATA_CMD -d | sed "s/user-data\: //g" | grep "^image-upgrade$"; then |
|
| 40 | + echo "Found image-upgrade in EC2 user data; upgrading image, then probably shutting down for AMI creation depending on the no-shutdown user data string..." >>/var/log/sailing.err |
|
| 41 | + $GIT_REPOSITORY/configuration/imageupgrade.sh |
|
| 42 | + else |
|
| 43 | + echo "No image-upgrade request found in EC2 user data $($EC2_METADATA_CMD -d); proceeding with regular server launch..." >>/var/log/sailing.err |
|
| 44 | + echo "Servers to launch: ${JAVA_START_INSTANCES}" >>/var/log/sailing.err |
|
| 45 | + if [ -f "${REBOOT_INDICATOR}" ]; then |
|
| 46 | + echo "This is a re-boot. No EC2 user data is evaluated for server configuration; no server configuration is performed. Only configured applications are launched." >>/var/log/sailing.err |
|
| 47 | + for conf in ${JAVA_START_INSTANCES}; do |
|
| 48 | + su - sailing -c "cd ${SERVERS_DIR}/${conf} && ./start" 2>>/var/log/sailing.err >>/var/log/sailing.err |
|
| 49 | + done |
|
| 50 | + else |
|
| 51 | + echo "This is a first-time boot. EC2 user data is evaluated for potential application deployment and configuration, and applications are launched." >>/var/log/sailing.err |
|
| 52 | + echo "Initializing local MongoDB replica set \"replica\"..." |
|
| 53 | + while ! echo "rs.initiate()" | mongo; do |
|
| 54 | + echo "MongoDB not ready yet; waiting and trying again..." |
|
| 55 | + sleep 5 |
|
| 56 | + done |
|
| 57 | + FIRST_SERVER=$( eval $( ${EC2_METADATA_CMD} -d | sed -e 's/^user-data: //' ); echo $SERVER_NAME ) |
|
| 58 | + if [ "${FIRST_SERVER}" = "" ]; then |
|
| 59 | + echo "No SERVER_NAME provided; not configuring/starting any application processes" >>/var/log/sailing.err |
|
| 60 | + else |
|
| 61 | + echo "Server to configure and start: ${FIRST_SERVER}" >>/var/log/sailing.err |
|
| 62 | + configure_and_start_server "${FIRST_SERVER}" |
|
| 63 | + fi |
|
| 64 | + echo 1 >"${REBOOT_INDICATOR}" |
|
| 65 | + fi |
|
| 66 | + fi |
|
| 67 | +} |
|
| 68 | + |
|
| 69 | +# Call with the server directory name (not the full path, just a single element from ${JAVA_START_INSTANCE}) as parameter |
|
| 70 | +# Example: configure_and_start_server server |
|
| 71 | +# This is expected to be called only in case there is only one server to configure; otherwise, the same EC2 user data |
|
| 72 | +# would get applied to all application configurations which would not be a good idea. |
|
| 73 | +configure_and_start_server() { |
|
| 74 | + conf="$1" |
|
| 75 | + mkdir -p "${SERVERS_DIR}/${conf}" >/dev/null 2>/dev/null |
|
| 76 | + chown sailing "${SERVERS_DIR}/${conf}" |
|
| 77 | + chgrp sailing "${SERVERS_DIR}/${conf}" |
|
| 78 | + # If there is a secret /root/mail.properties, copy it into the default server's configuration directory: |
|
| 79 | + /usr/local/bin/cp_root_mail_properties "${conf}" |
|
| 80 | + su - sailing -c "cd ${SERVERS_DIR}/${conf} && ${GIT_REPOSITORY}/java/target/refreshInstance.sh auto-install; ./start" 2>>/var/log/sailing.err >>/var/log/sailing.err |
|
| 81 | + pushd ${SERVERS_DIR}/${conf} |
|
| 82 | + ./defineReverseProxyMappings.sh 2>>/var/log/sailing.err >>/var/log/sailing.err |
|
| 83 | + popd |
|
| 84 | + RETVAL=$? |
|
| 85 | + [ $RETVAL -eq 0 ] && success || failure |
|
| 86 | +} |
|
| 87 | + |
|
| 88 | +stop_servers() { |
|
| 89 | + for conf in $JAVA_START_INSTANCES; do |
|
| 90 | + echo "Stopping Java server $conf" >> /var/log/sailing.err |
|
| 91 | + su - sailing -c "cd $SERVERS_DIR/$conf && ./stop" |
|
| 92 | + RETVAL=$? |
|
| 93 | + [ $RETVAL -eq 0 ] && success || failure |
|
| 94 | + sync_logs |
|
| 95 | + done |
|
| 96 | +} |
|
| 97 | + |
|
| 98 | +sync_logs() { |
|
| 99 | + echo "Executing logrotate followed by a sync to ensure that logs are synchronized" >> /var/log/sailing.err |
|
| 100 | + logrotate -f /etc/logrotate.conf |
|
| 101 | + sync |
|
| 102 | +} |
|
| 103 | + |
|
| 104 | +# See how we were called. |
|
| 105 | +case "$1" in |
|
| 106 | + start) |
|
| 107 | + start_servers |
|
| 108 | + /usr/sbin/update-motd |
|
| 109 | + touch /var/lock/subsys/sailing |
|
| 110 | + ;; |
|
| 111 | + stop) |
|
| 112 | + stop_servers |
|
| 113 | + rm -f /var/lock/subsys/sailing |
|
| 114 | + ;; |
|
| 115 | + status) |
|
| 116 | + status java |
|
| 117 | + RETVAL=$? |
|
| 118 | + ;; |
|
| 119 | + *) |
|
| 120 | + echo $"Usage: $0 {start|status|stop}" |
|
| 121 | + RETVAL=3 |
|
| 122 | +esac |
|
| 123 | + |
|
| 124 | +exit $RETVAL |
configuration/environments_scripts/sailing_server/files/usr/local/bin/update_authorized_keys_for_landscape_managers
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/update_authorized_keys_for_landscape_managers |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/sailing_server/files/usr/local/bin/update_authorized_keys_for_landscape_managers_if_changed
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../../repo/usr/local/bin/update_authorized_keys_for_landscape_managers_if_changed |
|
| ... | ... | \ No newline at end of file |
configuration/environments_scripts/sailing_server/users/root/crontab-update-authorized-keys
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../../../crontabs/crontab-update-authorized-keys |
|
| ... | ... | \ No newline at end of file |
configuration/httpd/cgi-bin/github.cgi
| ... | ... | @@ -1,20 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | -echo " |
|
| 3 | -" |
|
| 4 | -BODY=$( cat ) |
|
| 5 | -echo "${BODY}" >/tmp/github-hook-body |
|
| 6 | -REF=$( echo "${BODY}" | jq -r '.ref' ) |
|
| 7 | -PUSHER=$( echo "${BODY}" | jq -r '.pusher.email' ) |
|
| 8 | -logger -t github-cgi "ref ia $REF, pusher was $PUSHER" |
|
| 9 | -# For testing: |
|
| 10 | -#if [ "${PUSHER}" = "axel.uhl@sap.com" -a "${REF}" = "refs/heads/translation" ]; then |
|
| 11 | -if [ "${PUSHER}" = "tmsatsls+github.tools.sap_service-tip-git@sap.com" -a "${REF}" = "refs/heads/translation" ]; then |
|
| 12 | - echo "Identified a push to refs/heads/translation by ${PUSHER}." |
|
| 13 | - echo "Fetching translation branch from github.tools.sap and pushing it to ssh://trac@sapsailing.com/home/trac/git" |
|
| 14 | - logger -t github-cgi "fetching translation branch from github.tools.sap and pushing it to ssh://trac@sapsailing.com/home/trac/git" |
|
| 15 | - cd /home/wiki/gitwiki |
|
| 16 | - sudo -u wiki git fetch sapsailing translation:translation 2>&1 |
|
| 17 | - sudo -u wiki git push origin translation:translation 2>&1 |
|
| 18 | -else |
|
| 19 | - echo "Either pusher was not tmsatsls+github.tools.sap_service-tip-git@sap.com or ref was not refs/heads/translation. Not triggering." |
|
| 20 | -fi |
configuration/httpd/cgi-bin/github.cgi
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../../environments_scripts/central_reverse_proxy/files/var/www/cgi-bin/github.cgi |
|
| ... | ... | \ No newline at end of file |
configuration/hudson_instance_setup/hudson
| ... | ... | @@ -1,160 +0,0 @@ |
| 1 | -#!/bin/sh |
|
| 2 | -# |
|
| 3 | -# SUSE system statup script for Hudson |
|
| 4 | -# Copyright (C) 2007 Pascal Bleser |
|
| 5 | -# |
|
| 6 | -# This library is free software; you can redistribute it and/or modify it |
|
| 7 | -# under the terms of the GNU Lesser General Public License as published by |
|
| 8 | -# the Free Software Foundation; either version 2.1 of the License, or (at |
|
| 9 | -# your option) any later version. |
|
| 10 | -# |
|
| 11 | -# This library is distributed in the hope that it will be useful, but |
|
| 12 | -# WITHOUT ANY WARRANTY; without even the implied warranty of |
|
| 13 | -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
| 14 | -# Lesser General Public License for more details. |
|
| 15 | -# |
|
| 16 | -# You should have received a copy of the GNU Lesser General Public |
|
| 17 | -# License along with this library; if not, write to the Free Software |
|
| 18 | -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, |
|
| 19 | -# USA. |
|
| 20 | -# |
|
| 21 | -### BEGIN INIT INFO |
|
| 22 | -# Provides: hudson |
|
| 23 | -# Required-Start: $local_fs $remote_fs $network $time $named |
|
| 24 | -# Should-Start: $time sendmail |
|
| 25 | -# Required-Stop: $local_fs $remote_fs $network $time $named |
|
| 26 | -# Should-Stop: $time sendmail |
|
| 27 | -# Default-Start: 3 5 |
|
| 28 | -# Default-Stop: 0 1 2 6 |
|
| 29 | -# Short-Description: Hudson continuous build server |
|
| 30 | -# Description: Start the Hudson continuous build server |
|
| 31 | -### END INIT INFO |
|
| 32 | - |
|
| 33 | -# Check for missing binaries (stale symlinks should not happen) |
|
| 34 | -HUDSON_WAR="/usr/lib/hudson/hudson.war" |
|
| 35 | -test -r "$HUDSON_WAR" || { echo "$HUDSON_WAR not installed"; |
|
| 36 | - if [ "$1" = "stop" ]; then exit 0; |
|
| 37 | - else exit 5; fi; } |
|
| 38 | - |
|
| 39 | -# Check for existence of needed config file and read it |
|
| 40 | -HUDSON_CONFIG=/etc/sysconfig/hudson |
|
| 41 | -test -e "$HUDSON_CONFIG" || { echo "$HUDSON_CONFIG not existing"; |
|
| 42 | - if [ "$1" = "stop" ]; then exit 0; |
|
| 43 | - else exit 6; fi; } |
|
| 44 | -test -r "$HUDSON_CONFIG" || { echo "$HUDSON_CONFIG not readable. Perhaps you forgot 'sudo'?"; |
|
| 45 | - if [ "$1" = "stop" ]; then exit 0; |
|
| 46 | - else exit 6; fi; } |
|
| 47 | - |
|
| 48 | -HUDSON_PID_FILE="/var/run/hudson.pid" |
|
| 49 | -HUDSON_USER="hudson" |
|
| 50 | -HUDSON_GROUP="hudson" |
|
| 51 | - |
|
| 52 | -# Source function library. |
|
| 53 | -. /etc/init.d/functions |
|
| 54 | - |
|
| 55 | -# Read config |
|
| 56 | -[ -f "$HUDSON_CONFIG" ] && . "$HUDSON_CONFIG" |
|
| 57 | - |
|
| 58 | -# Set up environment accordingly to the configuration settings |
|
| 59 | -[ -n "$HUDSON_HOME" ] || { echo "HUDSON_HOME not configured in $HUDSON_CONFIG"; |
|
| 60 | - if [ "$1" = "stop" ]; then exit 0; |
|
| 61 | - else exit 6; fi; } |
|
| 62 | -[ -d "$HUDSON_HOME" ] || { echo "HUDSON_HOME directory does not exist: $HUDSON_HOME"; |
|
| 63 | - if [ "$1" = "stop" ]; then exit 0; |
|
| 64 | - else exit 1; fi; } |
|
| 65 | -export HUDSON_HOME |
|
| 66 | - |
|
| 67 | -# Search usable Java. We do this because various reports indicated |
|
| 68 | -# that /usr/bin/java may not always point to Java 1.5 |
|
| 69 | -# see http://www.nabble.com/guinea-pigs-wanted-----Hudson-RPM-for-RedHat-Linux-td25673707.html |
|
| 70 | -for candidate in /usr/lib/jvm/java-1.6.0/bin/java /usr/lib/jvm/jre-1.6.0/bin/java /usr/lib/jvm/java-1.5.0/bin/java /usr/lib/jvm/jre-1.5.0/bin/java /usr/bin/java |
|
| 71 | -do |
|
| 72 | - [ -x "$HUDSON_JAVA_CMD" ] && break |
|
| 73 | - HUDSON_JAVA_CMD="$candidate" |
|
| 74 | -done |
|
| 75 | - |
|
| 76 | -JAVA_CMD="$HUDSON_JAVA_CMD $HUDSON_JAVA_OPTIONS -DHUDSON_HOME=$HUDSON_HOME -jar $HUDSON_WAR" |
|
| 77 | -PARAMS="--logfile=/var/log/hudson/hudson.log --daemon" |
|
| 78 | -[ -n "$HUDSON_PORT" ] && PARAMS="$PARAMS --httpPort=$HUDSON_PORT" |
|
| 79 | -[ -n "$HUDSON_DEBUG_LEVEL" ] && PARAMS="$PARAMS --debug=$HUDSON_DEBUG_LEVEL" |
|
| 80 | -[ -n "$HUDSON_HANDLER_STARTUP" ] && PARAMS="$PARAMS --handlerCountStartup=$HUDSON_HANDLER_STARTUP" |
|
| 81 | -[ -n "$HUDSON_HANDLER_MAX" ] && PARAMS="$PARAMS --handlerCountMax=$HUDSON_HANDLER_MAX" |
|
| 82 | -[ -n "$HUDSON_HANDLER_IDLE" ] && PARAMS="$PARAMS --handlerCountMaxIdle=$HUDSON_HANDLER_IDLE" |
|
| 83 | -[ -n "$HUDSON_ARGS" ] && PARAMS="$PARAMS $HUDSON_ARGS" |
|
| 84 | - |
|
| 85 | -if [ "$HUDSON_ENABLE_ACCESS_LOG" = "yes" ]; then |
|
| 86 | - PARAMS="$PARAMS --accessLoggerClassName=winstone.accesslog.SimpleAccessLogger --simpleAccessLogger.format=combined --simpleAccessLogger.file=/var/log/hudson/access_log" |
|
| 87 | -fi |
|
| 88 | - |
|
| 89 | -RETVAL=0 |
|
| 90 | - |
|
| 91 | -case "$1" in |
|
| 92 | - start) |
|
| 93 | - echo -n "Starting Hudson " |
|
| 94 | - daemon --user "$HUDSON_USER" --pidfile "$HUDSON_PID_FILE" "$JAVA_CMD" "$PARAMS" &> /var/tmp/hudson.log & |
|
| 95 | - RETVAL=$? |
|
| 96 | - if [ $RETVAL = 0 ]; then |
|
| 97 | - success |
|
| 98 | - echo > "$HUDSON_PID_FILE" # just in case we fail to find it |
|
| 99 | - MY_SESSION_ID=`/bin/ps h -o sess -p $$` |
|
| 100 | - # get PID |
|
| 101 | - /bin/ps hww -u "$HUDSON_USER" -o sess,ppid,pid,cmd | \ |
|
| 102 | - while read sess ppid pid cmd; do |
|
| 103 | - [ "$ppid" = 1 ] || continue |
|
| 104 | - # this test doesn't work because Hudson sets a new Session ID |
|
| 105 | - # [ "$sess" = "$MY_SESSION_ID" ] || continue |
|
| 106 | - echo "$cmd" | grep $HUDSON_WAR > /dev/null |
|
| 107 | - [ $? = 0 ] || continue |
|
| 108 | - # found a PID |
|
| 109 | - echo $pid > "$HUDSON_PID_FILE" |
|
| 110 | - done |
|
| 111 | - else |
|
| 112 | - failure |
|
| 113 | - fi |
|
| 114 | - echo |
|
| 115 | - ;; |
|
| 116 | - stop) |
|
| 117 | - echo -n "Shutting down Hudson " |
|
| 118 | - killproc hudson |
|
| 119 | - RETVAL=$? |
|
| 120 | - echo |
|
| 121 | - ;; |
|
| 122 | - try-restart|condrestart) |
|
| 123 | - if test "$1" = "condrestart"; then |
|
| 124 | - echo "${attn} Use try-restart ${done}(LSB)${attn} rather than condrestart ${warn}(RH)${norm}" |
|
| 125 | - fi |
|
| 126 | - $0 status |
|
| 127 | - if test $? = 0; then |
|
| 128 | - $0 restart |
|
| 129 | - else |
|
| 130 | - : # Not running is not a failure. |
|
| 131 | - fi |
|
| 132 | - ;; |
|
| 133 | - restart) |
|
| 134 | - $0 stop |
|
| 135 | - $0 start |
|
| 136 | - ;; |
|
| 137 | - force-reload) |
|
| 138 | - echo -n "Reload service Hudson " |
|
| 139 | - $0 try-restart |
|
| 140 | - ;; |
|
| 141 | - reload) |
|
| 142 | - $0 restart |
|
| 143 | - ;; |
|
| 144 | - status) |
|
| 145 | - status hudson |
|
| 146 | - RETVAL=$? |
|
| 147 | - ;; |
|
| 148 | - probe) |
|
| 149 | - ## Optional: Probe for the necessity of a reload, print out the |
|
| 150 | - ## argument to this init script which is required for a reload. |
|
| 151 | - ## Note: probe is not (yet) part of LSB (as of 1.9) |
|
| 152 | - |
|
| 153 | - test "$HUDSON_CONFIG" -nt "$HUDSON_PID_FILE" && echo reload |
|
| 154 | - ;; |
|
| 155 | - *) |
|
| 156 | - echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload|probe}" |
|
| 157 | - exit 1 |
|
| 158 | - ;; |
|
| 159 | -esac |
|
| 160 | -exit $RETVAL |
configuration/hudson_instance_setup/hudson
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/build_server/files/usr/local/bin/hudson |
|
| ... | ... | \ No newline at end of file |
configuration/hudson_instance_setup/hudson.service
| ... | ... | @@ -1,13 +0,0 @@ |
| 1 | -[Unit] |
|
| 2 | -Description=The Hudson start-up / shut-down service |
|
| 3 | -Requires=-.mount mongod.service |
|
| 4 | -After=-.mount mongod.service |
|
| 5 | - |
|
| 6 | -[Install] |
|
| 7 | -RequiredBy=multi-user.target |
|
| 8 | - |
|
| 9 | -[Service] |
|
| 10 | -Type=oneshot |
|
| 11 | -RemainAfterExit=true |
|
| 12 | -ExecStart=/etc/init.d/hudson start |
|
| 13 | -ExecStop=/etc/init.d/hudson stop |
configuration/hudson_instance_setup/hudson.service
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/build_server/files/etc/systemd/system/hudson.service |
|
| ... | ... | \ No newline at end of file |
configuration/hudson_instance_setup/setup-hudson-server.sh
| ... | ... | @@ -14,7 +14,7 @@ |
| 14 | 14 | # tag key "image-type" and tag value "sailing-analytics-server". |
| 15 | 15 | if [ $# != 0 ]; then |
| 16 | 16 | SERVER=$1 |
| 17 | - ../sailing_server_setup/setup-sailing-server.sh ${SERVER} |
|
| 17 | + $(dirname $0)/../sailing_server_setup/setup-sailing-server.sh ${SERVER} |
|
| 18 | 18 | scp "${0}" ec2-user@${SERVER}: |
| 19 | 19 | ssh -A ec2-user@${SERVER} ./$( basename "${0}" ) |
| 20 | 20 | else |
| ... | ... | @@ -22,19 +22,36 @@ else |
| 22 | 22 | echo "Running on an AWS EC2 instance as user ${USER} / $(whoami), starting setup..." |
| 23 | 23 | # Install secrets |
| 24 | 24 | scp root@sapsailing.com:dev-secrets /tmp |
| 25 | - sudo mv /tmp/dev-secrets /root |
|
| 26 | - sudo chown root /root/secrets |
|
| 27 | - sudo chgrp root /root/secrets |
|
| 28 | - sudo chmod 600 /root/secrets |
|
| 25 | + scp root@sapsailing.com:hudson-aws-credentials /tmp |
|
| 26 | + sudo mv /tmp/dev-secrets /root/secrets |
|
| 27 | + sudo mkdir /root/.aws |
|
| 28 | + sudo mv /tmp/hudson-aws-credentials /root/.aws/credentials |
|
| 29 | + sudo chown root:root /root/secrets /root/.aws/credentials |
|
| 30 | + sudo chmod 600 /root/secrets /root/.aws/credentials |
|
| 31 | + # Make eu-west-1 the default region for any aws CLI interaction: |
|
| 32 | + sudo su - -c "aws configure set default.region eu-west-1" |
|
| 29 | 33 | # Create "hudson" user and clear its directory again which is to become a mount point |
| 30 | 34 | sudo adduser hudson |
| 31 | - sudo rm -rf /home/hudson/* /home/hudson/.* 2>/dev/null |
|
| 35 | + sudo su - hudson -c "rm -rf /home/hudson/* /home/hudson/.* 2>/dev/null" |
|
| 36 | + sudo mkdir /usr/lib/hudson |
|
| 37 | + sudo chown hudson /usr/lib/hudson |
|
| 38 | + sudo mkdir /var/log/hudson |
|
| 39 | + sudo chgrp hudson /var/log/hudson |
|
| 40 | + sudo chmod g+w /var/log/hudson |
|
| 41 | + sudo wget -O /usr/lib/hudson/hudson.war "https://static.sapsailing.com/hudson.war.patched-with-mail-1.6.2" |
|
| 32 | 42 | # Link hudson file to /etc/init.d |
| 33 | 43 | sudo ln -s /home/sailing/code/configuration/hudson_instance_setup/hudson /etc/init.d |
| 34 | 44 | # Link hudson service to /etc/systemd/system |
| 35 | 45 | sudo ln -s /home/sailing/code/configuration/hudson_instance_setup/hudson.service /etc/systemd/system |
| 36 | 46 | # Link Hudson system-wide config file: |
| 37 | 47 | sudo ln -s /home/sailing/code/configuration/hudson_instance_setup/sysconfig-hudson /etc/sysconfig/hudson |
| 48 | + # Link additional script files needed for Hudson build server control: |
|
| 49 | + sudo ln -s /home/sailing/code/configuration/launchhudsonslave /usr/local/bin |
|
| 50 | + sudo ln -s /home/sailing/code/configuration/launchhudsonslave-java11 /usr/local/bin |
|
| 51 | + sudo ln -s /home/sailing/code/configuration/aws-automation/getLatestImageOfType.sh /usr/local/bin |
|
| 52 | + # Enable NFS server |
|
| 53 | + sudo systemctl enable nfs-server.service |
|
| 54 | + sudo systemctl start nfs-server.service |
|
| 38 | 55 | # Enable the service: |
| 39 | 56 | sudo systemctl daemon-reload |
| 40 | 57 | sudo systemctl enable hudson.service |
| ... | ... | @@ -43,6 +60,30 @@ else |
| 43 | 60 | /home/hudson/android-sdk-linux 172.31.0.0/16(rw,nohide,no_root_squash) |
| 44 | 61 | EOF |
| 45 | 62 | " |
| 63 | + # Allow "hudson" user to launch EC2 instances: |
|
| 64 | + sudo su - -c "cat <<EOF >>/etc/sudoers.d/hudsoncanlaunchec2instances |
|
| 65 | +hudson ALL = (root) NOPASSWD: /usr/local/bin/launchhudsonslave |
|
| 66 | +hudson ALL = (root) NOPASSWD: /usr/local/bin/launchhudsonslave-java11 |
|
| 67 | +hudson ALL = (root) NOPASSWD: /usr/local/bin/getLatestImageOfType.sh |
|
| 68 | +EOF |
|
| 69 | +" |
|
| 70 | + # Install DEV server |
|
| 71 | + sudo su - sailing -c "mkdir /home/sailing/servers/DEV |
|
| 72 | +cd /home/sailing/servers/DEV |
|
| 73 | +cat <<EOF | /home/sailing/code/java/target/refreshInstance.sh auto-install-from-stdin |
|
| 74 | +USE_ENVIRONMENT=dev-server |
|
| 75 | +EOF |
|
| 76 | +" |
|
| 77 | + sudo cp /root/secrets /home/sailing/servers/DEV/configuration |
|
| 78 | + sudo chown sailing /home/sailing/servers/DEV/configuration/secrets |
|
| 79 | + sudo chgrp sailing /home/sailing/servers/DEV/configuration/secrets |
|
| 80 | + sudo cp /root/mail.properties /home/sailing/servers/DEV/configuration |
|
| 81 | + sudo chown sailing /home/sailing/servers/DEV/configuration/mail.properties |
|
| 82 | + sudo chgrp sailing /home/sailing/servers/DEV/configuration/mail.properties |
|
| 83 | + # Start the sailing.service with empty/no user data, so the next boot is recognized as a re-boot |
|
| 84 | + sudo systemctl start sailing.service |
|
| 85 | + sudo systemctl stop sailing.service |
|
| 86 | + sudo mount -a |
|
| 46 | 87 | else |
| 47 | 88 | echo "Not running on an AWS instance; refusing to run setup!" >&2 |
| 48 | 89 | echo "To prepare an instance running in AWS, provide its external IP as argument to this script." >&2 |
configuration/hudson_instance_setup/sysconfig-hudson
| ... | ... | @@ -1,88 +0,0 @@ |
| 1 | -## Path: Development/Hudson |
|
| 2 | -## Description: Configuration for the Hudson continuous build server |
|
| 3 | -## Type: string |
|
| 4 | -## Default: "/var/lib/hudson" |
|
| 5 | -## ServiceRestart: hudson |
|
| 6 | -# |
|
| 7 | -# Directory where Hudson store its configuration and working |
|
| 8 | -# files (checkouts, build reports, artifacts, ...). |
|
| 9 | -# |
|
| 10 | -HUDSON_HOME="/home/hudson/repo" |
|
| 11 | - |
|
| 12 | -## Type: string |
|
| 13 | -## Default: "" |
|
| 14 | -## ServiceRestart: hudson |
|
| 15 | -# |
|
| 16 | -# Java executable to run Hudson |
|
| 17 | -# When left empty, we'll try to find the suitable Java. |
|
| 18 | -# |
|
| 19 | - |
|
| 20 | -HUDSON_JAVA_CMD="/opt/sapjvm_8/bin/java" |
|
| 21 | - |
|
| 22 | -## Type: string |
|
| 23 | -## Default: "hudson" |
|
| 24 | -## ServiceRestart: hudson |
|
| 25 | -# |
|
| 26 | -# Unix user account that runs the Hudson daemon |
|
| 27 | -# Be careful when you change this, as you need to update |
|
| 28 | -# permissions of $HUDSON_HOME and /var/log/hudson. |
|
| 29 | -# |
|
| 30 | -HUDSON_USER="hudson" |
|
| 31 | - |
|
| 32 | -## Type: string |
|
| 33 | -## Default: "-Djava.awt.headless=true" |
|
| 34 | -## ServiceRestart: hudson |
|
| 35 | -# |
|
| 36 | -# Options to pass to java when running Hudson. |
|
| 37 | -# |
|
| 38 | -HUDSON_JAVA_OPTIONS="-Djava.awt.headless=true -Xmx2G -Dhudson.slaves.ChannelPinger.pingInterval=60 -Dhudson.slaves.ChannelPinger.pingIntervalSeconds=60 -Dhudson.slaves.ChannelPinger.pingTimeoutSeconds=60" |
|
| 39 | - |
|
| 40 | -## Type: integer(0:65535) |
|
| 41 | -## Default: 8080 |
|
| 42 | -## ServiceRestart: hudson |
|
| 43 | -# |
|
| 44 | -# Port Hudson is listening on. |
|
| 45 | -# |
|
| 46 | -HUDSON_PORT="8080" |
|
| 47 | - |
|
| 48 | -## Type: integer(1:9) |
|
| 49 | -## Default: 5 |
|
| 50 | -## ServiceRestart: hudson |
|
| 51 | -# |
|
| 52 | -# Debug level for logs -- the higher the value, the more verbose. |
|
| 53 | -# 5 is INFO. |
|
| 54 | -# |
|
| 55 | -HUDSON_DEBUG_LEVEL="5" |
|
| 56 | - |
|
| 57 | -## Type: yesno |
|
| 58 | -## Default: no |
|
| 59 | -## ServiceRestart: hudson |
|
| 60 | -# |
|
| 61 | -# Whether to enable access logging or not. |
|
| 62 | -# |
|
| 63 | -HUDSON_ENABLE_ACCESS_LOG="no" |
|
| 64 | - |
|
| 65 | -## Type: integer |
|
| 66 | -## Default: 100 |
|
| 67 | -## ServiceRestart: hudson |
|
| 68 | -# |
|
| 69 | -# Maximum number of HTTP worker threads. |
|
| 70 | -# |
|
| 71 | -HUDSON_HANDLER_MAX="100" |
|
| 72 | - |
|
| 73 | -## Type: integer |
|
| 74 | -## Default: 20 |
|
| 75 | -## ServiceRestart: hudson |
|
| 76 | -# |
|
| 77 | -# Maximum number of idle HTTP worker threads. |
|
| 78 | -# |
|
| 79 | -HUDSON_HANDLER_IDLE="20" |
|
| 80 | - |
|
| 81 | -## Type: string |
|
| 82 | -## Default: "" |
|
| 83 | -## ServiceRestart: hudson |
|
| 84 | -# |
|
| 85 | -# Pass arbitrary arguments to Hudson. |
|
| 86 | -# Full option list: java -jar hudson.war --help |
|
| 87 | -# |
|
| 88 | -HUDSON_ARGS="" |
configuration/hudson_instance_setup/sysconfig-hudson
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/build_server/files/etc/sysconfig/hudson |
|
| ... | ... | \ No newline at end of file |
configuration/hudson_slave_setup/README
| ... | ... | @@ -1,5 +0,0 @@ |
| 1 | -Deploy the .mount and .service units to /etc/systemd/system. |
|
| 2 | -Deploy the imageupgrade script to /usr/local/bin, |
|
| 3 | -furthermore the ../imageupgrade_functions.sh has to go to /usr/local/bin, best as symbolic link to files in git. |
|
| 4 | - |
|
| 5 | -See the script in wiki/howto/development/ci.md that shows how to set up such an instance from scratch. |
configuration/hudson_slave_setup/README
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/hudson_slave/README |
|
| ... | ... | \ No newline at end of file |
configuration/hudson_slave_setup/imageupgrade
| ... | ... | @@ -1,21 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | - |
|
| 3 | -# Script to deploy on an instance that has an ephemeral volume as /dev/nvme0n1 (adjust env var PARTITION if different) |
|
| 4 | -# Ensures the partition is xfs-formatted, any existing partition contents will be overwritten if formatted otherwise. |
|
| 5 | -# An existing xfs partition will be left alone. |
|
| 6 | -. imageupgrade_functions.sh |
|
| 7 | - |
|
| 8 | -get_ec2_user_data() { |
|
| 9 | - /usr/bin/ec2metadata --user |
|
| 10 | -} |
|
| 11 | - |
|
| 12 | -METADATA=$( get_ec2_user_data ) |
|
| 13 | -echo "Metadata: ${METADATA}" |
|
| 14 | -if echo "${METADATA}" | grep -q "^image-upgrade$"; then |
|
| 15 | - echo "Image upgrade..." |
|
| 16 | - LOGON_USER_HOME=/home/ubuntu |
|
| 17 | - run_apt_update_upgrade |
|
| 18 | - download_and_install_latest_sap_jvm_8 |
|
| 19 | - run_git_pull |
|
| 20 | - finalize |
|
| 21 | -fi |
configuration/hudson_slave_setup/imageupgrade
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/hudson_slave/files/usr/local/bin/imageupgrade |
|
| ... | ... | \ No newline at end of file |
configuration/hudson_slave_setup/imageupgrade.service
| ... | ... | @@ -1,12 +0,0 @@ |
| 1 | -[Unit] |
|
| 2 | -Description=Check for image-upgrade EC2 user data and triggers an image upgrade if found |
|
| 3 | -Requires=-.mount cloud-init.service |
|
| 4 | -After=-.mount cloud-init.service networking.service systemd-networkd.service |
|
| 5 | - |
|
| 6 | -[Install] |
|
| 7 | -WantedBy=multi-user.target |
|
| 8 | - |
|
| 9 | -[Service] |
|
| 10 | -Type=oneshot |
|
| 11 | -RemainAfterExit=true |
|
| 12 | -ExecStart=/usr/local/bin/imageupgrade |
configuration/hudson_slave_setup/imageupgrade.service
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/hudson_slave/files/etc/systemd/system/imageupgrade.service |
|
| ... | ... | \ No newline at end of file |
configuration/hudson_slave_setup/mounthudsonworkspace
| ... | ... | @@ -1,12 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | -# Expects an ephemeral volume to have been mounted at /ephemeral/data and |
|
| 3 | -# mounts that with a "bind" mount to /home/hudson/workspace. |
|
| 4 | -# The directory is then chown/chgrp'ed to hudson |
|
| 5 | -if [ -e /dev/nvme1n1 ]; then |
|
| 6 | - mkfs.ext4 /dev/nvme1n1 |
|
| 7 | - mount /dev/nvme1n1 /home/hudson/workspace |
|
| 8 | -else |
|
| 9 | - mount -o bind /ephemeral/data /home/hudson/workspace |
|
| 10 | -fi |
|
| 11 | -chgrp hudson /home/hudson/workspace |
|
| 12 | -chown hudson /home/hudson/workspace |
configuration/hudson_slave_setup/mounthudsonworkspace
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/hudson_slave/files/usr/local/bin/mounthudsonworkspace |
|
| ... | ... | \ No newline at end of file |
configuration/hudson_slave_setup/mounthudsonworkspace.service
| ... | ... | @@ -1,12 +0,0 @@ |
| 1 | -[Unit] |
|
| 2 | -Description=Mount Hudson Workspace |
|
| 3 | -After=ephemeral-data.mount |
|
| 4 | - |
|
| 5 | -[Service] |
|
| 6 | -Type=oneshot |
|
| 7 | -ExecStart=/usr/local/bin/mounthudsonworkspace |
|
| 8 | -RemainAfterExit=true |
|
| 9 | -StandardOutput=journal |
|
| 10 | - |
|
| 11 | -[Install] |
|
| 12 | -WantedBy=multi-user.target |
configuration/hudson_slave_setup/mounthudsonworkspace.service
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/hudson_slave/files/etc/systemd/system/mounthudsonworkspace.service |
|
| ... | ... | \ No newline at end of file |
configuration/imageupgrade.sh
| ... | ... | @@ -1,16 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | - |
|
| 3 | -# Upgrades the AWS EC2 instance that this script is assumed to be executed on. |
|
| 4 | -# The steps are as follows: |
|
| 5 | - |
|
| 6 | -. `dirname $0`/imageupgrade_functions.sh |
|
| 7 | - |
|
| 8 | -run_yum_update |
|
| 9 | -run_git_pull |
|
| 10 | -download_and_install_latest_sap_jvm_8 |
|
| 11 | -clean_logrotate_target |
|
| 12 | -clean_httpd_logs |
|
| 13 | -clean_servers_dir |
|
| 14 | -clean_startup_logs |
|
| 15 | -update_root_crontab |
|
| 16 | -finalize |
configuration/imageupgrade.sh
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +environments_scripts/sailing_server/files/usr/local/bin/imageupgrade.sh |
|
| ... | ... | \ No newline at end of file |
configuration/imageupgrade_functions.sh
| ... | ... | @@ -1,101 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | - |
|
| 3 | -# Upgrades the AWS EC2 instance that this script is assumed to be executed on. |
|
| 4 | -# The steps are as follows: |
|
| 5 | - |
|
| 6 | -REBOOT_INDICATOR=/var/run/is-rebooted |
|
| 7 | -LOGON_USER_HOME=/root |
|
| 8 | - |
|
| 9 | -run_yum_update() { |
|
| 10 | - echo "Updating packages using yum" >>/var/log/sailing.err |
|
| 11 | - yum -y update |
|
| 12 | -} |
|
| 13 | - |
|
| 14 | -run_apt_update_upgrade() { |
|
| 15 | - echo "Updating packages using apt" >>/var/log/sailing.err |
|
| 16 | - apt-get -y update; apt-get -y upgrade |
|
| 17 | - apt-get -y install linux-image-cloud-amd64 |
|
| 18 | - apt-get -y autoremove |
|
| 19 | -} |
|
| 20 | - |
|
| 21 | -run_git_pull() { |
|
| 22 | - echo "Pulling git to /home/sailing/code" >>/var/log/sailing.err |
|
| 23 | - su - sailing -c "cd code; git pull" |
|
| 24 | -} |
|
| 25 | - |
|
| 26 | -download_and_install_latest_sap_jvm_8() { |
|
| 27 | - echo "Downloading and installing latest SAP JVM 8 to /opt/sapjvm_8" >>/var/log/sailing.err |
|
| 28 | - vmpath=$( curl -s --cookie eula_3_1_agreed=tools.hana.ondemand.com/developer-license-3_1.txt https://tools.hana.ondemand.com | grep additional/sapjvm-8\..*-linux-x64.zip | head -1 | sed -e 's/^.*a href="\(additional\/sapjvm-8\..*-linux-x64\.zip\)".*/\1/' ) |
|
| 29 | - if [ -n "${vmpath}" ]; then |
|
| 30 | - echo "Found VM version ${vmpath}; upgrading installation at /opt/sapjvm_8" >>/var/log/sailing.err |
|
| 31 | - if [ -z "${TMP}" ]; then |
|
| 32 | - TMP=/tmp |
|
| 33 | - fi |
|
| 34 | - echo "Downloading SAP JVM 8 as ZIP file to ${TMP}/sapjvm8-linux-x64.zip" >>/var/log/sailing.err |
|
| 35 | - curl --cookie eula_3_1_agreed=tools.hana.ondemand.com/developer-license-3_1.txt "https://tools.hana.ondemand.com/${vmpath}" > ${TMP}/sapjvm8-linux-x64.zip 2>>/var/log/sailing.err |
|
| 36 | - cd /opt |
|
| 37 | - rm -rf sapjvm_8 |
|
| 38 | - if [ -f SIGNATURE.SMF ]; then |
|
| 39 | - rm -f SIGNATURE.SMF |
|
| 40 | - fi |
|
| 41 | - unzip ${TMP}/sapjvm8-linux-x64.zip >>/var/log/sailing.err |
|
| 42 | - rm -f ${TMP}/sapjvm8-linux-x64.zip |
|
| 43 | - rm -f SIGNATURE.SMF |
|
| 44 | - else |
|
| 45 | - echo "Did not find SAP JVM 8 at tools.hana.ondemand.com; not trying to upgrade" >>/var/log/sailing.err |
|
| 46 | - fi |
|
| 47 | -} |
|
| 48 | - |
|
| 49 | -clean_logrotate_target() { |
|
| 50 | - echo "Clearing logrorate-targets" >>/var/log/sailing.err |
|
| 51 | - rm -rf /var/log/logrotate-target/* |
|
| 52 | -} |
|
| 53 | - |
|
| 54 | -clean_httpd_logs() { |
|
| 55 | - echo "Clearing httpd logs" >>/var/log/sailing.err |
|
| 56 | - service httpd stop |
|
| 57 | - rm -rf /var/log/httpd/* |
|
| 58 | - rm -f /etc/httpd/conf.d/001-internals.conf |
|
| 59 | -} |
|
| 60 | - |
|
| 61 | -clean_startup_logs() { |
|
| 62 | - echo "Clearing bootstrap logs" >>/var/log/sailing.err |
|
| 63 | - rm -f /var/log/sailing* |
|
| 64 | - # Ensure that upon the next boot the reboot indicator is not present, indicating that it's the first boot |
|
| 65 | - rm "${REBOOT_INDICATOR}" |
|
| 66 | -} |
|
| 67 | - |
|
| 68 | -clean_servers_dir() { |
|
| 69 | - rm -rf /home/sailing/servers/* |
|
| 70 | -} |
|
| 71 | - |
|
| 72 | -update_root_crontab() { |
|
| 73 | - # The following assumes that /root/crontab is a symbolic link to /home/sailing/code/configuration/crontab |
|
| 74 | - # which has previously been updated by a git pull: |
|
| 75 | - cd /root |
|
| 76 | - crontab crontab |
|
| 77 | -} |
|
| 78 | - |
|
| 79 | -clean_root_ssh_dir_and_tmp() { |
|
| 80 | - echo "Cleaning up ${LOGON_USER_HOME}/.ssh" >>/var/log/sailing.err |
|
| 81 | - rm -rf ${LOGON_USER_HOME}/.ssh/* |
|
| 82 | - rm -f /var/run/last_change_aws_landscape_managers_ssh_keys |
|
| 83 | - rm -rf /tmp/image-upgrade-finished |
|
| 84 | -} |
|
| 85 | - |
|
| 86 | -get_ec2_user_data() { |
|
| 87 | - /opt/aws/bin/ec2-metadata -d | sed -e 's/^user-data: //' |
|
| 88 | -} |
|
| 89 | - |
|
| 90 | -finalize() { |
|
| 91 | - # Finally, shut down the node unless "no-shutdown" was provided in the user data, so that a new AMI can be constructed cleanly |
|
| 92 | - if get_ec2_user_data | grep "^no-shutdown$"; then |
|
| 93 | - echo "Shutdown disabled by no-shutdown option in user data. Remember to clean /root/.ssh when done." |
|
| 94 | - touch /tmp/image-upgrade-finished |
|
| 95 | - else |
|
| 96 | - # Only clean ${LOGON_USER_HOME}/.ssh directory and /tmp/image-upgrade-finished if the next step is shutdown / image creation |
|
| 97 | - clean_root_ssh_dir_and_tmp |
|
| 98 | - rm -f /var/log/sailing.err |
|
| 99 | - shutdown -h now & |
|
| 100 | - fi |
|
| 101 | -} |
configuration/imageupgrade_functions.sh
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +environments_scripts/sailing_server/files/usr/local/bin/imageupgrade_functions.sh |
|
| ... | ... | \ No newline at end of file |
configuration/launchhudsonslave
| ... | ... | @@ -1,31 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | -# Enable sudo for user hudson for this script by adding the following to /etc/sudoers.d/hudsoncanlaunchec2instances: |
|
| 3 | -# hudson ALL = (root) NOPASSWD: /usr/local/bin/launchhudsonslave |
|
| 4 | -# hudson ALL = (root) NOPASSWD: /usr/local/bin/launchhudsonslave-java11 |
|
| 5 | -# hudson ALL = (root) NOPASSWD: /usr/local/bin/getLatestImageOfType.sh |
|
| 6 | -AWS=/usr/bin/aws |
|
| 7 | -REGION=eu-west-1 |
|
| 8 | -HUDSON_SLAVE_AMI_ID=$( /usr/local/bin/getLatestImageOfType.sh hudson-slave ) |
|
| 9 | -echo Launching instance from AMI ${HUDSON_SLAVE_AMI_ID} ... |
|
| 10 | -instanceid=`$AWS ec2 run-instances --image-id $HUDSON_SLAVE_AMI_ID --count 1 --instance-type c5d.4xlarge --key-name Axel --security-groups "Sailing Analytics App" --region $REGION --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=Hudson Ubuntu Slave}]' --instance-initiated-shutdown-behavior terminate | tee /tmp/slavelaunch.out | jq .Instances[0].InstanceId | sed -e 's/"//g'` |
|
| 11 | -if [ "$instanceid" = "" ]; then |
|
| 12 | - echo Error launching instance |
|
| 13 | - exit 1 |
|
| 14 | -else |
|
| 15 | - echo Instance ID is $instanceid |
|
| 16 | - while [ "`$AWS ec2 describe-instances --region $REGION --instance-ids $instanceid | jq .Reservations[0].Instances[0].State.Name`" != "\"running\"" ]; do |
|
| 17 | - echo Instance $instanceid not running yet\; trying again... |
|
| 18 | - sleep 5 |
|
| 19 | - done |
|
| 20 | - echo Instance $instanceid seems running now |
|
| 21 | - private_ip=`$AWS ec2 describe-instances --region $REGION --instance-ids $instanceid | jq .Reservations[0].Instances[0].PrivateIpAddress | sed -e 's/"//g'` |
|
| 22 | - echo Probing for SSH on private IP $private_ip |
|
| 23 | - # Note: it's important to redirect stdin/stdout from/to /dev/null to ensure the Hudon master can properly connect stdin/stdout to the slave later |
|
| 24 | - while ! su - hudson -c "ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no hudson@$private_ip mkdir -p /home/hudson/workspace/___test___\; rmdir /home/hudson/workspace/___test___ </dev/null >/dev/null 2>/dev/null"; do |
|
| 25 | - echo SSH daemon not reachable yet. Trying again in a few seconds... |
|
| 26 | - sleep 10 |
|
| 27 | - done |
|
| 28 | - echo SSH daemon reached. State should be ready to connect to now. |
|
| 29 | - su - hudson -c "ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no hudson@$private_ip \"/opt/sapjvm_8/bin/java -jar slave.jar; sudo /sbin/shutdown -h now\"" |
|
| 30 | - $AWS ec2 terminate-instances --instance-ids $instanceid |
|
| 31 | -fi |
configuration/launchhudsonslave
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +environments_scripts/build_server/files/usr/local/bin/launchhudsonslave |
|
| ... | ... | \ No newline at end of file |
configuration/launchhudsonslave-java11
| ... | ... | @@ -1,31 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | -# Enable sudo for user hudson for this script by adding the following to /etc/sudoers.d/hudsoncanlaunchec2instances: |
|
| 3 | -# hudson ALL = (root) NOPASSWD: /usr/local/bin/launchhudsonslave |
|
| 4 | -# hudson ALL = (root) NOPASSWD: /usr/local/bin/launchhudsonslave-java11 |
|
| 5 | -# hudson ALL = (root) NOPASSWD: /usr/local/bin/getLatestImageOfType.sh |
|
| 6 | -AWS=/usr/bin/aws |
|
| 7 | -REGION=eu-west-1 |
|
| 8 | -HUDSON_SLAVE_AMI_ID=$( /usr/local/bin/getLatestImageOfType.sh hudson-slave-11 ) |
|
| 9 | -echo Launching instance from AMI ${HUDSON_SLAVE_AMI_ID} ... |
|
| 10 | -instanceid=`$AWS ec2 run-instances --image-id $HUDSON_SLAVE_AMI_ID --count 1 --instance-type c5d.4xlarge --key-name Axel --security-groups "Sailing Analytics App" --region $REGION --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=Hudson Ubuntu Slave Java 11}]' --instance-initiated-shutdown-behavior terminate | tee /tmp/slavelaunch.out | jq .Instances[0].InstanceId | sed -e 's/"//g'` |
|
| 11 | -if [ "$instanceid" = "" ]; then |
|
| 12 | - echo Error launching instance |
|
| 13 | - exit 1 |
|
| 14 | -else |
|
| 15 | - echo Instance ID is $instanceid |
|
| 16 | - while [ "`$AWS ec2 describe-instances --region $REGION --instance-ids $instanceid | jq .Reservations[0].Instances[0].State.Name`" != "\"running\"" ]; do |
|
| 17 | - echo Instance $instanceid not running yet\; trying again... |
|
| 18 | - sleep 5 |
|
| 19 | - done |
|
| 20 | - echo Instance $instanceid seems running now |
|
| 21 | - private_ip=`$AWS ec2 describe-instances --region $REGION --instance-ids $instanceid | jq .Reservations[0].Instances[0].PrivateIpAddress | sed -e 's/"//g'` |
|
| 22 | - echo Probing for SSH on private IP $private_ip |
|
| 23 | - # Note: it's important to redirect stdin/stdout from/to /dev/null to ensure the Hudon master can properly connect stdin/stdout to the slave later |
|
| 24 | - while ! su - hudson -c "ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no hudson@$private_ip mkdir -p /home/hudson/workspace/___test___\; rmdir /home/hudson/workspace/___test___ </dev/null >/dev/null 2>/dev/null"; do |
|
| 25 | - echo SSH daemon not reachable yet. Trying again in a few seconds... |
|
| 26 | - sleep 10 |
|
| 27 | - done |
|
| 28 | - echo SSH daemon reached. State should be ready to connect to now. |
|
| 29 | - su - hudson -c "ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no hudson@$private_ip \"/opt/sapjvm_8/bin/java -jar slave.jar; sudo /sbin/shutdown -h now\"" |
|
| 30 | - $AWS ec2 terminate-instances --instance-ids $instanceid |
|
| 31 | -fi |
configuration/launchhudsonslave-java11
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +environments_scripts/build_server/files/usr/local/bin/launchhudsonslave-java11 |
|
| ... | ... | \ No newline at end of file |
configuration/mongo_instance_setup/README
| ... | ... | @@ -1,16 +0,0 @@ |
| 1 | -Deploy the .mount and .service units to /etc/systemd/system. |
|
| 2 | -Deploy the ephemeralvolume and the patch-mongo-replicaset-name-from-ec2-metadata script to /usr/local/bin, |
|
| 3 | -furthermore the ../imageupgrade_functions.sh has to go to /usr/local/bin. |
|
| 4 | -Deploy mongod.conf to /etc and make sure that /root has a+r and a+x permissions because |
|
| 5 | -otherwise the mongod user won't be able to read through the symbolic link |
|
| 6 | -Link mongodb to /etc/logrotate.d |
|
| 7 | -Link crontab to /root/crontab and run "crontab crontab" as root. |
|
| 8 | - |
|
| 9 | -Run with optional EC2 user detail, e.g., as follows: |
|
| 10 | - |
|
| 11 | - REPLICA_SET_NAME=archive |
|
| 12 | - REPLICA_SET_PRIMARY=dbserver.internal.sapsailing.com:10201 |
|
| 13 | - |
|
| 14 | -This will automatically patch /etc/mongod.conf such that the replSetName property |
|
| 15 | -is set to the value of REPLICA_SET_NAME. Then, the instance will be added to |
|
| 16 | -the REPLICA_SET_PRIMARY's replica set. |
configuration/mongo_instance_setup/README
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/mongo_instance_setup/README |
|
| ... | ... | \ No newline at end of file |
configuration/mongo_instance_setup/add-as-replica
| ... | ... | @@ -1,25 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | -user_data=$( ec2-metadata -d | sed -e 's/^user-data: //' ) |
|
| 3 | -if echo ${user_data} | grep -q "^image-upgrade$"; then |
|
| 4 | - echo "Image upgrade... didn't expect to get this far because ephemeralvolume should have triggered upgrade and shutdown. Not registering MongoDB replica" |
|
| 5 | -else |
|
| 6 | - eval ${user_data} |
|
| 7 | - if [ -z "$REPLICA_SET_NAME" ]; then |
|
| 8 | - REPLICA_SET_NAME=live |
|
| 9 | - fi |
|
| 10 | - if [ -z "$REPLICA_SET_PRIMARY" ]; then |
|
| 11 | - REPLICA_SET_PRIMARY=mongo0.internal.sapsailing.com:27017 |
|
| 12 | - fi |
|
| 13 | - if [ -z "$REPLICA_SET_PRIORITY" ]; then |
|
| 14 | - REPLICA_SET_PRIORITY=1 |
|
| 15 | - fi |
|
| 16 | - if [ -z "$REPLICA_SET_VOTES" ]; then |
|
| 17 | - REPLICA_SET_VOTES=0 |
|
| 18 | - fi |
|
| 19 | - if [ \! -z "REPLICA_SET_PRIMARY" ]; then |
|
| 20 | - IP=$(ec2-metadata -o | sed -e 's/^local-ipv4: //') |
|
| 21 | - echo "rs.add({host: \"$IP:27017\", priority: $REPLICA_SET_PRIORITY, votes: $REPLICA_SET_VOTES})" | mongo "mongodb://$REPLICA_SET_PRIMARY/?replicaSet=$REPLICA_SET_NAME&retryWrites=true" |
|
| 22 | - else |
|
| 23 | - echo "rs.initiate()" | mongo |
|
| 24 | - fi |
|
| 25 | -fi |
configuration/mongo_instance_setup/add-as-replica
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/mongo_instance_setup/files/usr/local/bin/add-as-replica |
|
| ... | ... | \ No newline at end of file |
configuration/mongo_instance_setup/chownvarlibmongo.service
| ... | ... | @@ -1,14 +0,0 @@ |
| 1 | -[Unit] |
|
| 2 | -Description=Ensures all files under /var/lib/mongo are owned by mongod user/group |
|
| 3 | -Requires=ephemeralvolume.service |
|
| 4 | -After=ephemeralvolume.service |
|
| 5 | -Before=mongod.service |
|
| 6 | - |
|
| 7 | -[Install] |
|
| 8 | -RequiredBy=mongod.service |
|
| 9 | - |
|
| 10 | -[Service] |
|
| 11 | -Type=oneshot |
|
| 12 | -RemainAfterExit=true |
|
| 13 | -ExecStart=/bin/chown -R mongod /var/lib/mongo/ |
|
| 14 | -ExecStart=/bin/chgrp -R mongod /var/lib/mongo/ |
configuration/mongo_instance_setup/chownvarlibmongo.service
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/mongo_instance_setup/files/etc/systemd/system/chownvarlibmongo.service |
|
| ... | ... | \ No newline at end of file |
configuration/mongo_instance_setup/crontab
| ... | ... | @@ -1 +1,3 @@ |
| 1 | 1 | * * * * * export PATH=/bin:/usr/bin:/usr/local/bin; sleep $(( $RANDOM * 60 / 32768 )); update_authorized_keys_for_landscape_managers_if_changed $( cat /root/ssh-key-reader.token ) https://security-service.sapsailing.com /home/ec2-user 2>&1 >>/var/log/sailing.err |
| 2 | +# NOTICE: Please try to reference the customised crontabs at $GIT_HOME/configuration/crontabs or use |
|
| 3 | +# the build-crontab script. This file has been maintained for continuity, but is deprecated. |
|
| ... | ... | \ No newline at end of file |
configuration/mongo_instance_setup/ephemeralvolume
| ... | ... | @@ -1,33 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | - |
|
| 3 | -# Script to deploy on an instance that has an ephemeral volume as /dev/nvme0n1 (adjust env var PARTITION if different) |
|
| 4 | -# Ensures the partition is xfs-formatted, any existing partition contents will be overwritten if formatted otherwise. |
|
| 5 | -# An existing xfs partition will be left alone. |
|
| 6 | - |
|
| 7 | -METADATA=$( /bin/ec2-metadata -d | sed -e 's/^user-data: //' ) |
|
| 8 | -echo "Metadata: ${METADATA}" |
|
| 9 | -if echo "${METADATA}" | grep -q "^image-upgrade$"; then |
|
| 10 | - echo "Image upgrade; not trying to mount/format ephemeral volume; calling imageupgrade.sh instead..." |
|
| 11 | - imageupgrade.sh |
|
| 12 | -else |
|
| 13 | - echo "No image upgrade; looking for ephemeral volume and trying to format with xfs..." |
|
| 14 | - PARTITION=/dev/nvme0n1 |
|
| 15 | - if [ \! -e $PARTITION ]; then |
|
| 16 | - PARTITION=/dev/xvdb |
|
| 17 | - fi |
|
| 18 | - if [ \! -e $PARTITION ]; then |
|
| 19 | - echo "Neither /dev/nvme0n1 nor /dev/xvdb partition found; not formatting/mounting ephemeral volume" |
|
| 20 | - elif cat /proc/mounts | awk '{print $1;}' | grep "${PARTITION}"; then |
|
| 21 | - echo "Partition ${PARTITION} already mounted; not formatting/mounting ephemeral volume" |
|
| 22 | - else |
|
| 23 | - FSTYPE=$(blkid -p $PARTITION -s TYPE -o value) |
|
| 24 | - if [ "$FSTYPE" != "xfs" ]; then |
|
| 25 | - echo FSTYPE was "$FSTYPE" but should have been xfs. Formatting $PARTITION... |
|
| 26 | - mkfs.xfs -f $PARTITION |
|
| 27 | - else |
|
| 28 | - echo FSTYPE was "$FSTYPE" which is just right :-\) |
|
| 29 | - fi |
|
| 30 | - # mount the thing to /var/lib/mongo |
|
| 31 | - mount $PARTITION /var/lib/mongo |
|
| 32 | - fi |
|
| 33 | -fi |
configuration/mongo_instance_setup/ephemeralvolume
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/mongo_instance_setup/files/usr/local/bin/ephemeralvolume |
|
| ... | ... | \ No newline at end of file |
configuration/mongo_instance_setup/ephemeralvolume.service
| ... | ... | @@ -1,11 +0,0 @@ |
| 1 | -[Unit] |
|
| 2 | -Description=Ensures /dev/nvme0n1 or /dev/xvdb is XFS-formatted |
|
| 3 | -Requires=-.mount cloud-init.service network.service |
|
| 4 | -After=-.mount cloud-init.service network.service |
|
| 5 | - |
|
| 6 | -[Install] |
|
| 7 | - |
|
| 8 | -[Service] |
|
| 9 | -Type=oneshot |
|
| 10 | -RemainAfterExit=true |
|
| 11 | -ExecStart=/usr/local/bin/ephemeralvolume |
configuration/mongo_instance_setup/ephemeralvolume.service
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/mongo_instance_setup/files/etc/systemd/system/ephemeralvolume.service |
|
| ... | ... | \ No newline at end of file |
configuration/mongo_instance_setup/imageupgrade.sh
| ... | ... | @@ -1,25 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | - |
|
| 3 | -# Upgrades the AWS EC2 MongoDB instance that this script is assumed to be executed on. |
|
| 4 | -# The steps are as follows: |
|
| 5 | - |
|
| 6 | -. imageupgrade_functions.sh |
|
| 7 | - |
|
| 8 | -run_git_pull_root() { |
|
| 9 | - echo "Pulling git to /root/code" >>/var/log/sailing.err |
|
| 10 | - cd /root/code |
|
| 11 | - git pull |
|
| 12 | -} |
|
| 13 | - |
|
| 14 | -clean_mongo_pid() { |
|
| 15 | - rm -f /var/run/mongodb/mongod.pid |
|
| 16 | -} |
|
| 17 | - |
|
| 18 | -LOGON_USER_HOME=/home/ec2-user |
|
| 19 | - |
|
| 20 | -run_yum_update |
|
| 21 | -run_git_pull_root |
|
| 22 | -clean_startup_logs |
|
| 23 | -update_root_crontab |
|
| 24 | -clean_mongo_pid |
|
| 25 | -finalize |
configuration/mongo_instance_setup/imageupgrade.sh
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/mongo_instance_setup/files/usr/local/bin/imageupgrade.sh |
|
| ... | ... | \ No newline at end of file |
configuration/mongo_instance_setup/mongo-replica-set.service
| ... | ... | @@ -1,16 +0,0 @@ |
| 1 | -[Unit] |
|
| 2 | -Description=If REPLICA_SET_NAME EC2 user data is provided, add this node to the replica set of REPLICA_SET_PRIMARY |
|
| 3 | -Requires=mongod.service |
|
| 4 | -After=mongod.service |
|
| 5 | -Requires=cloud-init.service |
|
| 6 | -After=cloud-init.service |
|
| 7 | - |
|
| 8 | -[Install] |
|
| 9 | -WantedBy=multi-user.target |
|
| 10 | - |
|
| 11 | -[Service] |
|
| 12 | -Type=oneshot |
|
| 13 | -RemainAfterExit=true |
|
| 14 | -ExecStart=/usr/local/bin/add-as-replica |
|
| 15 | -ExecStop=/usr/local/bin/remove-as-replica |
|
| 16 | -TimeoutStopSpec=120s |
configuration/mongo_instance_setup/mongo-replica-set.service
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/mongo_instance_setup/files/etc/systemd/system/mongo-replica-set.service |
|
| ... | ... | \ No newline at end of file |
configuration/mongo_instance_setup/mongod.conf
| ... | ... | @@ -1,48 +0,0 @@ |
| 1 | -# mongod.conf |
|
| 2 | - |
|
| 3 | -# for documentation of all options, see: |
|
| 4 | -# http://docs.mongodb.org/manual/reference/configuration-options/ |
|
| 5 | - |
|
| 6 | -# where to write logging data. |
|
| 7 | -systemLog: |
|
| 8 | - destination: file |
|
| 9 | - logAppend: true |
|
| 10 | - path: /var/log/mongodb/mongod.log |
|
| 11 | - |
|
| 12 | -# Where and how to store data. |
|
| 13 | -storage: |
|
| 14 | - dbPath: /var/lib/mongo |
|
| 15 | - journal: |
|
| 16 | - enabled: true |
|
| 17 | - directoryPerDB: true |
|
| 18 | -# engine: |
|
| 19 | -# mmapv1: |
|
| 20 | -# wiredTiger: |
|
| 21 | - |
|
| 22 | -# how the process runs |
|
| 23 | -processManagement: |
|
| 24 | - fork: true # fork and run in background |
|
| 25 | - pidFilePath: /var/run/mongodb/mongod.pid # location of pidfile |
|
| 26 | - timeZoneInfo: /usr/share/zoneinfo |
|
| 27 | - |
|
| 28 | -# network interfaces |
|
| 29 | -net: |
|
| 30 | - port: 27017 |
|
| 31 | -# bindIp: 127.0.0.1 # Listen to local interface only, comment to listen on all interfaces. |
|
| 32 | -# bindIp: 172.31.33.146 |
|
| 33 | - bindIp: 0.0.0.0 |
|
| 34 | - |
|
| 35 | -#security: |
|
| 36 | - |
|
| 37 | -#operationProfiling: |
|
| 38 | - |
|
| 39 | -replication: |
|
| 40 | - replSetName: "live" |
|
| 41 | - |
|
| 42 | -#sharding: |
|
| 43 | - |
|
| 44 | -## Enterprise-Only Options |
|
| 45 | - |
|
| 46 | -#auditLog: |
|
| 47 | - |
|
| 48 | -#snmp: |
configuration/mongo_instance_setup/mongod.conf
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/mongo_instance_setup/files/etc/mongod.conf |
|
| ... | ... | \ No newline at end of file |
configuration/mongo_instance_setup/mongodb
| ... | ... | @@ -1,9 +0,0 @@ |
| 1 | -compress |
|
| 2 | -/var/log/mongodb/mongod.log |
|
| 3 | -{ |
|
| 4 | - rotate 5 |
|
| 5 | - weekly |
|
| 6 | - postrotate |
|
| 7 | - /usr/bin/killall -SIGUSR1 mongod |
|
| 8 | - endscript |
|
| 9 | -} |
configuration/mongo_instance_setup/mongodb
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/mongo_instance_setup/files/etc/logrotate.d/mongodb |
|
| ... | ... | \ No newline at end of file |
configuration/mongo_instance_setup/patch-mongo-replicaset-name-from-ec2-metadata
| ... | ... | @@ -1,8 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | -REPLICA_SET_NAME=$(ec2-metadata | grep REPLICA_SET_NAME | sed -e 's/^user-data: //' | sed -e 's/^REPLICA_SET_NAME=//') |
|
| 3 | -echo Replica set name: $REPLICA_SET_NAME |
|
| 4 | -if [ \! -z "$REPLICA_SET_NAME" ]; then |
|
| 5 | - echo "Not empty. Patching /etc/mongod.conf..." |
|
| 6 | - sed -i -e "s/replSetName: .*$/replSetName: $REPLICA_SET_NAME/" /etc/mongod.conf |
|
| 7 | - echo "Done" |
|
| 8 | -fi |
configuration/mongo_instance_setup/patch-mongo-replicaset-name-from-ec2-metadata
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/mongo_instance_setup/files/usr/local/bin/patch-mongo-replicaset-name-from-ec2-metadata |
|
| ... | ... | \ No newline at end of file |
configuration/mongo_instance_setup/patch-mongo-replicaset-name-from-ec2-metadata.service
| ... | ... | @@ -1,15 +0,0 @@ |
| 1 | -[Unit] |
|
| 2 | -Description=Check EC2 metadata for MongoDB Replica Set Name and patch /etc/mongod.conf accordingly |
|
| 3 | -Requires=ephemeralvolume.service |
|
| 4 | -After=ephemeralvolume.service |
|
| 5 | -Requires=cloud-init.service |
|
| 6 | -After=cloud-init.service |
|
| 7 | -Before=mongod.service |
|
| 8 | - |
|
| 9 | -[Install] |
|
| 10 | -RequiredBy=mongod.service |
|
| 11 | - |
|
| 12 | -[Service] |
|
| 13 | -Type=oneshot |
|
| 14 | -RemainAfterExit=true |
|
| 15 | -ExecStart=/usr/local/bin/patch-mongo-replicaset-name-from-ec2-metadata |
configuration/mongo_instance_setup/patch-mongo-replicaset-name-from-ec2-metadata.service
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/mongo_instance_setup/files/etc/systemd/system/patch-mongo-replicaset-name-from-ec2-metadata.service |
|
| ... | ... | \ No newline at end of file |
configuration/mongo_instance_setup/remove-as-replica
| ... | ... | @@ -1,6 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | -eval $( ec2-metadata -d | sed -e 's/^user-data: //' ) |
|
| 3 | -if [ \! -z "REPLICA_SET_PRIMARY" ]; then |
|
| 4 | - IP=$(ec2-metadata -o | sed -e 's/^local-ipv4: //') |
|
| 5 | - echo "rs.remove(\"$IP:27017\")" | mongo "mongodb://$REPLICA_SET_PRIMARY/?replicaSet=$REPLICA_SET_NAME&retryWrites=true" |
|
| 6 | -fi |
configuration/mongo_instance_setup/remove-as-replica
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/mongo_instance_setup/files/usr/local/bin/remove-as-replica |
|
| ... | ... | \ No newline at end of file |
configuration/mysql_instance_setup/crontab-ec2-user
| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | +* * * * * export PATH=/bin:/usr/bin:/usr/local/bin; sleep $(( $RANDOM * 60 / 32768 )); update_authorized_keys_for_landscape_managers_if_changed $( cat /home/ec2-user/ssh-key-reader.token ) https://security-service.sapsailing.com /home/ec2-user |
|
| 2 | +# NOTICE: Please try to reference the customised crontabs at $GIT_HOME/configuration/crontabs or use |
|
| 3 | +# the build-crontab script. This file has been maintained for continuity, but is deprecated. |
configuration/mysql_instance_setup/setup-mysql-server.sh
| ... | ... | @@ -0,0 +1,70 @@ |
| 1 | +#!/bin/bash |
|
| 2 | +# Usage: ${0} [ -b {bugs-password] ] [ -r {root-password} ] {instance-ip} |
|
| 3 | +# Deploy with Amazon Linux 2023 |
|
| 4 | + |
|
| 5 | +# Read options and assign to variables: |
|
| 6 | +options='b:r:' |
|
| 7 | +while getopts $options option |
|
| 8 | +do |
|
| 9 | + case $option in |
|
| 10 | + b) BUGS_PW="${OPTARG}" ;; |
|
| 11 | + r) ROOT_PW="${OPTARG}" ;; |
|
| 12 | + \?) echo "Invalid option" |
|
| 13 | + exit 4;; |
|
| 14 | + esac |
|
| 15 | +done |
|
| 16 | +if [ -z "${ROOT_PW}" ]; then |
|
| 17 | + echo -n "MySQL password for user root: " |
|
| 18 | + read -s ROOT_PW |
|
| 19 | + echo |
|
| 20 | +fi |
|
| 21 | +if [ -z "${BUGS_PW}" ]; then |
|
| 22 | + echo -n "MySQL password for user bugs: " |
|
| 23 | + read -s BUGS_PW |
|
| 24 | + echo |
|
| 25 | +fi |
|
| 26 | +shift $((OPTIND-1)) |
|
| 27 | +if [ $# != 0 ]; then |
|
| 28 | + SERVER=$1 |
|
| 29 | + scp -o StrictHostKeyChecking=false "${0}" ec2-user@${SERVER}: |
|
| 30 | + ssh -o StrictHostKeyChecking=false -A ec2-user@${SERVER} "./$( basename "${0}" ) -r \"${ROOT_PW}\" -b \"${BUGS_PW}\"" |
|
| 31 | +else |
|
| 32 | + BACKUP_FILE=/home/ec2-user/backupdb.sql |
|
| 33 | + backupdbNOLOCK=/home/ec2-user/backupdbNOLOCK.sql |
|
| 34 | + # Install cron job for ssh key update for landscape managers |
|
| 35 | + scp -o StrictHostKeyChecking=false root@sapsailing.com:/home/wiki/gitwiki/configuration/update_authorized_keys_for_landscape_managers /tmp |
|
| 36 | + sudo mv /tmp/update_authorized_keys_for_landscape_managers /usr/local/bin |
|
| 37 | + scp -o StrictHostKeyChecking=false root@sapsailing.com:/home/wiki/gitwiki/configuration/update_authorized_keys_for_landscape_managers_if_changed /tmp |
|
| 38 | + sudo mv /tmp/update_authorized_keys_for_landscape_managers_if_changed /usr/local/bin |
|
| 39 | + scp -o StrictHostKeyChecking=false root@sapsailing.com:/home/wiki/gitwiki/configuration/mysql_instance_setup/crontab-ec2-user /home/ec2-user/crontab |
|
| 40 | + scp -o StrictHostKeyChecking=false root@sapsailing.com:ssh-key-reader.token /home/ec2-user |
|
| 41 | + sudo chown ec2-user /home/ec2-user/ssh-key-reader.token |
|
| 42 | + sudo chgrp ec2-user /home/ec2-user/ssh-key-reader.token |
|
| 43 | + sudo chmod 600 /home/ec2-user/ssh-key-reader.token |
|
| 44 | + # Install packages for MariaDB and cron/anacron/crontab: |
|
| 45 | + sudo yum update -y |
|
| 46 | + sudo yum -y install mariadb105-server cronie |
|
| 47 | + sudo su -c "printf '\n[mysqld]\nlog_bin = /var/log/mariadb/mysql-bin.log\n' >> /etc/my.cnf.d/mariadb-server.cnf" |
|
| 48 | + sudo systemctl enable mariadb.service |
|
| 49 | + sudo systemctl start mariadb.service |
|
| 50 | + sudo systemctl enable crond.service |
|
| 51 | + sudo systemctl start crond.service |
|
| 52 | + crontab /home/ec2-user/crontab |
|
| 53 | + echo "Creating backup through mysql client on sapsailing.com..." |
|
| 54 | + ssh -o StrictHostKeyChecking=false root@sapsailing.com "mysqldump --all-databases -h mysql.internal.sapsailing.com --user=root --password=${ROOT_PW} --master-data --skip-lock-tables --lock-tables=0" >> ${BACKUP_FILE} |
|
| 55 | + # the two lock options are supposed to ignore table locks, but the following removes a problematic exception. |
|
| 56 | + echo "Removing lock on log table which causes failures" |
|
| 57 | + cat ${BACKUP_FILE} | sed "/LOCK TABLES \`transaction_registry\`/,/UNLOCK TABLES;/d" >${backupdbNOLOCK} |
|
| 58 | + echo "Importing backup locally..." |
|
| 59 | + sudo mysql -u root -h localhost <${backupdbNOLOCK} |
|
| 60 | + sudo mysql -u root -p${ROOT_PW} -e "FLUSH PRIVILEGES;" |
|
| 61 | + rm ${BACKUP_FILE} |
|
| 62 | + rm ${backupdbNOLOCK} |
|
| 63 | + sudo systemctl stop mariadb.service |
|
| 64 | + sudo systemctl start mariadb.service |
|
| 65 | + sudo mysql -u root -p${ROOT_PW} -e "select count(bug_id) from bugs.bugs;" |
|
| 66 | + echo 'Test your DB, e.g., by counting bugs: sudo mysql -u root -p -e "use bugs; select count(*) from bugs;"' |
|
| 67 | + echo "If you like what you see, switch to the new DB by updating the mysql.internal.sapsailing.com DNS record to this instance," |
|
| 68 | + echo "make sure the instance has the \"Database and Messaging\" security group set," |
|
| 69 | + echo "and tag the instance's root volume with the WeeklySailingInfrastructureBackup=Yes tag." |
|
| 70 | +fi |
configuration/notify-unhealthy-mongodb
| ... | ... | @@ -1,17 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | -MONGODBS="mongodb://dbserver.internal.sapsailing.com:10201/?replicaSet=archive mongodb://dbserver.internal.sapsailing.com:10202/?replicaSet=slow mongodb://dbserver.internal.sapsailing.com:10203/?replicaSet=live" |
|
| 3 | -MAILINGLIST="/home/trac/mailinglists/unhealthy-mongo-list" |
|
| 4 | -for db in $MONGODBS; do |
|
| 5 | - echo "rs.status()" | mongo --quiet "$db" | grep '\("set"\)\|\("name"\)\|\("health"\)\|\("stateStr"\)' | sed -e 's/^.* : "\?//' -e 's/"\?,$//' | ( |
|
| 6 | - read replicaset |
|
| 7 | - while read server; do |
|
| 8 | - read health |
|
| 9 | - read state |
|
| 10 | - if [ "$health" != "1" ]; then |
|
| 11 | - echo "MongoDB replica set $replicaset unhealthy, node $server is in state $state\ |
|
| 12 | - |
|
| 13 | -Check status of $db urgently. To unsubscribe, change $MAILINGLIST on `hostname`." | mail -s "MongoDB Replica Set \"$replicaset\" Unhealthy" `cat "$MAILINGLIST"` |
|
| 14 | - fi |
|
| 15 | - done |
|
| 16 | - ) |
|
| 17 | -done |
configuration/notify-unhealthy-mongodb
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +environments_scripts/central_reverse_proxy/files/home/trac/bin/notify-unhealthy-mongodb |
|
| ... | ... | \ No newline at end of file |
configuration/rabbitmq_instance_setup/crontab-admin
| ... | ... | @@ -0,0 +1,2 @@ |
| 1 | +SHELL=/bin/bash |
|
| 2 | +* * * * * export PATH=/bin:/usr/bin:/usr/local/bin; sleep $(( $RANDOM * 60 / 32768 )); update_authorized_keys_for_landscape_managers_if_changed $( cat /home/admin/ssh-key-reader.token ) https://security-service.sapsailing.com /home/admin |
configuration/rabbitmq_instance_setup/setup-rabbitmq-server.sh
| ... | ... | @@ -0,0 +1,43 @@ |
| 1 | +#!/bin/bash |
|
| 2 | +# Apply this to an instance launched from a barebones Debian 12 image with ~16GB of root volume size. |
|
| 3 | +# As a result, you'll get a ready-to-use RabbitMQ server that is running on the default port and accepts |
|
| 4 | +# connections with the guest user log-in also from non-localhost addresses. |
|
| 5 | +if [ $# != 0 ]; then |
|
| 6 | + SERVER=$1 |
|
| 7 | + scp -o StrictHostKeyChecking=false "${0}" admin@${SERVER}: |
|
| 8 | + ssh -o StrictHostKeyChecking=false -A admin@${SERVER} ./$( basename "${0}" ) |
|
| 9 | +else |
|
| 10 | + # Fix the non-sensical use of "dash" as the default shell: |
|
| 11 | + sudo rm /usr/bin/sh |
|
| 12 | + sudo ln -s /usr/bin/bash /usr/bin/sh |
|
| 13 | + # Install cron job for ssh key update for landscape managers |
|
| 14 | + scp -o StrictHostKeyChecking=false root@sapsailing.com:/home/wiki/gitwiki/configuration/update_authorized_keys_for_landscape_managers /tmp |
|
| 15 | + sudo mv /tmp/update_authorized_keys_for_landscape_managers /usr/local/bin |
|
| 16 | + scp -o StrictHostKeyChecking=false root@sapsailing.com:/home/wiki/gitwiki/configuration/update_authorized_keys_for_landscape_managers_if_changed /tmp |
|
| 17 | + sudo mv /tmp/update_authorized_keys_for_landscape_managers_if_changed /usr/local/bin |
|
| 18 | + scp -o StrictHostKeyChecking=false root@sapsailing.com:/home/wiki/gitwiki/configuration/rabbitmq_instance_setup/crontab-admin /home/admin/crontab |
|
| 19 | + scp -o StrictHostKeyChecking=false root@sapsailing.com:ssh-key-reader.token /home/admin |
|
| 20 | + sudo chown admin /home/admin/ssh-key-reader.token |
|
| 21 | + sudo chgrp admin /home/admin/ssh-key-reader.token |
|
| 22 | + sudo chmod 600 /home/admin/ssh-key-reader.token |
|
| 23 | + # Install packages for MariaDB and cron/anacron/crontab: |
|
| 24 | + sudo apt-get -y update |
|
| 25 | + sudo DEBIAN_FRONTEND=noninteractive apt-get -yq -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confnew upgrade |
|
| 26 | + sudo DEBIAN_FRONTEND=noninteractive apt-get -yq -o Dpkg::Options::=--force-confdef -o Dpkg::Options::=--force-confnew install rabbitmq-server systemd-cron jq syslog-ng |
|
| 27 | + sudo touch /var/run/last_change_aws_landscape_managers_ssh_keys |
|
| 28 | + sudo chown admin:admin /var/run/last_change_aws_landscape_managers_ssh_keys |
|
| 29 | + crontab /home/admin/crontab |
|
| 30 | + # Wait for RabbitMQ to become available; note that install under apt also means start... |
|
| 31 | + sleep 10 |
|
| 32 | + sudo rabbitmq-plugins enable rabbitmq_management |
|
| 33 | + # Allow guest login from non-localhost IPs: |
|
| 34 | + sudo su - -c "cat <<EOF >>/etc/rabbitmq/rabbitmq.conf |
|
| 35 | +loopback_users = none |
|
| 36 | +EOF |
|
| 37 | +" |
|
| 38 | + sudo systemctl restart rabbitmq-server.service |
|
| 39 | + echo 'Test your DB, e.g., by counting bugs: sudo mysql -u root -p -e "use bugs; select count(*) from bugs;"' |
|
| 40 | + echo "If you like what you see, switch to the new DB by updating the mysql.internal.sapsailing.com DNS record to this instance," |
|
| 41 | + echo "make sure the instance has the \"Database and Messaging\" security group set," |
|
| 42 | + echo "and tag the instance's root volume with the WeeklySailingInfrastructureBackup=Yes tag." |
|
| 43 | +fi |
configuration/sailing
| ... | ... | @@ -1,133 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | -# |
|
| 3 | -# sailing Starts sailing services |
|
| 4 | -# |
|
| 5 | -# chkconfig: 2345 95 10 |
|
| 6 | -# description: Sailing contains all sailing services |
|
| 7 | -# |
|
| 8 | -### BEGIN INIT INFO |
|
| 9 | -# Provides: sailing |
|
| 10 | -# Required-Start: $local_fs $network $named $remote_fs |
|
| 11 | -# Should-Start: |
|
| 12 | -# Required-Stop: |
|
| 13 | -# Should-Stop: |
|
| 14 | -# Default-Start: 2 3 4 5 |
|
| 15 | -# Default-Stop: 0 1 6 |
|
| 16 | -# Short-Description: The sailing service |
|
| 17 | -# Description: Start all sailing services required for this instance |
|
| 18 | -### END INIT INFO |
|
| 19 | - |
|
| 20 | -# Source function library. |
|
| 21 | -. /etc/init.d/functions |
|
| 22 | - |
|
| 23 | -RETVAL=0 |
|
| 24 | - |
|
| 25 | -SERVERS_DIR=/home/sailing/servers |
|
| 26 | -cd "${SERVERS_DIR}" |
|
| 27 | -JAVA_START_INSTANCES="$(find * -type d -prune)" |
|
| 28 | -GIT_REPOSITORY=/home/sailing/code |
|
| 29 | -if [ -x /bin/ec2-metadata ]; then |
|
| 30 | - EC2_METADATA_CMD=/bin/ec2-metadata |
|
| 31 | -elif [ -x /usr/bin/ec2-metadata ]; then |
|
| 32 | - EC2_METADATA_CMD=/usr/bin/ec2-metadata |
|
| 33 | -else |
|
| 34 | - EC2_METADATA_CMD=/opt/aws/bin/ec2-metadata |
|
| 35 | -fi |
|
| 36 | -REBOOT_INDICATOR=/var/run/is-rebooted |
|
| 37 | -SSH_KEY_READER_BEARER_TOKEN=/root/ssh-key-reader.token |
|
| 38 | - |
|
| 39 | -echo "Executing with $1 at `date`" >>/var/log/sailing.err |
|
| 40 | - |
|
| 41 | -start_servers() { |
|
| 42 | - echo "Initializing local MongoDB replica set \"replica\"..." |
|
| 43 | - while ! echo "rs.initiate()" | mongo; do |
|
| 44 | - echo "MongoDB not ready yet; waiting and trying again..." |
|
| 45 | - sleep 5 |
|
| 46 | - done |
|
| 47 | - /usr/local/bin/update_authorized_keys_for_landscape_managers $( cat ${SSH_KEY_READER_BEARER_TOKEN} ) https://security-service.sapsailing.com /root 2>&1 >>/var/log/sailing.err |
|
| 48 | - cp /home/sailing/code/configuration/cp_root_mail_properties /usr/local/bin |
|
| 49 | - chown root /usr/local/bin/cp_root_mail_properties |
|
| 50 | - chgrp root /usr/local/bin/cp_root_mail_properties |
|
| 51 | - chmod 755 /usr/local/bin/cp_root_mail_properties |
|
| 52 | - cp /home/sailing/code/configuration/cp_root_mail_properties_sudoers /etc/sudoers.d |
|
| 53 | - if which $EC2_METADATA_CMD && $EC2_METADATA_CMD -d | sed "s/user-data\: //g" | grep "^image-upgrade$"; then |
|
| 54 | - echo "Found image-upgrade in EC2 user data; upgrading image, then probably shutting down for AMI creation depending on the no-shutdown user data string..." >>/var/log/sailing.err |
|
| 55 | - $GIT_REPOSITORY/configuration/imageupgrade.sh |
|
| 56 | - else |
|
| 57 | - echo "No image-upgrade request found in EC2 user data $($EC2_METADATA_CMD -d); proceeding with regular server launch..." >>/var/log/sailing.err |
|
| 58 | - echo "Servers to launch: ${JAVA_START_INSTANCES}" >>/var/log/sailing.err |
|
| 59 | - if [ -f "${REBOOT_INDICATOR}" ]; then |
|
| 60 | - echo "This is a re-boot. No EC2 user data is evaluated for server configuration; no server configuration is performed. Only configured applications are launched." >>/var/log/sailing.err |
|
| 61 | - for conf in ${JAVA_START_INSTANCES}; do |
|
| 62 | - su - sailing -c "cd ${SERVERS_DIR}/${conf} && ./start" 2>>/var/log/sailing.err >>/var/log/sailing.err |
|
| 63 | - done |
|
| 64 | - else |
|
| 65 | - echo "This is a first-time boot. EC2 user data is evaluated for potential application deployment and configuration, and applications are launched." >>/var/log/sailing.err |
|
| 66 | - FIRST_SERVER=$( eval $( ${EC2_METADATA_CMD} -d | sed -e 's/^user-data: //' ); echo $SERVER_NAME ) |
|
| 67 | - if [ "${FIRST_SERVER}" = "" ]; then |
|
| 68 | - echo "No SERVER_NAME provided; not configuring/starting any application processes" >>/var/log/sailing.err |
|
| 69 | - else |
|
| 70 | - echo "Server to configure and start: ${FIRST_SERVER}" >>/var/log/sailing.err |
|
| 71 | - configure_and_start_server "${FIRST_SERVER}" |
|
| 72 | - fi |
|
| 73 | - echo 1 >"${REBOOT_INDICATOR}" |
|
| 74 | - fi |
|
| 75 | - fi |
|
| 76 | -} |
|
| 77 | - |
|
| 78 | -# Call with the server directory name (not the full path, just a single element from ${JAVA_START_INSTANCE}) as parameter |
|
| 79 | -# Example: configure_and_start_server server |
|
| 80 | -# This is expected to be called only in case there is only one server to configure; otherwise, the same EC2 user data |
|
| 81 | -# would get applied to all application configurations which would not be a good idea. |
|
| 82 | -configure_and_start_server() { |
|
| 83 | - conf="$1" |
|
| 84 | - mkdir -p "${SERVERS_DIR}/${conf}" >/dev/null 2>/dev/null |
|
| 85 | - chown sailing "${SERVERS_DIR}/${conf}" |
|
| 86 | - chgrp sailing "${SERVERS_DIR}/${conf}" |
|
| 87 | - # If there is a secret /root/mail.properties, copy it into the default server's configuration directory: |
|
| 88 | - /usr/local/bin/cp_root_mail_properties "${conf}" |
|
| 89 | - su - sailing -c "cd ${SERVERS_DIR}/${conf} && ${GIT_REPOSITORY}/java/target/refreshInstance.sh auto-install; ./start" 2>>/var/log/sailing.err >>/var/log/sailing.err |
|
| 90 | - pushd ${SERVERS_DIR}/${conf} |
|
| 91 | - ./defineReverseProxyMappings.sh 2>>/var/log/sailing.err >>/var/log/sailing.err |
|
| 92 | - popd |
|
| 93 | - RETVAL=$? |
|
| 94 | - [ $RETVAL -eq 0 ] && success || failure |
|
| 95 | -} |
|
| 96 | - |
|
| 97 | -stop_servers() { |
|
| 98 | - for conf in $JAVA_START_INSTANCES; do |
|
| 99 | - echo "Stopping Java server $conf" >> /var/log/sailing.err |
|
| 100 | - su - sailing -c "cd $SERVERS_DIR/$conf && ./stop" |
|
| 101 | - RETVAL=$? |
|
| 102 | - [ $RETVAL -eq 0 ] && success || failure |
|
| 103 | - sync_logs |
|
| 104 | - done |
|
| 105 | -} |
|
| 106 | - |
|
| 107 | -sync_logs() { |
|
| 108 | - echo "Executing logrotate followed by a sync to ensure that logs are synchronized" >> /var/log/sailing.err |
|
| 109 | - logrotate -f /etc/logrotate.conf |
|
| 110 | - sync |
|
| 111 | -} |
|
| 112 | - |
|
| 113 | -# See how we were called. |
|
| 114 | -case "$1" in |
|
| 115 | - start) |
|
| 116 | - start_servers |
|
| 117 | - /usr/sbin/update-motd |
|
| 118 | - touch /var/lock/subsys/sailing |
|
| 119 | - ;; |
|
| 120 | - stop) |
|
| 121 | - stop_servers |
|
| 122 | - rm -f /var/lock/subsys/sailing |
|
| 123 | - ;; |
|
| 124 | - status) |
|
| 125 | - status java |
|
| 126 | - RETVAL=$? |
|
| 127 | - ;; |
|
| 128 | - *) |
|
| 129 | - echo $"Usage: $0 {start|status|stop}" |
|
| 130 | - RETVAL=3 |
|
| 131 | -esac |
|
| 132 | - |
|
| 133 | -exit $RETVAL |
configuration/sailing
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +environments_scripts/sailing_server/files/usr/local/bin/sailing |
|
| ... | ... | \ No newline at end of file |
configuration/sailing.sh
| ... | ... | @@ -1,14 +0,0 @@ |
| 1 | -# Script to be linked from /etc/profile.d |
|
| 2 | -# Appends to PATH, sets DISPLAY for VNC running on :2, exports JAVA_HOME and Amazon EC2 variables |
|
| 3 | - |
|
| 4 | -ulimit -n 100000 |
|
| 5 | -ulimit -u 40000 |
|
| 6 | - |
|
| 7 | -# SAP JVM |
|
| 8 | -export JAVA_HOME=/opt/sapjvm_8 |
|
| 9 | -# JDK 11.0.1: |
|
| 10 | -#export JAVA_HOME=/opt/jdk-11.0.1+13 |
|
| 11 | -#export JAVA_HOME=/opt/jdk1.8.0_45 |
|
| 12 | -export ANDROID_HOME=/opt/android-sdk-linux |
|
| 13 | -export PATH=$PATH:$JAVA_HOME/bin |
|
| 14 | -export DISPLAY=:2.0 |
configuration/sailing.sh
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +environments_scripts/sailing_server/files/etc/profile.d/sailing.sh |
|
| ... | ... | \ No newline at end of file |
configuration/sailing_server_setup/crontab-root
| ... | ... | @@ -1 +1,3 @@ |
| 1 | 1 | * * * * * export PATH=/bin:/usr/bin:/usr/local/bin; sleep $(( $RANDOM * 60 / 32768 )); update_authorized_keys_for_landscape_managers_if_changed $( cat /root/ssh-key-reader.token ) https://security-service.sapsailing.com /root 2>&1 >>/var/log/sailing.err |
| 2 | +# NOTICE: Please try to reference the customised crontabs at $GIT_HOME/configuration/crontabs or use |
|
| 3 | +# the build-crontab script. This file has been maintained for continuity, but is deprecated. |
configuration/sailing_server_setup/sailing.service
| ... | ... | @@ -1,13 +0,0 @@ |
| 1 | -[Unit] |
|
| 2 | -Description=The sailing start-up service reading through EC2 userdata and acting accordingly |
|
| 3 | -Requires=-.mount mongod.service |
|
| 4 | -After=-.mount mongod.service |
|
| 5 | - |
|
| 6 | -[Install] |
|
| 7 | -RequiredBy=multi-user.target |
|
| 8 | - |
|
| 9 | -[Service] |
|
| 10 | -Type=oneshot |
|
| 11 | -RemainAfterExit=true |
|
| 12 | -ExecStart=/etc/init.d/sailing start |
|
| 13 | -ExecStop=/etc/init.d/sailing stop |
configuration/sailing_server_setup/sailing.service
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +../environments_scripts/sailing_server/files/etc/systemd/system/sailing.service |
|
| ... | ... | \ No newline at end of file |
configuration/sailing_server_setup/setup-sailing-server.sh
| ... | ... | @@ -117,7 +117,11 @@ EOF |
| 117 | 117 | sudo chgrp root /var/cache/swapfile |
| 118 | 118 | sudo chmod 600 /var/cache/swapfile |
| 119 | 119 | sudo mkswap /var/cache/swapfile |
| 120 | - sudo su - -c 'echo "/var/cache/swapfile none swap pri=0 0 0" >>/etc/fstab' |
|
| 120 | + # And while adding to /etc/fstab, also add the NFS mount of /home/scores: |
|
| 121 | + sudo mkdir /home/scores |
|
| 122 | + sudo su - -c 'echo "/var/cache/swapfile none swap pri=0 0 0 |
|
| 123 | +logfiles.internal.sapsailing.com:/home/scores /home/scores nfs tcp,intr,timeo=100,retry=0" >>/etc/fstab' |
|
| 124 | + sudo swapon -a |
|
| 121 | 125 | else |
| 122 | 126 | echo "Not running on an AWS instance; refusing to run setup!" >&2 |
| 123 | 127 | echo "To prepare an instance running in AWS, provide its external IP as argument to this script." >&2 |
configuration/switchoverArchive.sh
| ... | ... | @@ -1,114 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | - |
|
| 3 | -# Purpose: Script is used to switch to the failover archive if the primary is unhealthy, by altering the macros |
|
| 4 | -# file and then reloading Httpd. |
|
| 5 | -# Crontab for every minute: * * * * * /path/to/switchoverArchive.sh |
|
| 6 | -help() { |
|
| 7 | - echo "$0 PATH_TO_HTTPD_MACROS_FILE TIMEOUT_FIRST_CURL_SECONDS TIMEOUT_SECOND_CURL_SECONDS" |
|
| 8 | - echo "" |
|
| 9 | - echo "Script used to automatically update the archive location (to the failover) in httpd if the primary is down." |
|
| 10 | - echo "Pass in the path to the macros file containing the archive definitions;" |
|
| 11 | - echo "the timeout of the first curl check in seconds; and the timeout of the second curl check, also in seconds." |
|
| 12 | - echo "Make sure the combined time taken is not longer than the crontab." |
|
| 13 | - exit 2 |
|
| 14 | -} |
|
| 15 | -# $# is the number of arguments |
|
| 16 | -if [ $# -eq 0 ]; then |
|
| 17 | - help |
|
| 18 | -fi |
|
| 19 | -#The names of the variables in the macros file. |
|
| 20 | -ARCHIVE_IP_NAME="ARCHIVE_IP" |
|
| 21 | -ARCHIVE_FAILOVER_IP_NAME="ARCHIVE_FAILOVER_IP" |
|
| 22 | -PRODUCTION_ARCHIVE_NAME="PRODUCTION_ARCHIVE" |
|
| 23 | -ARCHIVE_PORT=8888 |
|
| 24 | -MACROS_PATH=$1 |
|
| 25 | -# The amount of time (in seconds) that must have elapsed, since the last httpd macros email, before notifying operators again. |
|
| 26 | -TIME_CHECK_SECONDS=$((15*60)) |
|
| 27 | -# Connection timeouts for curl requests (the time waited for a connection to be established). The second should be longer |
|
| 28 | -# as we want to be confident the main archive is in fact "down" before switching. |
|
| 29 | -TIMEOUT1_IN_SECONDS=$2 |
|
| 30 | -TIMEOUT2_IN_SECONDS=$3 |
|
| 31 | -CACHE_LOCATION="/var/cache/lastIncorrectMacroUnixTime" |
|
| 32 | -# The following line checks if all the strings in "search" are present at the beginning of their own line. Note: grep uses BRE by default, |
|
| 33 | -# so the plus symbol must be escaped to refer to "one or more" of the previous character. |
|
| 34 | -for i in "^Define ${PRODUCTION_ARCHIVE_NAME}\>" \ |
|
| 35 | - "^Define ${ARCHIVE_IP_NAME} [0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+$" \ |
|
| 36 | - "^Define ${ARCHIVE_FAILOVER_IP_NAME} [0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+$" |
|
| 37 | -do |
|
| 38 | - if ! grep -q "${i}" "${MACROS_PATH}"; then |
|
| 39 | - currentUnixTime=$(date +"%s") |
|
| 40 | - if [[ ! -f ${CACHE_LOCATION} || $((currentUnixTime - $(cat "${CACHE_LOCATION}") )) -gt "$TIME_CHECK_SECONDS" ]]; then |
|
| 41 | - date +"%s" > "${CACHE_LOCATION}" |
|
| 42 | - echo "Macros file does not contain proper definitions for the archive and failover IPs. Expression ${i} not matched." | notify-operators "Incorrect httpd macros" |
|
| 43 | - fi |
|
| 44 | - logger -t archive "Necessary variable assignment pattern ${i} not found in macros" |
|
| 45 | - exit 1 |
|
| 46 | - fi |
|
| 47 | -done |
|
| 48 | -# These next lines get the current ip values for the archive and failover, plus they store the value of production, |
|
| 49 | -# which is a variable pointing to either the primary or failover value. |
|
| 50 | -archiveIp="$(sed -n -e "s/^Define ${ARCHIVE_IP_NAME} \(.*\)/\1/p" ${MACROS_PATH} | tr -d '[:space:]')" |
|
| 51 | -failoverIp="$(sed -n -e "s/^Define ${ARCHIVE_FAILOVER_IP_NAME} \(.*\)/\1/p" ${MACROS_PATH} | tr -d '[:space:]')" |
|
| 52 | -productionIp="$(sed -n -e "s/^Define ${PRODUCTION_ARCHIVE_NAME} \(.*\)/\1/p" ${MACROS_PATH} | tr -d '[:space:]')" |
|
| 53 | -# Checks if the macro.conf is set as healthy or unhealthy currently. |
|
| 54 | -if [[ "${productionIp}" == "\${${ARCHIVE_IP_NAME}}" ]] |
|
| 55 | -then |
|
| 56 | - alreadyHealthy=1 |
|
| 57 | - logger -t archive "currently healthy" |
|
| 58 | -else |
|
| 59 | - alreadyHealthy=0 |
|
| 60 | - logger -t archive "currently unhealthy" |
|
| 61 | -fi |
|
| 62 | - |
|
| 63 | -setProduction() { |
|
| 64 | - # parameter $1: the name of the variable holding the IP of the archive instance to switch to |
|
| 65 | - sed -i -e "s/^Define ${PRODUCTION_ARCHIVE_NAME}\>.*$/Define ${PRODUCTION_ARCHIVE_NAME} \${${1}}/" ${MACROS_PATH} |
|
| 66 | -} |
|
| 67 | - |
|
| 68 | -# Sets the production value to point to the variable defining the main archive IP, provided it isn't already set. |
|
| 69 | -setProductionMainIfNotSet() { |
|
| 70 | - if [[ $alreadyHealthy -eq 0 ]] |
|
| 71 | - then |
|
| 72 | - # currently unhealthy |
|
| 73 | - # set production to archive |
|
| 74 | - logger -t archive "Healthy: setting production to main archive" |
|
| 75 | - setProduction ${ARCHIVE_IP_NAME} |
|
| 76 | - systemctl reload httpd |
|
| 77 | - echo "The main archive server is healthy again. Switching to it." | notify-operators "Healthy: main archive online" |
|
| 78 | - else |
|
| 79 | - # If already healthy then no reload or notification occurs. |
|
| 80 | - logger -t archive "Healthy: already set, no change needed" |
|
| 81 | - fi |
|
| 82 | -} |
|
| 83 | - |
|
| 84 | -setFailoverIfNotSet() { |
|
| 85 | - if [[ $alreadyHealthy -eq 1 ]] |
|
| 86 | - then |
|
| 87 | - # Set production to failover if not already. Separate if statement in case the curl statement |
|
| 88 | - # fails but the production is already set to point to the backup |
|
| 89 | - setProduction ${ARCHIVE_FAILOVER_IP_NAME} |
|
| 90 | - logger -t archive "Unhealthy: second check failed, switching to failover" |
|
| 91 | - systemctl reload httpd |
|
| 92 | - echo "Main archive is unhealthy. Switching to failover. Please urgently take a look at ${archiveIp}." | notify-operators "Unhealthy: main archive offline, failover in place" |
|
| 93 | - else |
|
| 94 | - logger -t archive "Unhealthy: second check still fails, failover already in use" |
|
| 95 | - fi |
|
| 96 | -} |
|
| 97 | - |
|
| 98 | -logger -t archive "begin check" |
|
| 99 | -# --fail option ensures that, if a server error is returned (ie. 5xx/4xx status code), then the status code (stored in $?) will be non zero. |
|
| 100 | -# -L follows redirects |
|
| 101 | -curl -s -L --fail --connect-timeout ${TIMEOUT1_IN_SECONDS} "http://${archiveIp}:${ARCHIVE_PORT}/gwt/status" >> /dev/null |
|
| 102 | -if [[ $? -ne 0 ]] |
|
| 103 | -then |
|
| 104 | - logger -t archive "first check failed" |
|
| 105 | - curl -s -L --fail --connect-timeout ${TIMEOUT2_IN_SECONDS} "http://${archiveIp}:${ARCHIVE_PORT}/gwt/status" >> /dev/null |
|
| 106 | - if [[ $? -ne 0 ]] |
|
| 107 | - then |
|
| 108 | - setFailoverIfNotSet |
|
| 109 | - else |
|
| 110 | - setProductionMainIfNotSet |
|
| 111 | - fi |
|
| 112 | -else |
|
| 113 | - setProductionMainIfNotSet |
|
| 114 | -fi |
configuration/switchoverArchive.sh
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +environments_scripts/repo/usr/local/bin/switchoverArchive.sh |
|
| ... | ... | \ No newline at end of file |
configuration/syncgit
| ... | ... | @@ -1,12 +0,0 @@ |
| 1 | -#!/bin/sh
|
|
| 2 | -ADMIN_EMAIL="axel.uhl@sap.com jan.hamann@sapsailing.com"
|
|
| 3 | -
|
|
| 4 | -cd /home/wiki/gitwiki
|
|
| 5 | -git pull >/tmp/wiki-git.out 2>/tmp/wiki-git.err
|
|
| 6 | -if [ "$?" != "0" ]; then
|
|
| 7 | - cat /tmp/wiki-git.out /tmp/wiki-git.err | mail -s "Wiki git problem" $ADMIN_EMAIL
|
|
| 8 | -fi
|
|
| 9 | -git push >>/tmp/wiki-git.out 2>/tmp/wiki-git.err
|
|
| 10 | -if [ "$?" != "0" ]; then
|
|
| 11 | - cat /tmp/wiki-git.out /tmp/wiki-git.err | mail -s "Wiki git problem" $ADMIN_EMAIL
|
|
| 12 | -fi
|
configuration/syncgit
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +environments_scripts/repo/usr/local/bin/syncgit |
|
| ... | ... | \ No newline at end of file |
configuration/update-tractrac-urls-to-archive.sh
| ... | ... | @@ -1,6 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | -GIT_ROOT=/home/wiki/gitwiki |
|
| 3 | -mongo --quiet "mongodb://dbserver.internal.sapsailing.com:10201/winddb?replicaSet=archive" --eval 'db.TRACTRAC_CONFIGURATIONS.find({}, {TT_CONFIG_JSON_URL : 1}).toArray()' | grep -v ObjectId | jq -r '.[].TT_CONFIG_JSON_URL' | sort -u >"${GIT_ROOT}/configuration/tractrac-json-urls" |
|
| 4 | -cd "${GIT_ROOT}" |
|
| 5 | -git commit -m "Updated tractrac-json-urls" -a |
|
| 6 | -git push |
configuration/update-tractrac-urls-to-archive.sh
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +environments_scripts/central_reverse_proxy/files/usr/local/bin/update-tractrac-urls-to-archive.sh |
|
| ... | ... | \ No newline at end of file |
configuration/update_authorized_keys_for_landscape_managers
| ... | ... | @@ -1,48 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | -BEARER_TOKEN="$1" |
|
| 3 | -BASE_URL="$2" |
|
| 4 | -LOGON_USER_HOME="$3" |
|
| 5 | -SSH_DIR="$3/.ssh" |
|
| 6 | -EXIT_CODE=0 |
|
| 7 | -# |
|
| 8 | -curl_output=$( curl -H 'X-SAPSSE-Forward-Request-To: master' -H 'Authorization: Bearer '${BEARER_TOKEN} "${BASE_URL}/security/api/restsecurity/users_with_permission?permission=LANDSCAPE:MANAGE:AWS" 2>/dev/null ) |
|
| 9 | -curl_exit_code=$? |
|
| 10 | -if [ "${curl_exit_code}" = "0" ]; then |
|
| 11 | - users=$( echo "${curl_output}" | jq -r '.[]' ) |
|
| 12 | - jq_exit_code=$? |
|
| 13 | - if [ "${jq_exit_code}" = "0" ]; then |
|
| 14 | - logger -t sailing "Users with LANDSCAPE:MANAGE:AWS permission: ${users}" |
|
| 15 | - public_keys=$( for user in ${users}; do |
|
| 16 | - ssh_key_curl_output=$(curl -H 'X-SAPSSE-Forward-Request-To: master' -H 'Authorization: Bearer '${BEARER_TOKEN} "${BASE_URL}/landscape/api/landscape/get_ssh_keys_owned_by_user?username[]=${user}" 2>/dev/null ) |
|
| 17 | - ssh_key_curl_exit_code=$? |
|
| 18 | - if [ "${ssh_key_curl_exit_code}" = "0" ]; then |
|
| 19 | - echo "${ssh_key_curl_output}" | jq -r '.[].publicKey' |
|
| 20 | - ssh_key_jq_exit_code=$? |
|
| 21 | - if [ "${ssh_key_jq_exit_code}" != "0" ]; then |
|
| 22 | - EXIT_CODE=${ssh_key_jq_exit_code} |
|
| 23 | - logger -t sailing "Couldn't parse response of get_ssh_keys_owned_by_user; jq exit code ${ssh_key_jq_exit_code}" |
|
| 24 | - fi |
|
| 25 | - else |
|
| 26 | - EXIT_CODE=${ssh_key_curl_exit_code} |
|
| 27 | - logger -t sailing "Couldn't get response of get_ssh_keys_owned_by_user; curl exit code ${ssh_key_corl_exit_code}" |
|
| 28 | - fi |
|
| 29 | - done | sort -u ) |
|
| 30 | - logger -t sailing "Obtained public keys: ${public_keys}" |
|
| 31 | - if [ ! -f ${SSH_DIR}/authorized_keys.org ]; then |
|
| 32 | - # Create a copy of the original authorized_keys file as generated by AWS from the start-up key: |
|
| 33 | - logger -t sailing "Saving original authorized_keys file from ${SSH_DIR}" |
|
| 34 | - cp ${SSH_DIR}/authorized_keys ${SSH_DIR}/authorized_keys.org |
|
| 35 | - fi |
|
| 36 | - # Start out with the original AWS-generated authorized_keys file |
|
| 37 | - # and append the public SSH keys of all users having LANDSCAPE:MANAGE:AWS permission: |
|
| 38 | - echo "$( cat ${SSH_DIR}/authorized_keys.org ) |
|
| 39 | - ${public_keys}" | sort -u >${SSH_DIR}/authorized_keys |
|
| 40 | - else |
|
| 41 | - EXIT_CODE=${jq_exit_code} |
|
| 42 | - logger -t sailing "Couldn't parse response of users_with_permission; jq exit code ${jq_exit_code}" |
|
| 43 | - fi |
|
| 44 | -else |
|
| 45 | - EXIT_CODE=${curl_exit_code} |
|
| 46 | - logger -t sailing "Couldn't get response of users_with_permission; curl exit code ${curl_exit_code}" |
|
| 47 | -fi |
|
| 48 | -exit ${EXIT_CODE} |
configuration/update_authorized_keys_for_landscape_managers
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +environments_scripts/repo/usr/local/bin/update_authorized_keys_for_landscape_managers |
|
| ... | ... | \ No newline at end of file |
configuration/update_authorized_keys_for_landscape_managers_if_changed
| ... | ... | @@ -1,42 +0,0 @@ |
| 1 | -#!/bin/bash |
|
| 2 | -BEARER_TOKEN="$1" |
|
| 3 | -BASE_URL="$2" |
|
| 4 | -LOGON_USER_HOME="$3" |
|
| 5 | -LAST_CHANGE_FILE=/var/run/last_change_aws_landscape_managers_ssh_keys |
|
| 6 | -# Uncomment the following for production use, with no error output |
|
| 7 | -curl_output=$( curl -H 'X-SAPSSE-Forward-Request-To: master' -H 'Authorization: Bearer '${BEARER_TOKEN} "${BASE_URL}/landscape/api/landscape/get_time_point_of_last_change_in_ssh_keys_of_aws_landscape_managers" 2>/dev/null ) |
|
| 8 | -curl_exit_code=$? |
|
| 9 | -if [ "${curl_exit_code}" = "0" ]; then |
|
| 10 | - last_change_millis=$( echo "${curl_output}" | jq -r '."timePointOfLastChangeOfSetOfLandscapeManagers-millis"' ) |
|
| 11 | - jq_exit_code=$? |
|
| 12 | - if [ "${jq_exit_code}" = "0" ]; then |
|
| 13 | - if [ -f "${LAST_CHANGE_FILE}" ]; then |
|
| 14 | - PREVIOUS_CHANGE=$(cat "${LAST_CHANGE_FILE}") |
|
| 15 | - if [ -z ${PREVIOUS_CHANGE} ]; then |
|
| 16 | - PREVIOUS_CHANGE=0 |
|
| 17 | - fi |
|
| 18 | - else |
|
| 19 | - PREVIOUS_CHANGE=0 |
|
| 20 | - fi |
|
| 21 | - if [ -z ${last_change_millis} ]; then |
|
| 22 | - logger -t sailing "Empty response from get_time_point_of_last_change_in_ssh_keys_of_aws_landscape_managers; exiting" |
|
| 23 | - exit 1 |
|
| 24 | - else |
|
| 25 | - if [ ${PREVIOUS_CHANGE} -lt ${last_change_millis} ]; then |
|
| 26 | - logger -t sailing "New SSH key changes for landscape managers (${last_change_millis} newer than ${PREVIOUS_CHANGE})" |
|
| 27 | - if update_authorized_keys_for_landscape_managers "${BEARER_TOKEN}" "${BASE_URL}" "${LOGON_USER_HOME}" ; then |
|
| 28 | - logger -t sailing "Updating SSH keys for landscape managers successful; updating ${LAST_CHANGE_FILE}" |
|
| 29 | - echo ${last_change_millis} >${LAST_CHANGE_FILE} |
|
| 30 | - else |
|
| 31 | - logger -t sailing "Updating SSH keys for landscape managers failed with exit code $?; not updating ${LAST_CHANGE_FILE}" |
|
| 32 | - fi |
|
| 33 | - fi |
|
| 34 | - fi |
|
| 35 | - else |
|
| 36 | - logger -t sailing "Parsing response of get_time_point_of_last_change_in_ssh_keys_of_aws_landscape_managers failed with exit code ${jq_exit_code}" |
|
| 37 | - exit ${jq_exit_code} |
|
| 38 | - fi |
|
| 39 | -else |
|
| 40 | - logger -t sailing "Getting response of get_time_point_of_last_change_in_ssh_keys_of_aws_landscape_managers failed with exit code ${curl_exit_code}" |
|
| 41 | - exit ${curl_exit_code} |
|
| 42 | -fi |
configuration/update_authorized_keys_for_landscape_managers_if_changed
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +environments_scripts/repo/usr/local/bin/update_authorized_keys_for_landscape_managers_if_changed |
|
| ... | ... | \ No newline at end of file |
configuration/update_landscape_managers_mailing_list.sh
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +environments_scripts/repo/usr/local/bin/update_landscape_managers_mailing_list.sh |
|
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/RibDashboard.gwt.xml
| ... | ... | @@ -14,6 +14,7 @@ |
| 14 | 14 | <inherits name="com.sap.sse.Security" /> |
| 15 | 15 | <inherits name="com.sap.sse.security.ui.SecurityCommon" /> |
| 16 | 16 | <inherits name="com.sap.sse.security.ui.LoginPanel" /> |
| 17 | + <inherits name='com.sap.sailing.gwt.ui.SourceRaceboard' /> |
|
| 17 | 18 | |
| 18 | 19 | <inherits name="com.googlecode.mgwt.MGWT" /> |
| 19 | 20 | <inherits name="com.googlecode.mgwt.linker.Linker" /> |
java/com.sap.sailing.gwt.ui/EmbeddedMapAndWindChart.css
| ... | ... | @@ -597,7 +597,7 @@ img.openColumn { |
| 597 | 597 | |
| 598 | 598 | .raceBoard-Logo, .RegattaRaceInformation-Header { |
| 599 | 599 | display: inline-block; |
| 600 | - vertical-align: text-bottom; |
|
| 600 | + vertical-align: text-top; |
|
| 601 | 601 | margin-left: 10px; |
| 602 | 602 | margin-top: 5px; |
| 603 | 603 | } |
java/com.sap.sailing.gwt.ui/RaceBoard.css
| ... | ... | @@ -126,13 +126,6 @@ table { |
| 126 | 126 | margin-left: 1.2em; |
| 127 | 127 | } |
| 128 | 128 | |
| 129 | -.RaceMap-HeaderPanel { |
|
| 130 | - height: 60px; |
|
| 131 | - width: 100%; |
|
| 132 | - background-color: #333; |
|
| 133 | - opacity: .5; |
|
| 134 | -} |
|
| 135 | - |
|
| 136 | 129 | .leaderboardLabel { |
| 137 | 130 | border-bottom: 2px solid #007dc0; |
| 138 | 131 | padding: 0 0 6px 0; |
| ... | ... | @@ -472,10 +465,6 @@ img.openColumn { |
| 472 | 465 | padding: 7px; |
| 473 | 466 | } |
| 474 | 467 | |
| 475 | -.raceBoard-Logo { |
|
| 476 | - cursor: pointer; |
|
| 477 | -} |
|
| 478 | - |
|
| 479 | 468 | .SideBySideComponentViewer-MainPanel { |
| 480 | 469 | top: 12px; |
| 481 | 470 | } |
| ... | ... | @@ -665,37 +654,67 @@ img.openColumn { |
| 665 | 654 | margin-left: 5px; |
| 666 | 655 | } |
| 667 | 656 | |
| 668 | -.RegattaRaceInformation-Header { |
|
| 669 | - font-family: 'Open Sans', Arial, Verdana, sans-serif; |
|
| 670 | - position: relative; |
|
| 657 | +.RaceMap-HeaderPanel { |
|
| 658 | + display: flex; |
|
| 659 | + box-sizing: border-box; |
|
| 660 | + justify-content: space-between; |
|
| 661 | + align-items: center; |
|
| 662 | + overflow: visible; |
|
| 663 | + color: white; |
|
| 664 | + background: rgba(0, 3, 51, 0.5); |
|
| 665 | + padding: 0; |
|
| 666 | + position: absolute; |
|
| 667 | + top: 0; |
|
| 668 | +} |
|
| 669 | + |
|
| 670 | +.raceBoard-Logo { |
|
| 671 | + cursor: pointer; |
|
| 671 | 672 | } |
| 672 | 673 | |
| 673 | 674 | .raceBoard-Logo, .RegattaRaceInformation-Header { |
| 674 | 675 | display: inline-block; |
| 675 | - vertical-align: text-bottom; |
|
| 676 | + vertical-align: middle; |
|
| 676 | 677 | margin-left: 10px; |
| 677 | - margin-top: 5px; |
|
| 678 | -} |
|
| 679 | - |
|
| 680 | -.RegattaRaceInformation-Header .RaceName-Label { |
|
| 681 | - display: inline; |
|
| 682 | - color: white; |
|
| 683 | - margin-right: 10px; |
|
| 684 | - font-size: 36px; |
|
| 685 | 678 | } |
| 686 | 679 | |
| 687 | -.RegattaRaceInformation-Header .RaceSeriesAndFleet-Label { |
|
| 688 | - display: inline; |
|
| 689 | - color: white; |
|
| 690 | - font-size: 15px; |
|
| 680 | +.RegattaRaceInformation-Header { |
|
| 681 | + display: flex; |
|
| 682 | + flex-shrink: 2; |
|
| 683 | + flex-grow: 2; |
|
| 684 | + align-items: center; |
|
| 685 | + max-height: 60px; |
|
| 686 | + margin: 5px; |
|
| 687 | + cursor: pointer; |
|
| 688 | + font-family: 'Open Sans', Arial, Verdana, sans-serif; |
|
| 689 | + font-size: large; |
|
| 690 | + line-height: normal; |
|
| 691 | +} |
|
| 692 | + |
|
| 693 | +@media (max-width: 500px) { |
|
| 694 | + .RegattaRaceInformation-Header { |
|
| 695 | + display: flex; |
|
| 696 | + flex-shrink: 2; |
|
| 697 | + align-items: center; |
|
| 698 | + max-height: 60px; |
|
| 699 | + margin: 5px; |
|
| 700 | + cursor: pointer; |
|
| 701 | + font-family: 'Open Sans', Arial, Verdana, sans-serif; |
|
| 702 | + font-size: small; |
|
| 703 | + } |
|
| 704 | +} |
|
| 705 | + |
|
| 706 | +.RaceMap-Header-Filler { |
|
| 707 | + flex-grow: 1; |
|
| 691 | 708 | } |
| 692 | 709 | |
| 693 | 710 | .RegattaAndRaceTime-Header { |
| 694 | - position: absolute; |
|
| 695 | - right: 65px; |
|
| 696 | - top: 13px; |
|
| 697 | - text-align: right; |
|
| 698 | - color: white; |
|
| 711 | + display: flex; |
|
| 712 | + flex-grow: 2; |
|
| 713 | + flex-shrink: 3; |
|
| 714 | + flex-direction: column; |
|
| 715 | + align-items: flex-end; |
|
| 716 | + padding: 5px; |
|
| 717 | + min-width: 0; |
|
| 699 | 718 | } |
| 700 | 719 | |
| 701 | 720 | .RegattaAndRaceTime-Header_with_databy { |
| ... | ... | @@ -708,58 +727,54 @@ img.openColumn { |
| 708 | 727 | } |
| 709 | 728 | |
| 710 | 729 | .RegattaAndRaceTime-Header .RegattaName-Anchor { |
| 711 | - font-size: 15px; |
|
| 730 | + font-size: 15px; |
|
| 712 | 731 | color: white; |
| 732 | + min-width: 0; |
|
| 733 | + width: 100%; |
|
| 734 | + white-space: nowrap; |
|
| 735 | + overflow: hidden; |
|
| 736 | + text-overflow: ellipsis; |
|
| 737 | + text-align: right; |
|
| 713 | 738 | text-decoration: none; |
| 714 | - padding-left: 25px; |
|
| 739 | + padding-right: 25px; |
|
| 715 | 740 | background-image: url(images/home.png); |
| 716 | 741 | background-repeat: no-repeat; |
| 717 | 742 | background-size: 17px; |
| 718 | - background-position: 4px 1px; |
|
| 719 | -} |
|
| 720 | - |
|
| 721 | -.RegattaAndRaceTime-Header .RegattaName-Anchor:hover { |
|
| 722 | - text-decoration: underline; |
|
| 743 | + background-position: right; |
|
| 723 | 744 | } |
| 724 | 745 | |
| 725 | -.RegattaAndRaceTime-Header .RaceTime-Label { |
|
| 726 | - font-size: 15px; |
|
| 727 | - margin-top: 5px; |
|
| 728 | -} |
|
| 729 | - |
|
| 730 | -.RegattaAndRaceTime-Header .HelpButton { |
|
| 731 | - position: absolute; |
|
| 732 | - left: -25px; |
|
| 746 | +.compactHeader .RegattaAndRaceTime-Header .RegattaName-Anchor { |
|
| 747 | + padding-right: 20px; |
|
| 733 | 748 | } |
| 734 | 749 | |
| 735 | -.compactHeader .RegattaAndRaceTime-Header>* { |
|
| 736 | - margin-top: 3px; |
|
| 750 | +.Help-And-RaceTime { |
|
| 751 | + display: flex; |
|
| 752 | + justify-content: flex-end; |
|
| 753 | + align-items: center; |
|
| 754 | + min-width: 0; |
|
| 755 | + width: 100%; |
|
| 737 | 756 | } |
| 738 | 757 | |
| 739 | -.RegattaAndRaceTime-Header_with_databy .RegattaName-Anchor, .RegattaAndRaceTime-Header_with_databy .RaceTime-Label { |
|
| 740 | - font-size: 13px; |
|
| 758 | +.RegattaAndRaceTime-Header .RegattaName-Anchor:hover { |
|
| 759 | + text-decoration: underline; |
|
| 741 | 760 | } |
| 742 | 761 | |
| 743 | -.compactHeader .RegattaRaceInformation-Header .RaceName-Label { |
|
| 744 | - display: block; |
|
| 745 | - font-size: 16px; |
|
| 746 | - padding: 2px 0; |
|
| 747 | -} |
|
| 748 | -.compactHeader .RegattaRaceInformation-Header .RaceSeriesAndFleet-Label { |
|
| 749 | - display: block; |
|
| 762 | +.RaceTime-Label { |
|
| 750 | 763 | white-space: nowrap; |
| 764 | + overflow: hidden; |
|
| 765 | + text-overflow: ellipsis; |
|
| 766 | + font-size: smaller; |
|
| 751 | 767 | } |
| 768 | + |
|
| 752 | 769 | .compactHeader .RegattaAndRaceTime-Header { |
| 753 | - top: 11px; |
|
| 770 | + top: 0; |
|
| 754 | 771 | } |
| 755 | -.compactHeader .RegattaAndRaceTime-Header .RegattaName-Anchor { |
|
| 756 | - width: 115px; |
|
| 757 | - overflow: hidden; |
|
| 758 | - text-overflow: ellipsis; |
|
| 759 | - display: inline-block; |
|
| 760 | - white-space: nowrap; |
|
| 761 | - padding: 2px 0 2px 20px; |
|
| 762 | - background-position: 4px -1px; |
|
| 772 | + |
|
| 773 | +.AuthenticationButton { |
|
| 774 | + flex-grow: 0; |
|
| 775 | + flex-shrink: 100; |
|
| 776 | + position: relative; |
|
| 777 | + padding-right: 5px; |
|
| 763 | 778 | } |
| 764 | 779 | |
| 765 | 780 | .VideoPopup-Close-Button { |
java/com.sap.sailing.gwt.ui/Simulator.css
| ... | ... | @@ -978,7 +978,7 @@ img.openColumn { |
| 978 | 978 | |
| 979 | 979 | .raceBoard-Logo, .RegattaRaceInformation-Header { |
| 980 | 980 | display: inline-block; |
| 981 | - vertical-align: text-bottom; |
|
| 981 | + vertical-align: text-top; |
|
| 982 | 982 | margin-left: 10px; |
| 983 | 983 | margin-top: 5px; |
| 984 | 984 | } |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventheader/DropdownItem.java
| ... | ... | @@ -1,38 +0,0 @@ |
| 1 | -package com.sap.sailing.gwt.home.desktop.partials.eventheader; |
|
| 2 | - |
|
| 3 | -import com.google.gwt.core.client.GWT; |
|
| 4 | -import com.google.gwt.dom.client.AnchorElement; |
|
| 5 | -import com.google.gwt.dom.client.DivElement; |
|
| 6 | -import com.google.gwt.dom.client.Element; |
|
| 7 | -import com.google.gwt.safehtml.shared.SafeUri; |
|
| 8 | -import com.google.gwt.uibinder.client.UiBinder; |
|
| 9 | -import com.google.gwt.uibinder.client.UiField; |
|
| 10 | -import com.google.gwt.user.client.ui.Widget; |
|
| 11 | - |
|
| 12 | -public class DropdownItem extends Widget { |
|
| 13 | - |
|
| 14 | - private static DropdownItemUiBinder uiBinder = GWT.create(DropdownItemUiBinder.class); |
|
| 15 | - |
|
| 16 | - interface DropdownItemUiBinder extends UiBinder<Element, DropdownItem> { |
|
| 17 | - } |
|
| 18 | - |
|
| 19 | - @UiField EventHeaderResources local_res; |
|
| 20 | - |
|
| 21 | - @UiField |
|
| 22 | - AnchorElement link; |
|
| 23 | - |
|
| 24 | - @UiField |
|
| 25 | - DivElement title; |
|
| 26 | - |
|
| 27 | - public DropdownItem(String text, SafeUri link, boolean active) { |
|
| 28 | - setElement(uiBinder.createAndBindUi(this)); |
|
| 29 | - |
|
| 30 | - if(active) { |
|
| 31 | - getElement().addClassName(local_res.css().dropdown_content_linkactive()); |
|
| 32 | - } |
|
| 33 | - |
|
| 34 | - this.link.setHref(link); |
|
| 35 | - this.title.setInnerText(text); |
|
| 36 | - } |
|
| 37 | - |
|
| 38 | -} |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventheader/DropdownItem.ui.xml
| ... | ... | @@ -1,16 +0,0 @@ |
| 1 | -<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"> |
|
| 2 | -<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"> |
|
| 3 | - <ui:with field="i18n" type="com.sap.sailing.gwt.ui.client.StringMessages" /> |
|
| 4 | - <ui:with field="res" type="com.sap.sailing.gwt.common.client.SharedResources" /> |
|
| 5 | - <ui:with field="local_res" type="com.sap.sailing.gwt.home.desktop.partials.eventheader.EventHeaderResources" /> |
|
| 6 | - <ui:style> |
|
| 7 | - .important { |
|
| 8 | - font-weight: bold; |
|
| 9 | - } |
|
| 10 | - </ui:style> |
|
| 11 | - <a ui:field="link" class="{local_res.css.dropdown_content_link}"> |
|
| 12 | - <div ui:field="title" class="{local_res.css.dropdown_content_link_title}"> |
|
| 13 | - <!-- regatta.name --> |
|
| 14 | - </div> |
|
| 15 | - </a> |
|
| 16 | -</ui:UiBinder> |
|
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventheader/EventHeader.gss
| ... | ... | @@ -1,121 +1,6 @@ |
| 1 | 1 | /** |
| 2 | 2 | * The header of the pages to an event |
| 3 | 3 | */ |
| 4 | -.jsdropdown { |
|
| 5 | - position: relative; |
|
| 6 | -} |
|
| 7 | -.jsdropdown_head, |
|
| 8 | -.jsdropdown_content { |
|
| 9 | - text-indent: 0; |
|
| 10 | -} |
|
| 11 | -.dropdown { |
|
| 12 | - font-size: 1rem; |
|
| 13 | - width: 100%; |
|
| 14 | - max-width: 100%; |
|
| 15 | - display: inline-block; |
|
| 16 | - text-align: left; |
|
| 17 | - min-width: 21.333333333333332em; |
|
| 18 | - margin-left: -0.666666666666667em; |
|
| 19 | -} |
|
| 20 | -.dropdown_head { |
|
| 21 | - display: inline-block; |
|
| 22 | - position: relative; |
|
| 23 | - padding-left: 0.75em; |
|
| 24 | - padding-right: 1.75em; |
|
| 25 | - border-radius: 0.107142857142857em; |
|
| 26 | - border: 1px solid transparent; |
|
| 27 | - cursor: pointer; |
|
| 28 | -} |
|
| 29 | -.dropdown_head:hover { |
|
| 30 | - border: 1px solid #cfcfcf; |
|
| 31 | - background-color: #eee; |
|
| 32 | - text-decoration: none; |
|
| 33 | -} |
|
| 34 | -.dropdown_head_title { |
|
| 35 | - font-size: 1.866666666666667em; |
|
| 36 | - color: #333; |
|
| 37 | - height: 1.464285714285714em; |
|
| 38 | - line-height: 1.428571428571429em; |
|
| 39 | - font-weight: 700; |
|
| 40 | -} |
|
| 41 | -.dropdown_head_title_button { |
|
| 42 | - position: absolute; |
|
| 43 | - top: 0.75em; |
|
| 44 | - right: 0; |
|
| 45 | - display: inline-block; |
|
| 46 | - margin-left: 0.714285714285714em; |
|
| 47 | - height: 0.285714285714286em; |
|
| 48 | - width: 0.428571428571429em; |
|
| 49 | - background-image: resourceUrl("arrowDownFilledBlack"); |
|
| 50 | - background-position: center bottom; |
|
| 51 | - background-repeat: no-repeat; |
|
| 52 | - background-size: 100%; |
|
| 53 | - -webkit-transform: translate(-50%, -50%); |
|
| 54 | - -moz-transform: translate(-50%, -50%); |
|
| 55 | - -ms-transform: translate(-50%, -50%); |
|
| 56 | - transform: translate(-50%, -50%); |
|
| 57 | - float: none; |
|
| 58 | -} |
|
| 59 | -.dropdown_content { |
|
| 60 | - position: absolute; |
|
| 61 | - z-index: 1; |
|
| 62 | - display: none; |
|
| 63 | - height: auto; |
|
| 64 | - max-height: 23.333333333333332em; |
|
| 65 | - overflow: auto; |
|
| 66 | - width: auto; |
|
| 67 | - background-color: #fff; |
|
| 68 | - border: 1px solid #ccc; |
|
| 69 | - box-shadow: 0.133333333333333em 0.133333333333333em 0.333333333333333em #ccc; |
|
| 70 | -} |
|
| 71 | -.dropdownregattas_content { |
|
| 72 | -} |
|
| 73 | -.jsdropdownactive .dropdown_content { |
|
| 74 | - display: block; |
|
| 75 | -} |
|
| 76 | -.dropdown_content_link { |
|
| 77 | - display: block; |
|
| 78 | - cursor: pointer; |
|
| 79 | - padding: 1.333333333333333em 3.333333333333333em 1em 1em; |
|
| 80 | - border-bottom: 1px solid #f4f4f4; |
|
| 81 | - position: relative; |
|
| 82 | - border: none; |
|
| 83 | -} |
|
| 84 | -.dropdown_content_link:hover, |
|
| 85 | -.dropdown_content_link:focus { |
|
| 86 | - background: #f8f8f8; |
|
| 87 | - text-decoration: none; |
|
| 88 | -} |
|
| 89 | -.dropdown_content_link:hover .dropdown_content_link_title, |
|
| 90 | -.dropdown_content_link:focus .dropdown_content_link_title { |
|
| 91 | - text-decoration: underline; |
|
| 92 | -} |
|
| 93 | -.dropdown_content_link_title { |
|
| 94 | - display: block; |
|
| 95 | - font-weight: 600; |
|
| 96 | - font-size: 1.2em; |
|
| 97 | - color: #666; |
|
| 98 | -} |
|
| 99 | -.dropdown_content_link_subtitle { |
|
| 100 | - color: #cfcfcf; |
|
| 101 | - font-size: 1.066666666666667em; |
|
| 102 | -} |
|
| 103 | -.dropdown_content_linkactive:after { |
|
| 104 | - content: ''; |
|
| 105 | - position: absolute; |
|
| 106 | - right: 1.333333333333333em; |
|
| 107 | - top: 50%; |
|
| 108 | - height: 0.933333333333333em; |
|
| 109 | - width: 0.933333333333333em; |
|
| 110 | - background-image: resourceUrl("dropdownCheck"); |
|
| 111 | - background-repeat: no-repeat; |
|
| 112 | - background-position: right center; |
|
| 113 | - background-size: contain; |
|
| 114 | - -webkit-transform: translate(0, -50%); |
|
| 115 | - -moz-transform: translate(0, -50%); |
|
| 116 | - -ms-transform: translate(0, -50%); |
|
| 117 | - transform: translate(0, -50%); |
|
| 118 | -} |
|
| 119 | 4 | .leaderboardquickaccess { |
| 120 | 5 | font-size: 1rem; |
| 121 | 6 | text-align: right; |
| ... | ... | @@ -203,66 +88,6 @@ |
| 203 | 88 | .eventnavigationnormal { |
| 204 | 89 | position: relative; |
| 205 | 90 | } |
| 206 | -.eventnavigation_dropdown { |
|
| 207 | - display: inline-block; |
|
| 208 | - position: relative; |
|
| 209 | -} |
|
| 210 | -.eventnavigation_dropdown_toggle { |
|
| 211 | - cursor: pointer; |
|
| 212 | - display: inline-block; |
|
| 213 | -} |
|
| 214 | -@media (min-width: 61em) { |
|
| 215 | - .eventnavigation_dropdown_toggle { |
|
| 216 | - display: none; |
|
| 217 | - } |
|
| 218 | -} |
|
| 219 | -.eventnavigation_dropdown_toggle_icon { |
|
| 220 | - display: inline-block; |
|
| 221 | - position: relative; |
|
| 222 | - top: 50%; |
|
| 223 | - width: 0.8em; |
|
| 224 | - height: 0.533333333333333em; |
|
| 225 | - vertical-align: middle; |
|
| 226 | - background-image: resourceUrl("arrowDownFilledBlack"); |
|
| 227 | - background-size: 100%; |
|
| 228 | - background-repeat: no-repeat; |
|
| 229 | - background-position: center bottom; |
|
| 230 | -} |
|
| 231 | -.eventnavigation_dropdown_container { |
|
| 232 | - position: absolute; |
|
| 233 | - left: 0; |
|
| 234 | - z-index: 99; |
|
| 235 | -} |
|
| 236 | -@media (min-width: 61em) { |
|
| 237 | - .eventnavigation_dropdown_container { |
|
| 238 | - display: inline-block; |
|
| 239 | - position: relative; |
|
| 240 | - left: auto; |
|
| 241 | - } |
|
| 242 | -} |
|
| 243 | -.eventnavigation_dropdown_containerhidden { |
|
| 244 | - display: none; |
|
| 245 | -} |
|
| 246 | -@media (min-width: 61em) { |
|
| 247 | - .eventnavigation_dropdown_containerhidden { |
|
| 248 | - display: inline-block; |
|
| 249 | - } |
|
| 250 | -} |
|
| 251 | -.eventnavigation_dropdown_container .navbar_button { |
|
| 252 | - border-top-width: 0; |
|
| 253 | - width: 16.666666666666668em; |
|
| 254 | -} |
|
| 255 | -@media (min-width: 61em) { |
|
| 256 | - .eventnavigation_dropdown_container .navbar_button { |
|
| 257 | - border-top-width: 1px; |
|
| 258 | - width: auto; |
|
| 259 | - } |
|
| 260 | -} |
|
| 261 | -@media (min-width: 61em) { |
|
| 262 | - .eventnavigation_dropdown_container .navbar_buttonhidden { |
|
| 263 | - display: inline-block; |
|
| 264 | - } |
|
| 265 | -} |
|
| 266 | 91 | .eventnavigation .navbar_button { |
| 267 | 92 | font-size: 1.2rem; |
| 268 | 93 | } |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventheader/EventHeader.java
| ... | ... | @@ -26,7 +26,6 @@ import com.sap.sailing.gwt.home.desktop.places.event.EventView.Presenter; |
| 26 | 26 | import com.sap.sailing.gwt.home.shared.partials.shared.SharingMetadataProvider; |
| 27 | 27 | import com.sap.sailing.gwt.home.shared.places.ShareablePlaceContext; |
| 28 | 28 | import com.sap.sailing.gwt.home.shared.places.event.AbstractEventPlace; |
| 29 | -import com.sap.sailing.gwt.home.shared.utils.DropdownHandler; |
|
| 30 | 29 | import com.sap.sailing.gwt.home.shared.utils.EventDatesFormatterUtil; |
| 31 | 30 | import com.sap.sailing.gwt.home.shared.utils.LabelTypeUtil; |
| 32 | 31 | import com.sap.sailing.gwt.home.shared.utils.LogoUtil; |
| ... | ... | @@ -34,6 +33,9 @@ import com.sap.sailing.gwt.ui.client.StringMessages; |
| 34 | 33 | import com.sap.sailing.gwt.ui.shared.databylogo.DataByLogo; |
| 35 | 34 | import com.sap.sse.common.Util; |
| 36 | 35 | import com.sap.sse.gwt.client.LinkUtil; |
| 36 | +import com.sap.sse.gwt.client.controls.dropdown.DropdownHandler; |
|
| 37 | +import com.sap.sse.gwt.client.controls.dropdown.DropdownItem; |
|
| 38 | +import com.sap.sse.gwt.client.controls.dropdown.DropdownResources; |
|
| 37 | 39 | |
| 38 | 40 | public class EventHeader extends Composite { |
| 39 | 41 | private static EventHeaderUiBinder uiBinder = GWT.create(EventHeaderUiBinder.class); |
| ... | ... | @@ -63,6 +65,8 @@ public class EventHeader extends Composite { |
| 63 | 65 | @UiField FlowPanel dropdownContent; |
| 64 | 66 | @UiField EventHeaderSharingButtons sharing; |
| 65 | 67 | @UiField DataByLogo dataByLogo; |
| 68 | + @UiField EventHeaderResources local_res; |
|
| 69 | + @UiField DropdownResources dropdownitem_res; |
|
| 66 | 70 | |
| 67 | 71 | private EventViewDTO event; |
| 68 | 72 | private Presenter presenter; |
| ... | ... | @@ -70,8 +74,9 @@ public class EventHeader extends Composite { |
| 70 | 74 | public EventHeader(EventView.Presenter presenter) { |
| 71 | 75 | this.event = presenter.getEventDTO(); |
| 72 | 76 | this.presenter = presenter; |
| 73 | - EventHeaderResources.INSTANCE.css().ensureInjected(); |
|
| 74 | 77 | initWidget(uiBinder.createAndBindUi(this)); |
| 78 | + local_res.css().ensureInjected(); |
|
| 79 | + dropdownitem_res.css().ensureInjected(); |
|
| 75 | 80 | initFields(); |
| 76 | 81 | initSharing(); |
| 77 | 82 | } |
| ... | ... | @@ -99,7 +104,7 @@ public class EventHeader extends Composite { |
| 99 | 104 | } |
| 100 | 105 | String eventDisplayName = event.getDisplayName(); |
| 101 | 106 | String nameToShow; |
| 102 | - if(presenter.showRegattaMetadata()) { |
|
| 107 | + if (presenter.showRegattaMetadata()) { |
|
| 103 | 108 | HasRegattaMetadata regattaMetadata = presenter.getRegattaMetadata(); |
| 104 | 109 | String regattaDisplayName = regattaMetadata.getDisplayName(); |
| 105 | 110 | if (regattaDisplayName.toLowerCase().contains(eventDisplayName.toLowerCase())) { |
| ... | ... | @@ -107,7 +112,6 @@ public class EventHeader extends Composite { |
| 107 | 112 | } else { |
| 108 | 113 | nameToShow = eventDisplayName + " - " + regattaDisplayName; |
| 109 | 114 | } |
| 110 | - |
|
| 111 | 115 | if (regattaMetadata.getCompetitorsCount() > 0) { |
| 112 | 116 | competitors.setInnerText((i18n.competitorsCount(regattaMetadata.getCompetitorsCount()))); |
| 113 | 117 | } else { |
| ... | ... | @@ -131,16 +135,14 @@ public class EventHeader extends Composite { |
| 131 | 135 | Date startDate = regattaMetadata.getStartDate() != null ? regattaMetadata.getStartDate() : event.getStartDate(); |
| 132 | 136 | Date endDate = regattaMetadata.getEndDate() != null ? regattaMetadata.getEndDate() : event.getEndDate(); |
| 133 | 137 | eventDate.setInnerHTML(EventDatesFormatterUtil.formatDateRangeWithYear(startDate, endDate)); |
| 134 | - |
|
| 135 | 138 | hide(eventVenueContainer, eventLink); |
| 136 | 139 | } else { |
| 137 | 140 | nameToShow = eventDisplayName; |
| 138 | 141 | eventDate.setInnerHTML(EventDatesFormatterUtil.formatDateRangeWithYear(event.getStartDate(), event.getEndDate())); |
| 139 | 142 | eventVenue.setInnerText(event.getLocationAndVenueAndCountry()); |
| 140 | - |
|
| 141 | - if(event.getOfficialWebsiteURL() != null) { |
|
| 143 | + if (event.getOfficialWebsiteURL() != null) { |
|
| 142 | 144 | String title = withoutPrefix(event.getOfficialWebsiteURL(), "http://", "https://"); |
| 143 | - if(title.length() > 35) { |
|
| 145 | + if (title.length() > 35) { |
|
| 144 | 146 | title = StringMessages.INSTANCE.officalEventWebsite(); |
| 145 | 147 | } |
| 146 | 148 | eventLink.setInnerText(title); |
| ... | ... | @@ -175,14 +177,13 @@ public class EventHeader extends Composite { |
| 175 | 177 | new DropdownHandler(dropdownTrigger, dropdownContent.getElement()) { |
| 176 | 178 | @Override |
| 177 | 179 | protected void dropdownStateChanged(boolean dropdownShown) { |
| 178 | - if(dropdownShown) { |
|
| 179 | - dropdownTitle.addClassName(EventHeaderResources.INSTANCE.css().jsdropdownactive()); |
|
| 180 | + if (dropdownShown) { |
|
| 181 | + dropdownTitle.addClassName(dropdownitem_res.css().jsdropdownactive()); |
|
| 180 | 182 | } else { |
| 181 | - dropdownTitle.removeClassName(EventHeaderResources.INSTANCE.css().jsdropdownactive()); |
|
| 183 | + dropdownTitle.removeClassName(dropdownitem_res.css().jsdropdownactive()); |
|
| 182 | 184 | } |
| 183 | 185 | } |
| 184 | 186 | }; |
| 185 | - |
|
| 186 | 187 | presenter.forPlaceSelection(new PlaceCallback() { |
| 187 | 188 | @Override |
| 188 | 189 | public void forPlace(final AbstractEventPlace place, String title, boolean active) { |
| ... | ... | @@ -190,7 +191,7 @@ public class EventHeader extends Composite { |
| 190 | 191 | dropdownItem.addDomHandler(new ClickHandler() { |
| 191 | 192 | @Override |
| 192 | 193 | public void onClick(ClickEvent event) { |
| 193 | - if(LinkUtil.handleLinkClick((Event) event.getNativeEvent())) { |
|
| 194 | + if (LinkUtil.handleLinkClick((Event) event.getNativeEvent())) { |
|
| 194 | 195 | event.preventDefault(); |
| 195 | 196 | presenter.navigateTo(place); |
| 196 | 197 | } |
| ... | ... | @@ -203,7 +204,7 @@ public class EventHeader extends Composite { |
| 203 | 204 | |
| 204 | 205 | private String withoutPrefix(String title, String... prefixes) { |
| 205 | 206 | for (String prefix : prefixes) { |
| 206 | - if(title.startsWith(prefix)) { |
|
| 207 | + if (title.startsWith(prefix)) { |
|
| 207 | 208 | return title.substring(prefix.length(), title.length()); |
| 208 | 209 | } |
| 209 | 210 | } |
| ... | ... | @@ -212,7 +213,6 @@ public class EventHeader extends Composite { |
| 212 | 213 | |
| 213 | 214 | private void hide(Element... elementsToHide) { |
| 214 | 215 | for (Element element : elementsToHide) { |
| 215 | -// element.getStyle().setDisplay(Display.NONE); |
|
| 216 | 216 | element.removeFromParent(); |
| 217 | 217 | } |
| 218 | 218 | } |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventheader/EventHeader.ui.xml
| ... | ... | @@ -5,6 +5,7 @@ |
| 5 | 5 | <ui:with field="i18n" type="com.sap.sailing.gwt.ui.client.StringMessages" /> |
| 6 | 6 | <ui:with field="res" type="com.sap.sailing.gwt.common.client.SharedResources" /> |
| 7 | 7 | <ui:with field="local_res" type="com.sap.sailing.gwt.home.desktop.partials.eventheader.EventHeaderResources" /> |
| 8 | + <ui:with field="dropdownitem_res" type="com.sap.sse.gwt.client.controls.dropdown.DropdownResources" /> |
|
| 8 | 9 | <g:HTMLPanel addStyleNames="{res.mediaCss.grid}" debugId="EventHeaderPanel"> |
| 9 | 10 | <div class="{res.mediaCss.small12} {res.mediaCss.large9} {res.mediaCss.columns}"> |
| 10 | 11 | <div class="{local_res.css.eventheader_intro}"> |
| ... | ... | @@ -12,19 +13,19 @@ |
| 12 | 13 | <a ui:field="eventLogoAnchorUi"><div ui:field="eventLogo" class="{local_res.css.eventheader_intro_logo_image}" /></a> |
| 13 | 14 | </div> |
| 14 | 15 | <!-- IF header.dropdown === 'regattas' --> |
| 15 | - <div ui:field="dropdownTitle" class="{local_res.css.dropdown} {local_res.css.jsdropdown}"> |
|
| 16 | - <a ui:field="dropdownTrigger" class="{local_res.css.dropdown_head} {local_res.css.jsdropdown_head}"> |
|
| 17 | - <span class="{local_res.css.dropdown_head_title}"> |
|
| 16 | + <div ui:field="dropdownTitle" class="{dropdownitem_res.css.dropdown} {dropdownitem_res.css.jsdropdown}"> |
|
| 17 | + <a ui:field="dropdownTrigger" class="{dropdownitem_res.css.dropdown_head} {dropdownitem_res.css.jsdropdown_head}"> |
|
| 18 | + <span class="{dropdownitem_res.css.dropdown_head_title}"> |
|
| 18 | 19 | <span ui:field="dropdownEventName"></span> |
| 19 | 20 | <!-- IF event.state === 'live' --> |
| 20 | 21 | <div ui:field="dropdownEventState" data-labeltype="live" class="{res.mainCss.label} {res.mainCss.labellarge}"> |
| 21 | 22 | <!-- <ui:text from='{i18n.live}' /> --> |
| 22 | 23 | </div> |
| 23 | 24 | <!-- IFEND event.state === 'live' --> |
| 24 | - <div class="{local_res.css.dropdown_head_title_button}"></div> |
|
| 25 | + <div class="{dropdownitem_res.css.dropdown_head_title_button}"></div> |
|
| 25 | 26 | </span> |
| 26 | 27 | </a> |
| 27 | - <g:FlowPanel ui:field="dropdownContent" addStyleNames="{local_res.css.dropdown_content} {local_res.css.jsdropdown_content}"> |
|
| 28 | + <g:FlowPanel ui:field="dropdownContent" addStyleNames="{dropdownitem_res.css.dropdown_content} {dropdownitem_res.css.jsdropdown_content}"> |
|
| 28 | 29 | <!-- EACH regattaItem in event.regattas --> |
| 29 | 30 | <!-- TODO <s:eventHeader.RegattasDropdownItem ui:field='regattaItem'></s> --> |
| 30 | 31 | <!-- EACHEND --> |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventheader/EventHeaderResources.java
| ... | ... | @@ -15,19 +15,6 @@ public interface EventHeaderResources extends SharedDesktopResources { |
| 15 | 15 | ImageResource location(); |
| 16 | 16 | |
| 17 | 17 | public interface LocalCss extends CssResource { |
| 18 | - String jsdropdown(); |
|
| 19 | - String jsdropdown_head(); |
|
| 20 | - String jsdropdown_content(); |
|
| 21 | - String dropdown(); |
|
| 22 | - String dropdown_head(); |
|
| 23 | - String dropdown_head_title(); |
|
| 24 | - String dropdown_head_title_button(); |
|
| 25 | - String dropdown_content(); |
|
| 26 | - String jsdropdownactive(); |
|
| 27 | - String dropdown_content_link(); |
|
| 28 | - String dropdown_content_link_title(); |
|
| 29 | - String dropdown_content_link_subtitle(); |
|
| 30 | - String dropdown_content_linkactive(); |
|
| 31 | 18 | String leaderboardquickaccess(); |
| 32 | 19 | String eventheader(); |
| 33 | 20 | String eventheader_breadcrumb(); |
| ... | ... | @@ -44,12 +31,6 @@ public interface EventHeaderResources extends SharedDesktopResources { |
| 44 | 31 | String locationicon(); |
| 45 | 32 | String eventnavigation(); |
| 46 | 33 | String eventnavigationnormal(); |
| 47 | - String eventnavigation_dropdown(); |
|
| 48 | - String eventnavigation_dropdown_toggle(); |
|
| 49 | - String eventnavigation_dropdown_toggle_icon(); |
|
| 50 | - String eventnavigation_dropdown_container(); |
|
| 51 | - String eventnavigation_dropdown_containerhidden(); |
|
| 52 | 34 | String navbar_button(); |
| 53 | - String navbar_buttonhidden(); |
|
| 54 | 35 | } |
| 55 | 36 | } |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/header/Header.java
| ... | ... | @@ -54,9 +54,9 @@ import com.sap.sailing.gwt.home.shared.places.solutions.SolutionsPlace; |
| 54 | 54 | import com.sap.sailing.gwt.home.shared.places.solutions.SolutionsPlace.SolutionsNavigationTabs; |
| 55 | 55 | import com.sap.sailing.gwt.home.shared.places.start.StartPlace; |
| 56 | 56 | import com.sap.sailing.gwt.home.shared.places.subscription.SubscriptionPlace; |
| 57 | -import com.sap.sailing.gwt.home.shared.utils.DropdownHandler; |
|
| 58 | 57 | import com.sap.sailing.gwt.ui.client.EntryPointLinkFactory; |
| 59 | 58 | import com.sap.sailing.gwt.ui.client.StringMessages; |
| 59 | +import com.sap.sse.gwt.client.controls.dropdown.DropdownHandler; |
|
| 60 | 60 | import com.sap.sse.gwt.client.mvp.PlaceChangedEvent; |
| 61 | 61 | import com.sap.sse.gwt.shared.ClientConfiguration; |
| 62 | 62 | import com.sap.sse.gwt.shared.DebugConstants; |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/regattanavigation/DropdownFilter.java
| ... | ... | @@ -14,8 +14,8 @@ import com.google.gwt.user.client.ui.Composite; |
| 14 | 14 | import com.google.gwt.user.client.ui.FlowPanel; |
| 15 | 15 | import com.google.gwt.user.client.ui.Widget; |
| 16 | 16 | import com.sap.sailing.gwt.home.desktop.partials.regattanavigation.RegattaNavigationResources.LocalCss; |
| 17 | -import com.sap.sailing.gwt.home.shared.utils.DropdownHandler; |
|
| 18 | 17 | import com.sap.sse.gwt.client.LinkUtil; |
| 18 | +import com.sap.sse.gwt.client.controls.dropdown.DropdownHandler; |
|
| 19 | 19 | |
| 20 | 20 | public class DropdownFilter<T> extends Composite { |
| 21 | 21 |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html
| ... | ... | @@ -9,6 +9,14 @@ |
| 9 | 9 | <ul class="bulletList"> |
| 10 | 10 | <li>Bug fix for colored tails: maximum value for color key was sometimes not |
| 11 | 11 | adjusted properly, leading to a "red shift" on the tails.</li> |
| 12 | + <li>The race name display in the header bar of the race viewer ("RaceBoard") is |
|
| 13 | + now a clickable drop-down menu that allows users to easily navigate to other |
|
| 14 | + races of the same regatta.</li> |
|
| 15 | + <li>The advantage line, if shown, now extends across the entire size of the map, |
|
| 16 | + regardless the zoom level.</li> |
|
| 17 | + <li>Event base URLs with trailing slashes no longer produce incorrect race viewer |
|
| 18 | + URLs in the REST API. Furthermore, race and regatta names will now be properly |
|
| 19 | + URL-encoded when constructing a race viewer URL.</li> |
|
| 12 | 20 | </ul> |
| 13 | 21 | <h5 class="articleSubheadline">December 2023</h5> |
| 14 | 22 | <ul class="bulletList"> |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/resources/SharedDesktopResources.java
| ... | ... | @@ -4,19 +4,12 @@ import com.google.gwt.core.client.GWT; |
| 4 | 4 | import com.google.gwt.resources.client.DataResource; |
| 5 | 5 | import com.google.gwt.resources.client.DataResource.MimeType; |
| 6 | 6 | import com.sap.sailing.gwt.home.shared.SharedHomeResources; |
| 7 | -import com.google.gwt.resources.client.ImageResource; |
|
| 8 | 7 | |
| 9 | 8 | public interface SharedDesktopResources extends SharedHomeResources { |
| 10 | 9 | |
| 11 | 10 | public static final SharedDesktopResources INSTANCE = GWT.create(SharedDesktopResources.class); |
| 12 | 11 | |
| 13 | - @Source("dropdown__check@2x.png") |
|
| 14 | - ImageResource dropdownCheck(); |
|
| 15 | - |
|
| 16 | 12 | @Source("liveraces.svg") |
| 17 | 13 | @MimeType("image/svg+xml") |
| 18 | 14 | DataResource liveraces(); |
| 19 | - |
|
| 20 | - @Source("arrow-down-filled-black.png") |
|
| 21 | - ImageResource arrowDownFilledBlack(); |
|
| 22 | 15 | } |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/resources/arrow-down-filled-black.png
| ... | ... | Binary files a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/resources/arrow-down-filled-black.png and /dev/null differ |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/resources/dropdown__check@2x.png
| ... | ... | Binary files a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/resources/dropdown__check@2x.png and /dev/null differ |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/mobile/partials/header/Header.java
| ... | ... | @@ -24,9 +24,9 @@ import com.sap.sailing.gwt.home.shared.app.PlaceNavigation; |
| 24 | 24 | import com.sap.sailing.gwt.home.shared.app.ResettableNavigationPathDisplay; |
| 25 | 25 | import com.sap.sailing.gwt.home.shared.partials.header.HeaderConstants; |
| 26 | 26 | import com.sap.sailing.gwt.home.shared.places.solutions.SolutionsPlace.SolutionsNavigationTabs; |
| 27 | -import com.sap.sailing.gwt.home.shared.utils.DropdownHandler; |
|
| 28 | 27 | import com.sap.sailing.gwt.ui.client.StringMessages; |
| 29 | 28 | import com.sap.sse.gwt.client.LinkUtil; |
| 29 | +import com.sap.sse.gwt.client.controls.dropdown.DropdownHandler; |
|
| 30 | 30 | import com.sap.sse.gwt.shared.ClientConfiguration; |
| 31 | 31 | import com.sap.sse.security.shared.impl.SecuredSecurityTypes.ServerActions; |
| 32 | 32 | import com.sap.sse.security.ui.authentication.AuthenticationContextEvent; |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/shared/utils/DropdownHandler.java
| ... | ... | @@ -1,99 +0,0 @@ |
| 1 | -package com.sap.sailing.gwt.home.shared.utils; |
|
| 2 | - |
|
| 3 | -import com.google.gwt.core.client.Scheduler; |
|
| 4 | -import com.google.gwt.core.client.Scheduler.ScheduledCommand; |
|
| 5 | -import com.google.gwt.dom.client.Element; |
|
| 6 | -import com.google.gwt.dom.client.EventTarget; |
|
| 7 | -import com.google.gwt.dom.client.Style.Display; |
|
| 8 | -import com.google.gwt.user.client.Event; |
|
| 9 | -import com.google.gwt.user.client.Event.NativePreviewEvent; |
|
| 10 | -import com.google.gwt.user.client.Event.NativePreviewHandler; |
|
| 11 | -import com.google.gwt.user.client.EventListener; |
|
| 12 | -import com.google.gwt.user.client.ui.FocusWidget; |
|
| 13 | -import com.google.web.bindery.event.shared.HandlerRegistration; |
|
| 14 | - |
|
| 15 | -public class DropdownHandler { |
|
| 16 | - |
|
| 17 | - private HandlerRegistration reg; |
|
| 18 | - boolean dropdownShown = false; |
|
| 19 | - private final Element dropdownContainer; |
|
| 20 | - |
|
| 21 | - public DropdownHandler(Element dropdownTrigger, Element dropdownContainer) { |
|
| 22 | - this.dropdownContainer = dropdownContainer; |
|
| 23 | - Event.sinkEvents(dropdownTrigger, Event.ONCLICK); |
|
| 24 | - Event.setEventListener(dropdownTrigger, new EventListener() { |
|
| 25 | - @Override |
|
| 26 | - public void onBrowserEvent(Event event) { |
|
| 27 | - if(dropdownShown) { |
|
| 28 | - return; |
|
| 29 | - } |
|
| 30 | - |
|
| 31 | - show(); |
|
| 32 | - } |
|
| 33 | - |
|
| 34 | - }); |
|
| 35 | - } |
|
| 36 | - |
|
| 37 | - public DropdownHandler(FocusWidget dropdownTrigger, Element dropdownContainer) { |
|
| 38 | - this.dropdownContainer = dropdownContainer; |
|
| 39 | - dropdownTrigger.addClickHandler(event -> { |
|
| 40 | - if (dropdownShown) { |
|
| 41 | - return; |
|
| 42 | - } |
|
| 43 | - show(); |
|
| 44 | - }); |
|
| 45 | - } |
|
| 46 | - |
|
| 47 | - private void show() { |
|
| 48 | - dropdownShown = true; |
|
| 49 | - dropdownStateChanged(true); |
|
| 50 | - reg = Event.addNativePreviewHandler(new NativePreviewHandler() { |
|
| 51 | - @Override |
|
| 52 | - public void onPreviewNativeEvent(NativePreviewEvent event) { |
|
| 53 | - EventTarget eventTarget = event.getNativeEvent().getEventTarget(); |
|
| 54 | - if(!Element.is(eventTarget)) { |
|
| 55 | - return; |
|
| 56 | - } |
|
| 57 | - Element evtElement = Element.as(eventTarget); |
|
| 58 | - if(event.getTypeInt() == Event.ONCLICK && !dropdownContainer.isOrHasChild(evtElement)) { |
|
| 59 | - hide(); |
|
| 60 | - } |
|
| 61 | - } |
|
| 62 | - |
|
| 63 | - }); |
|
| 64 | - } |
|
| 65 | - |
|
| 66 | - private void hide() { |
|
| 67 | - Scheduler.get().scheduleDeferred(new ScheduledCommand() { |
|
| 68 | - @Override |
|
| 69 | - public void execute() { |
|
| 70 | - dropdownShown = false; |
|
| 71 | - dropdownStateChanged(false); |
|
| 72 | - if(reg != null) { |
|
| 73 | - reg.removeHandler(); |
|
| 74 | - reg = null; |
|
| 75 | - } |
|
| 76 | - } |
|
| 77 | - }); |
|
| 78 | - } |
|
| 79 | - |
|
| 80 | - public void setVisible(boolean visible) { |
|
| 81 | - if(visible == dropdownShown) { |
|
| 82 | - return; |
|
| 83 | - } |
|
| 84 | - if(visible) { |
|
| 85 | - show(); |
|
| 86 | - } else { |
|
| 87 | - hide(); |
|
| 88 | - } |
|
| 89 | - } |
|
| 90 | - |
|
| 91 | - protected void dropdownStateChanged(boolean dropdownShown) { |
|
| 92 | - if(dropdownShown) { |
|
| 93 | - dropdownContainer.getStyle().clearDisplay(); |
|
| 94 | - } else { |
|
| 95 | - dropdownContainer.getStyle().setDisplay(Display.NONE); |
|
| 96 | - } |
|
| 97 | - } |
|
| 98 | - |
|
| 99 | -} |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/SourceClient.gwt.xml
| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | <!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.7.0//EN" "http://gwtproject.org/doctype/2.7.0/gwt-module.dtd"> |
| 3 | 3 | <module> |
| 4 | 4 | <inherits name='com.google.gwt.user.User'/> |
| 5 | - <inherits name="com.sap.sailing.ExpeditionConnectorCommon" /> |
|
| 5 | + <inherits name="com.sap.sailing.ExpeditionConnectorCommon" /> |
|
| 6 | 6 | <source path="client" /> |
| 7 | 7 | </module> |
| 8 | 8 | |
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_cs.properties
| ... | ... | @@ -2465,7 +2465,7 @@ helptextLinkingRaces=Chcete-li propojit rozjížďky s trasovanými rozjížďka |
| 2465 | 2465 | scoringSchemeLowPointA82Only=Nízkobodový systém; shoda podle závodních pravidel jachtingu A8.2 (poslední rozjížďka) |
| 2466 | 2466 | scoringSchemeLowPointA82OnlyDescription=Nízkobodový systém; shoda podle závodních pravidel jachtingu A8.2; Pokud shoda přetrvá po kontrole účasti a skóre ve finálové rozjížďce, porovnejte skóre v poslední rozjížďce (včetně vyloučených skóre), potom v předposlední atd., dokud shoda nebude rozhodnuta. |
| 2467 | 2467 | scoringSchemeLowPointSystemFirstThreeWinsA82Only=Nízkobodový systém; první se třemi vítězstvími ve finálovém závodu je vítěz; shoda A8.2 (poslední rozjížďka) |
| 2468 | -scoringSchemeLowPointSystemFirstThreeWinsA82OnlyDescription=Nízkobodový systém. První ve finálovém závodu, který vyhraje tři rozjížďky, vyhrává finálový závod. Sloupec přenosu ve finálovém závodu lze použít k modelování přenášených vítězství. Shoda v úvodních závodech je založena na A8.2 (poslední rozjížďka, pak předposlední atd.). |
|
| 2468 | +scoringSchemeLowPointSystemFirstThreeWinsA82OnlyDescription=Nízkobodový systém. První, kdo ve finálovém závodu vyhraje tři rozjížďky, vyhrává finálový závod. Sloupec přenosu ve finálovém závodu lze použít k modelování přenášených vítězství. Shoda v úvodních závodech je založena na A8.2 (poslední rozjížďka, pak předposlední atd.). |
|
| 2469 | 2469 | errorFetchingUserPreference=Chyba při načítání uživatelských preferencí s klíčem „{0}“: {1} |
| 2470 | 2470 | errorSettingUserPreference=Chyba při načítání uživatelských preferencí s klíčem „{0}“: {1} |
| 2471 | 2471 | scoringSchemeLowPointWithEliminatingMedalSeriesPromotingOneToFinalAndTwoToSemifinal=Nízkobodový systém; finálové rozjížďky jako čtvrtfinále, semifinále a finále |
| ... | ... | @@ -2473,5 +2473,11 @@ scoringSchemeLowPointWithEliminatingMedalSeriesPromotingOneToFinalAndTwoToSemifi |
| 2473 | 2473 | incrementalScoreCorrectionInPoints=Dodatečná oprava skóre (body) |
| 2474 | 2474 | errorObtainingCourseAreasForLeaderboard=Chyba při získávání oblastí dráhy pro výsledkovou tabuli {0}: {1}. |
| 2475 | 2475 | tackType=Dlouhý/krátký obrat |
| 2476 | -tackTypeTooltip=Po větru: Pokud je rozdíl mezi kurzem proti dnu a směrem k dalšímu trasovému bodu menší než rozdíl mezi kurzem proti dnu a směrem větru, jedná se o dlouhý obrat (1,0); pokud je menší, jedná se o krátký obrat (-1.0). Proti větru: Podobné jako po větru, ale místo „směru větru“ se používá opačný směr. Tj. směr vanutí větru. Boční vítr: Podobné jako proti větru, ale místo porovnání „kurzu proti dnu a směru větru“ se používá 10°. |
|
| 2476 | +tackTypeTooltip=Po větru: Pokud je rozdíl mezi kurzem proti dnu a směrem k dalšímu trasovému bodu menší než rozdíl mezi kurzem proti dnu a směrem větru, jedná se o dlouhý obrat (1,0); pokud je menší, jedná se o krátký obrat (-1.0). Proti větru: Podobné jako po větru, ale místo „směru větru“ se používá opačný směr, tj. směr vanutí větru. Boční vítr: Podobné jako proti větru, ale místo porovnání „kurzu proti dnu a směru větru“ se používá 10°. |
|
| 2477 | 2477 | tackTypeUnit=Dlouhý=1.0, Krátký=-1.0, Není známo=0.0 |
| 2478 | +tackTypeSegments=Obratové segmenty |
|
| 2479 | +errorMinimumDurationBetweenAdjacentTackTypeSegmentsMustNotBeNegative=Minimální doba mezi dvěma po sobě jdoucími obratovými segmenty nesmí být záporná. |
|
| 2480 | +errorMinimumTackTypeSegmentDurationMustNotBeNegative=Minimální trvání obratového segmentu nesmí být záporné. |
|
| 2481 | +minimumDurationBetweenAdjacentTackTypeSegmentsInSeconds=Minimální doba mezi dvěma po sobě jdoucími obratovými segmenty (s) |
|
| 2482 | +minimumTackTypeSegmentsDurationInSeconds=Minimální trvání obratových segmentů (s) |
|
| 2483 | +errorNoAuthenticationParamsForGoogleMapsFound=Chyba: Nebyly nalezeny žádné autentizační parametry pro API Map Google: {0}. |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_da.properties
| ... | ... | @@ -2475,3 +2475,9 @@ errorObtainingCourseAreasForLeaderboard=Fejl ved hentning af baneområder for ra |
| 2475 | 2475 | tackType=Lang/kort stagvende |
| 2476 | 2476 | tackTypeTooltip=Ved bidevind: Hvis forskellen mellem COG og næste waypoint-retning er mindre end den mellem COG og vindretning, er det lang stagvende (1,0), og hvis den er mindre, kort stagvende (-1,0). Ved læns: Som ved bidevind, men i stedet for "vindretning" bruges den modsatte retning, dvs. den retning, vinden blæser mod. Ved kryds: Som ved bidevind, men i stedet for at sammenligne med "COG og vindretning" bruges 10°. |
| 2477 | 2477 | tackTypeUnit=L=1,0, K=-1,0, Ukendt=0,0 |
| 2478 | +tackTypeSegments=Segmenter for stagvendetyper |
|
| 2479 | +errorMinimumDurationBetweenAdjacentTackTypeSegmentsMustNotBeNegative=Min. varighed mellem segmenter for tilstødende stagvendetyper må ikke være negativ |
|
| 2480 | +errorMinimumTackTypeSegmentDurationMustNotBeNegative=Min. varighed mellem et segment for stagvendetyper må ikke være negativ |
|
| 2481 | +minimumDurationBetweenAdjacentTackTypeSegmentsInSeconds=Min. varighed mellem tilstødende segmenter for stagvendetyper (sek.) |
|
| 2482 | +minimumTackTypeSegmentsDurationInSeconds=Min. varighed mellem segmenter for stagvendetyper (sek.) |
|
| 2483 | +errorNoAuthenticationParamsForGoogleMapsFound=Fejl: Ingen autentifikationsparametre for Google Maps-API blev fundet: {0} |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_es.properties
| ... | ... | @@ -2475,3 +2475,9 @@ errorObtainingCourseAreasForLeaderboard=Error al obtener las áreas de regata pa |
| 2475 | 2475 | tackType=Bordo largo/corto |
| 2476 | 2476 | tackTypeTooltip=Para barlovento: si la diferencia entre COG y la dirección del punto de ruta siguiente es menor que el de entre COG y la dirección del viento, se trata de un bordo largo (1,0); si es menor, de un bordo corto (-1,0). Para sotavento: similar a barlovento pero en lugar de "dirección del viento", se utiliza la dirección opuesta. Es decir, hacia dónde sopla el viento. Para alcance: similar a barlovento, pero en lugar de comparar con "COG y dirección del viento", se utilizan 10°. |
| 2477 | 2477 | tackTypeUnit=L=1.0, C=-1.0, Desconocido=0.0 |
| 2478 | +tackTypeSegments=Segmentos de tipo de bordo |
|
| 2479 | +errorMinimumDurationBetweenAdjacentTackTypeSegmentsMustNotBeNegative=La duración mínima entre segmentos adyacentes de tipo de bordo no puede ser negativa |
|
| 2480 | +errorMinimumTackTypeSegmentDurationMustNotBeNegative=La duración mínima de un segmento de tipo de bordo no puede ser negativa |
|
| 2481 | +minimumDurationBetweenAdjacentTackTypeSegmentsInSeconds=Duración mínima entre segmentos adyacentes de tipo de bordo (s) |
|
| 2482 | +minimumTackTypeSegmentsDurationInSeconds=Duración mínima de segmentos adyacentes de tipo de bordo (s) |
|
| 2483 | +errorNoAuthenticationParamsForGoogleMapsFound=Error: No se han encontrado parámetros de autenticación para la API de Google Maps: {0} |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_fr.properties
| ... | ... | @@ -2475,3 +2475,9 @@ errorObtainingCourseAreasForLeaderboard=Erreur lors de l''accès aux zones de co |
| 2475 | 2475 | tackType=Virement long/court |
| 2476 | 2476 | tackTypeTooltip=Dans le lit du vent : si la différence entre la route fond et la direction du prochain point de cheminement est inférieure à celle entre la route fond et la direction du vent, il s''agit d''un virement long (1,0) ; si la différence est inférieure, il s''agit d''un virement court (-1,0). Avec vent arrière : similaire à la situation dans le lit du vent, mais à la place de la « direction du vent », on utilise la direction inverse, c''est-à-dire la direction vers laquelle souffle le vent. Pour l''atteinte d''un point : similaire à la situation dans le lit du vent, mais au lieu de comparer « route fond et direction du vent », on utilise 10°. |
| 2477 | 2477 | tackTypeUnit=L=1,0, S=-1,0, Inconnu=0.0 |
| 2478 | +tackTypeSegments=Tronçons de type Virement |
|
| 2479 | +errorMinimumDurationBetweenAdjacentTackTypeSegmentsMustNotBeNegative=La durée minimale entre deux tronçons de type Virement adjacents ne doit pas être négative. |
|
| 2480 | +errorMinimumTackTypeSegmentDurationMustNotBeNegative=La durée minimale d''un tronçon de type Virement ne doit pas être négative. |
|
| 2481 | +minimumDurationBetweenAdjacentTackTypeSegmentsInSeconds=Durée minimale (en s) entre deux tronçons de type Virement adjacents |
|
| 2482 | +minimumTackTypeSegmentsDurationInSeconds=Durée minimale (en s) d''un tronçon de type Virement |
|
| 2483 | +errorNoAuthenticationParamsForGoogleMapsFound=Erreur : aucun paramètre d''authentification trouvé pour l''API Google Maps : {0} |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_it.properties
| ... | ... | @@ -2475,3 +2475,9 @@ errorObtainingCourseAreasForLeaderboard=Errore durante l''ottenimento delle aree |
| 2475 | 2475 | tackType=Bordo lungo/corto |
| 2476 | 2476 | tackTypeTooltip=Sopravento: se la differenza tra la rotta effettiva al fondo e la successiva direzione del waypoint è superiore a quella tra la rotta effettiva al fondo e la direzione del vento si parla di bordo lungo (1,0); se è inferiore si parla di bordo corto (-1,0). Sottovento: simile al sopravento ma anziché utilizzare la direzione del vento si utilizza la direzione opposta, quindi la direzione verso cui soffia il vento. Per l''andatura al lasco: simile al sopravento ma anziché confrontare rotta effettiva al fondo e direzione del vento, si utilizzano 10°. |
| 2477 | 2477 | tackTypeUnit=L=1,0, B=-1,0, Sconosciuto=0,0 |
| 2478 | +tackTypeSegments=Segmenti tipo bordo |
|
| 2479 | +errorMinimumDurationBetweenAdjacentTackTypeSegmentsMustNotBeNegative=La durata minima tra segmenti adiacenti di tipo bordo non può essere negativa |
|
| 2480 | +errorMinimumTackTypeSegmentDurationMustNotBeNegative=La durata minima di un segmento di tipo bordo non può essere negativa |
|
| 2481 | +minimumDurationBetweenAdjacentTackTypeSegmentsInSeconds=Durata minima tra i segmenti adiacenti di tipo bordo (s) |
|
| 2482 | +minimumTackTypeSegmentsDurationInSeconds=Durata minima dei segmenti di tipo bordo (s) |
|
| 2483 | +errorNoAuthenticationParamsForGoogleMapsFound=Errore: nessun parametro di autenticazione trovato per l''API di Google Maps API: {0} |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ja.properties
| ... | ... | @@ -2475,3 +2475,9 @@ errorObtainingCourseAreasForLeaderboard=リーダーボード {0} のコース |
| 2475 | 2475 | tackType=ロング/ショートタック |
| 2476 | 2476 | tackTypeTooltip=アップウィンドの場合: COG (対地針路) と次の変針点方向との間の差異が COG と風向との間の差異より小さい場合はロングタック (1.0) です。より小さい場合はショートタック (-1.0) です。ダウンウィンドの場合: アップウィンドに類似していますが、"風向" ではなくその反対方向を使用します (風が正面から吹き付けているときの向き)。リーチングの場合: アップウィンドに類似していますが、"COG と風向" との比較ではなく、10°を使用します。 |
| 2477 | 2477 | tackTypeUnit=L = 1.0、S = -1.0、不明 = 0.0 |
| 2478 | +tackTypeSegments=タックタイプセグメント |
|
| 2479 | +errorMinimumDurationBetweenAdjacentTackTypeSegmentsMustNotBeNegative=隣り合ったタックタイプセグメント間の最小時間はマイナスの値であってはなりません。 |
|
| 2480 | +errorMinimumTackTypeSegmentDurationMustNotBeNegative=タックタイプセグメントの最小時間はマイナスの値であってはなりません。 |
|
| 2481 | +minimumDurationBetweenAdjacentTackTypeSegmentsInSeconds=隣り合ったタックタイプセグメント間の最小時間 (秒) |
|
| 2482 | +minimumTackTypeSegmentsDurationInSeconds=タックタイプセグメントの最小時間 (秒) |
|
| 2483 | +errorNoAuthenticationParamsForGoogleMapsFound=エラー: Google Maps API の認証パラメータが見つかりませんでした: {0} |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_pt.properties
| ... | ... | @@ -2475,3 +2475,9 @@ errorObtainingCourseAreasForLeaderboard=Erro ao obter áreas de percurso para pa |
| 2475 | 2475 | tackType=Cambada longa/curta |
| 2476 | 2476 | tackTypeTooltip=Para contravento: se a diferença entre o percurso no fundo e a direção do próximo waypoint for inferior à diferença entre o percurso no fundo e a direção do vento, é cambada longa (1,0), se for inferior, é cambada curta (-1,0). Para popa: semelhante ao contravento, mas em vez da "direção do vento", usa-se a direção oposta. Isto é, para onde o vento está soprando. Para través: semelhante ao contravento, mas em vez da comparação com "percurso no fundo e direção do vento", usa-se 10º. |
| 2477 | 2477 | tackTypeUnit=L= 1,0, C= -1,0, Desconhecido= 0,0 |
| 2478 | +tackTypeSegments=Segmentos de tipo de cambada |
|
| 2479 | +errorMinimumDurationBetweenAdjacentTackTypeSegmentsMustNotBeNegative=A duração mínima entre os segmentos adjacentes de tipo de cambada não deve ser negativa |
|
| 2480 | +errorMinimumTackTypeSegmentDurationMustNotBeNegative=A duração mínima de um segmento de tipo de cambada não deve ser negativa |
|
| 2481 | +minimumDurationBetweenAdjacentTackTypeSegmentsInSeconds=Duração mínima entre segmentos adjacentes de tipo de cambada (s) |
|
| 2482 | +minimumTackTypeSegmentsDurationInSeconds=Duração mínima de segmentos de tipo de cambada (s) |
|
| 2483 | +errorNoAuthenticationParamsForGoogleMapsFound=Erro: não foram encontrados parâmetros de autenticação para a API do Google Maps: {0} |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties
| ... | ... | @@ -2475,3 +2475,9 @@ errorObtainingCourseAreasForLeaderboard=Ошибка при получении |
| 2475 | 2475 | tackType=Длинный /короткий галс |
| 2476 | 2476 | tackTypeTooltip=Против ветра: если разница между COG и направлением следующей путевой точки меньше разницы между COG и направлением ветра, то это длинный галс (1.0); если меньше, короткий галс (-1.0). По ветру: определяется так же, как против ветра, но вместо направления ветра используют противоположное направление. То есть, куда дует ветер. Полный ветер: определяется так же, как против ветра, но вместо сравнения с COG и направлением ветра используют 10°. |
| 2477 | 2477 | tackTypeUnit=Д=1.0, К=-1.0, Неизвестно=0.0 |
| 2478 | +tackTypeSegments=Сегменты типов галсов |
|
| 2479 | +errorMinimumDurationBetweenAdjacentTackTypeSegmentsMustNotBeNegative=Минимальная продолжительность между смежными сегментами типов галсов не должна быть отрицательной |
|
| 2480 | +errorMinimumTackTypeSegmentDurationMustNotBeNegative=Минимальная продолжительность сегмента типа галса не должна быть отрицательной |
|
| 2481 | +minimumDurationBetweenAdjacentTackTypeSegmentsInSeconds=Минимальная продолжительность между сегментами типов галсов (сек) |
|
| 2482 | +minimumTackTypeSegmentsDurationInSeconds=Минимальная продолжительность сегментов типов галсов (сек) |
|
| 2483 | +errorNoAuthenticationParamsForGoogleMapsFound=Ошибка: не найдены параметры полномочий для API Google Maps: {0} |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_sl.properties
| ... | ... | @@ -2475,3 +2475,9 @@ errorObtainingCourseAreasForLeaderboard=Napaka pri pridobivanju območij proge z |
| 2475 | 2475 | tackType=Dolgo/kratko prečenje |
| 2476 | 2476 | tackTypeTooltip=Proti vetru: Če je razlika med COG in smerjo vmesnega cilja poti manjša od razlike med COG in smerjo vetra, je dolgo prečenje (1,0); če je manjše kratko prečenje (-1,0). Z vetrom: Podobno kot proti vetru, vendar namesto "smeri vetra" uporabite nasprotno smer. Torej, kam veter piha. Za doseganje: Podobno kot proti vetru, vendar namesto primerjave z "COG in smerjo vetra" uporabite 10°. |
| 2477 | 2477 | tackTypeUnit=D=1,0, K=-1,0, neznano=0,0 |
| 2478 | +tackTypeSegments=Segmenti vrste prečenja |
|
| 2479 | +errorMinimumDurationBetweenAdjacentTackTypeSegmentsMustNotBeNegative=Minimalno trajanje med sosednjimi segmenti vrste prečenja ne sme biti negativno |
|
| 2480 | +errorMinimumTackTypeSegmentDurationMustNotBeNegative=Minimalno trajanje segmenta vrste prečenja ne sme biti negativno |
|
| 2481 | +minimumDurationBetweenAdjacentTackTypeSegmentsInSeconds=Minimalno trajanje med sosednjimi segmenti vrste prečenja (s) |
|
| 2482 | +minimumTackTypeSegmentsDurationInSeconds=Minimalno trajanje segmentov vrste prečenja (s) |
|
| 2483 | +errorNoAuthenticationParamsForGoogleMapsFound=Napaka: Parametri preverjanja pristnosti za API aplikacije Google Maps niso bilo najdeni: {0} |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_zh.properties
| ... | ... | @@ -2475,3 +2475,9 @@ errorObtainingCourseAreasForLeaderboard=获取积分榜 {0} 的场地区域出 |
| 2475 | 2475 | tackType=长程/短程迎风转向 |
| 2476 | 2476 | tackTypeTooltip=对于迎风:如果对地航向与下个航路点方向之间的差值小于对地航向与风向之间的差值,则为长程迎风转向 (1.0);如果更小,则为短程迎风转向 (-1.0)。对于顺风:与迎风类似,但不是“风向”,而是使用相反的方向。也就是风吹向哪里。对于横风:与迎风类似,但不与“对地航向和风向”进行比较,而是使用 10°。 |
| 2477 | 2477 | tackTypeUnit=L=1.0,S=-1.0,未知=0.0 |
| 2478 | +tackTypeSegments=迎风转向类型段 |
|
| 2479 | +errorMinimumDurationBetweenAdjacentTackTypeSegmentsMustNotBeNegative=相邻迎风转向类型段之间的最短持续时间不得为负 |
|
| 2480 | +errorMinimumTackTypeSegmentDurationMustNotBeNegative=迎风转向类型段的最短持续时间不得为负 |
|
| 2481 | +minimumDurationBetweenAdjacentTackTypeSegmentsInSeconds=相邻迎风转向类型段之间的最短持续时间 (s) |
|
| 2482 | +minimumTackTypeSegmentsDurationInSeconds=迎风转向类型段的最短持续时间 (s) |
|
| 2483 | +errorNoAuthenticationParamsForGoogleMapsFound=错误:未找到 Google 地图 API 的身份验证参数:{0} |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java
| ... | ... | @@ -123,6 +123,7 @@ import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapHelpLinesSettings.Hel |
| 123 | 123 | import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapZoomSettings.ZoomTypes; |
| 124 | 124 | import com.sap.sailing.gwt.ui.client.shared.racemap.windladder.WindLadder; |
| 125 | 125 | import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil; |
| 126 | +import com.sap.sailing.gwt.ui.raceboard.RaceboardDropdownResources; |
|
| 126 | 127 | import com.sap.sailing.gwt.ui.server.SailingServiceImpl; |
| 127 | 128 | import com.sap.sailing.gwt.ui.shared.CompactBoatPositionsDTO; |
| 128 | 129 | import com.sap.sailing.gwt.ui.shared.ControlPointDTO; |
| ... | ... | @@ -222,9 +223,14 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme |
| 222 | 223 | */ |
| 223 | 224 | private DelegateCoordinateSystem coordinateSystem; |
| 224 | 225 | |
| 226 | + /** |
|
| 227 | + * A panel with flex-box display, representing the semi-transparent header bar. It aligns its flex-items |
|
| 228 | + * on the center line vertically and uses "space-between" for the horizontal alignment. It has a fixed height |
|
| 229 | + * and uses "border-box" sizing. Things to display in the header bar at the top of the map must be added |
|
| 230 | + * as elements to it, making the children "flex-items" which may again use "display: flex" in their styles |
|
| 231 | + * to nest flex boxes in the header. |
|
| 232 | + */ |
|
| 225 | 233 | private FlowPanel headerPanel; |
| 226 | - private AbsolutePanel panelForLeftHeaderLabels; |
|
| 227 | - private AbsolutePanel panelForRightHeaderLabels; |
|
| 228 | 234 | |
| 229 | 235 | private final SailingServiceAsync sailingService; |
| 230 | 236 | private final ErrorReporter errorReporter; |
| ... | ... | @@ -469,6 +475,12 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme |
| 469 | 475 | private boolean autoZoomInProgress; |
| 470 | 476 | |
| 471 | 477 | /** |
| 478 | + * The length of the advantage line; default is 1000m, but upon map initialization and zoom it is set to the |
|
| 479 | + * length of the diagonal spanning the map, so it should always cover the entire map. See also bug 616. |
|
| 480 | + */ |
|
| 481 | + private Distance advantageLineLength = new MeterDistance(1000); |
|
| 482 | + |
|
| 483 | + /** |
|
| 472 | 484 | * Tells whether currently an orientation change is in progress; this is required handle map events during the configuration of the map |
| 473 | 485 | * during an orientation change. |
| 474 | 486 | */ |
| ... | ... | @@ -687,10 +699,156 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme |
| 687 | 699 | this.hasPolar = false; |
| 688 | 700 | headerPanel = new FlowPanel(); |
| 689 | 701 | headerPanel.setStyleName("RaceMap-HeaderPanel"); |
| 690 | - panelForLeftHeaderLabels = new AbsolutePanel(); |
|
| 691 | - panelForLeftHeaderLabels.setHeight("60px"); |
|
| 692 | - panelForRightHeaderLabels = new AbsolutePanel(); |
|
| 693 | - panelForRightHeaderLabels.setHeight("60px"); |
|
| 702 | + /* |
|
| 703 | + * Here goes a test file with flex box sizing: |
|
| 704 | + |
|
| 705 | +<html> |
|
| 706 | + <head> |
|
| 707 | +<style> |
|
| 708 | +* { |
|
| 709 | + box-sizing: border-box; |
|
| 710 | +} |
|
| 711 | + |
|
| 712 | +.header-bar { |
|
| 713 | + display: flex; |
|
| 714 | + justify-content: space-between; |
|
| 715 | + align-items: center; |
|
| 716 | + height: 64px; |
|
| 717 | + overflow: hidden; |
|
| 718 | + background: rgba(0, 3, 51, 0.5); |
|
| 719 | + padding: 10px 5px; |
|
| 720 | +} |
|
| 721 | + |
|
| 722 | +.logo { |
|
| 723 | + height: 32px; |
|
| 724 | + width: 64px; |
|
| 725 | + min-width: 64px; |
|
| 726 | + background-image: url("data:image/svg+xml;base64,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNDEyLjM4IDIwNCI+PGRlZnM+PHN0eWxlPi5jbHMtMSwuY2xzLTJ7ZmlsbC1ydWxlOmV2ZW5vZGQ7fS5jbHMtMXtmaWxsOnVybCgjbGluZWFyLWdyYWRpZW50KTt9LmNscy0ye2ZpbGw6I2ZmZjt9PC9zdHlsZT48bGluZWFyR3JhZGllbnQgaWQ9ImxpbmVhci1ncmFkaWVudCIgeDE9IjIwNi4xOSIgeDI9IjIwNi4xOSIgeTI9IjIwNCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iIzAwYjhmMSIvPjxzdG9wIG9mZnNldD0iMC4wMiIgc3RvcC1jb2xvcj0iIzAxYjZmMCIvPjxzdG9wIG9mZnNldD0iMC4zMSIgc3RvcC1jb2xvcj0iIzBkOTBkOSIvPjxzdG9wIG9mZnNldD0iMC41OCIgc3RvcC1jb2xvcj0iIzE3NzVjOCIvPjxzdG9wIG9mZnNldD0iMC44MiIgc3RvcC1jb2xvcj0iIzFjNjViZiIvPjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzFlNWZiYiIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjx0aXRsZT5TQVBfZ3JhZF9SX3Njcm5fWmVpY2hlbmZsw6RjaGUgMTwvdGl0bGU+PHBvbHlsaW5lIGNsYXNzPSJjbHMtMSIgcG9pbnRzPSIwIDIwNCAyMDguNDEgMjA0IDQxMi4zOCAwIDAgMCAwIDIwNCIvPjxwYXRoIGNsYXNzPSJjbHMtMiIgZD0iTTI0NC43MywzOC4zNmwtNDAuNiwwdjk2LjUyTDE2OC42NywzOC4zM0gxMzMuNTFsLTMwLjI3LDgwLjcyQzEwMCw5OC43LDc5LDkxLjY3LDYyLjQsODYuNCw1MS40Niw4Mi44OSwzOS44NSw3Ny43Miw0MCw3MmMuMDktNC42OCw2LjIzLTksMTguMzgtOC4zOCw4LjE3LjQzLDE1LjM3LDEuMDksMjkuNzEsOGwxNC4xLTI0LjU1Qzg5LjA2LDQwLjQyLDcxLDM2LjIxLDU2LjE3LDM2LjE5aC0uMDljLTE3LjI4LDAtMzEuNjgsNS42LTQwLjYsMTQuODNBMzQuMjMsMzQuMjMsMCwwLDAsNS43Nyw3NC43QzUuNTQsODcuMTUsMTAuMTEsOTYsMTkuNzEsMTAzYzguMSw1Ljk0LDE4LjQ2LDkuNzksMjcuNiwxMi42MiwxMS4yNywzLjQ5LDIwLjQ3LDYuNTMsMjAuMzYsMTNBOS41Nyw5LjU3LDAsMCwxLDY1LDEzNWMtMi44MSwyLjktNy4xMyw0LTEzLjA5LDQuMS0xMS40OS4yNC0yMC0xLjU2LTMzLjYxLTkuNTlMNS43NywxNTQuNDJhOTMuNzcsOTMuNzcsMCwwLDAsNDYsMTIuMjJsMi4xMSwwYzE0LjI0LS4yNSwyNS43NC00LjMxLDM0LjkyLTExLjcxLjUzLS40MSwxLS44NCwxLjQ5LTEuMjhMODYuMTcsMTY0LjVIMTIzbDYuMTktMTguODJhNjcuNDYsNjcuNDYsMCwwLDAsMjEuNjgsMy40Myw2OC4zMyw2OC4zMywwLDAsMCwyMS4xNi0zLjI1bDYsMTguNjRoNjAuMTR2LTM5aDEzLjExYzMxLjcxLDAsNTAuNDYtMTYuMTUsNTAuNDYtNDMuMkMzMDEuNzQsNTIuMTksMjgzLjUyLDM4LjM2LDI0NC43MywzOC4zNlpNMTUwLjkxLDEyMWEzNi45MywzNi45MywwLDAsMS0xMy0yLjI4bDEyLjg3LTQwLjU5SDE1MWwxMi42NSw0MC43MUEzOC41LDM4LjUsMCwwLDEsMTUwLjkxLDEyMVptOTYuMi0yMy4zM2gtOC45NFY2NC45MWg4Ljk0YzExLjkzLDAsMjEuNDQsNCwyMS40NCwxNi4xNCwwLDEyLjYtOS41MSwxNi41Ny0yMS40NCwxNi41NyIvPjwvc3ZnPg=="); |
|
| 727 | +} |
|
| 728 | + |
|
| 729 | +.dropdown { |
|
| 730 | + display: flex; |
|
| 731 | + flex-shrink: 100; |
|
| 732 | + align-items: center; |
|
| 733 | + max-height: 64px; |
|
| 734 | + margin: 5px; |
|
| 735 | + border: 1px solid #ccc; |
|
| 736 | + cursor: pointer; |
|
| 737 | + font-size: large; |
|
| 738 | +} |
|
| 739 | + |
|
| 740 | +@media (max-width: 500px) { |
|
| 741 | + .dropdown { |
|
| 742 | + display: flex; |
|
| 743 | + flex-shrink: 100; |
|
| 744 | + align-items: center; |
|
| 745 | + max-height: 64px; |
|
| 746 | + margin: 5px; |
|
| 747 | + border: 1px solid #ccc; |
|
| 748 | + cursor: pointer; |
|
| 749 | + font-size: small; |
|
| 750 | + } |
|
| 751 | +} |
|
| 752 | + |
|
| 753 | +.dropdown-text { |
|
| 754 | + max-height: 64px; |
|
| 755 | +} |
|
| 756 | + |
|
| 757 | +.dropdown-arrow { |
|
| 758 | + margin-left: 10px; |
|
| 759 | +} |
|
| 760 | + |
|
| 761 | +.filler { |
|
| 762 | + display: flex; |
|
| 763 | + flex-grow: 10; |
|
| 764 | +} |
|
| 765 | + |
|
| 766 | +.event-date-data-by { |
|
| 767 | + display: flex; |
|
| 768 | + flex-grow: 100; |
|
| 769 | + flex-direction: column; |
|
| 770 | + align-items: flex-end; |
|
| 771 | + padding: 5px; |
|
| 772 | + min-width: 0; |
|
| 773 | +} |
|
| 774 | + |
|
| 775 | +.home-and-event { |
|
| 776 | + display: flex; |
|
| 777 | + justify-content: flex-end; |
|
| 778 | + align-items: center; |
|
| 779 | + min-width: 0; |
|
| 780 | + width: 100%; |
|
| 781 | +} |
|
| 782 | + |
|
| 783 | +.help-and-date { |
|
| 784 | + display: flex; |
|
| 785 | + justify-content: flex-end; |
|
| 786 | + align-items: center; |
|
| 787 | + min-width: 0; |
|
| 788 | + width: 100%; |
|
| 789 | +} |
|
| 790 | + |
|
| 791 | +.help-button, .home-button { |
|
| 792 | +} |
|
| 793 | + |
|
| 794 | +.event-name { |
|
| 795 | + white-space: nowrap; |
|
| 796 | + overflow: hidden; |
|
| 797 | + text-overflow: ellipsis; |
|
| 798 | +} |
|
| 799 | + |
|
| 800 | +.timestamp { |
|
| 801 | + white-space: nowrap; |
|
| 802 | + overflow: hidden; |
|
| 803 | + text-overflow: ellipsis; |
|
| 804 | +} |
|
| 805 | + |
|
| 806 | +.data-provider { |
|
| 807 | + white-space: nowrap; |
|
| 808 | + overflow: hidden; |
|
| 809 | + text-overflow: ellipsis; |
|
| 810 | + min-width: 0; |
|
| 811 | + width: 100%; |
|
| 812 | + text-align: right; |
|
| 813 | +} |
|
| 814 | + |
|
| 815 | +.sign-in { |
|
| 816 | + flex-grow: 0; |
|
| 817 | + flex-shrink: 100; |
|
| 818 | +} |
|
| 819 | + |
|
| 820 | +@media (max-width: 768px) { |
|
| 821 | + .dropdown { |
|
| 822 | + font-size: 14px; |
|
| 823 | + } |
|
| 824 | +} |
|
| 825 | +</style> |
|
| 826 | + </head> |
|
| 827 | + <body> |
|
| 828 | + <div style="z-index: -1; color: red; position: fixed; top: 0; left: 0">Hello World</div> |
|
| 829 | + <div class="header-bar"> |
|
| 830 | + <div class="logo"></div> |
|
| 831 | + <div class="dropdown"> |
|
| 832 | + <span class="dropdown-text">Series/Race Column/Fleet Names</span> |
|
| 833 | + <div class="dropdown-arrow">▼</div> |
|
| 834 | + </div> |
|
| 835 | + <div class="filler"></div> |
|
| 836 | + <div class="event-date-data-by"> |
|
| 837 | + <div class="home-and-event"> |
|
| 838 | + <button class="home-button">Home</button> |
|
| 839 | + <span class="event-name">Event/Leaderboard Group Name</span> |
|
| 840 | + </div> |
|
| 841 | + <div class="help-and-date"> |
|
| 842 | + <button class="help-button">Help</button> |
|
| 843 | + <span class="timestamp">This is a very long Timestamp</span> |
|
| 844 | + </div> |
|
| 845 | + <a class="data-provider" href="#">This is a very long Data by...</a> |
|
| 846 | + </div> |
|
| 847 | + <span class="sign-in">Sign In/User</span> |
|
| 848 | + </div> |
|
| 849 | + </body> |
|
| 850 | +</html> |
|
| 851 | + */ |
|
| 694 | 852 | raceMapStyle = raceMapResources.raceMapStyle(); |
| 695 | 853 | raceMapStyle.ensureInjected(); |
| 696 | 854 | combinedWindPanel = new CombinedWindPanel(this, raceMapImageManager, raceMapStyle, stringMessages, coordinateSystem, paywallResolver, raceMapLifecycle.getRaceDTO()); |
| ... | ... | @@ -864,6 +1022,8 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme |
| 864 | 1022 | && paywallResolver.hasPermission(SecuredDomainType.TrackedRaceActions.VIEWSTREAMLETS, raceMapLifecycle.getRaceDTO())) { |
| 865 | 1023 | streamletOverlay.onBoundsChanged(map.getZoom() != currentZoomLevel); |
| 866 | 1024 | } |
| 1025 | + advantageLineLength = getMapDiagonalVisibleDistance(); |
|
| 1026 | + showAdvantageLine(getCompetitorsToShow(), getTimer().getTime(), /* timeForPositionTransitionMillis */ -1 /* (no transition) */); |
|
| 867 | 1027 | } |
| 868 | 1028 | |
| 869 | 1029 | private void showLayoutsAfterAnimationFinishes() { |
| ... | ... | @@ -920,6 +1080,8 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme |
| 920 | 1080 | && paywallResolver.hasPermission(SecuredDomainType.TrackedRaceActions.VIEWSTREAMLETS, raceMapLifecycle.getRaceDTO())) { |
| 921 | 1081 | streamletOverlay.setCanvasSettings(); |
| 922 | 1082 | } |
| 1083 | + advantageLineLength = getMapDiagonalVisibleDistance(); |
|
| 1084 | + showAdvantageLine(getCompetitorsToShow(), getTimer().getTime(), /* timeForPositionTransitionMillis */ -1 /* (no transition) */); |
|
| 923 | 1085 | refreshMapWithoutAnimation(); |
| 924 | 1086 | if (!mapFirstZoomDone) { |
| 925 | 1087 | zoomMapToNewBounds(settings.getZoomSettings().getNewBounds(RaceMap.this)); |
| ... | ... | @@ -945,6 +1107,8 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme |
| 945 | 1107 | currentMapBounds = map.getBounds(); |
| 946 | 1108 | currentZoomLevel = newZoomLevel; |
| 947 | 1109 | headerPanel.getElement().getStyle().setWidth(map.getOffsetWidth(), Unit.PX); |
| 1110 | + advantageLineLength = getMapDiagonalVisibleDistance(); |
|
| 1111 | + showAdvantageLine(getCompetitorsToShow(), getTimer().getTime(), /* timeForPositionTransitionMillis */ -1 /* (no transition) */); |
|
| 948 | 1112 | } |
| 949 | 1113 | }); |
| 950 | 1114 | // If there was a time change before the API was loaded, reset the time |
| ... | ... | @@ -980,7 +1144,7 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme |
| 980 | 1144 | if (showHeaderPanel) { |
| 981 | 1145 | createHeaderPanel(map); |
| 982 | 1146 | if (ClientConfiguration.getInstance().isBrandingActive()) { |
| 983 | - getLeftHeaderPanel().insert(createSAPLogo(), 0); |
|
| 1147 | + getHeaderPanel().insert(createSAPLogo(), 0); |
|
| 984 | 1148 | } |
| 985 | 1149 | } |
| 986 | 1150 | createAdvancedFunctionsButtonGroup(showMapControls); |
| ... | ... | @@ -1129,6 +1293,10 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme |
| 1129 | 1293 | // we need a panel that does not have any transparency to have the |
| 1130 | 1294 | // labels shown in the right color. This panel also needs to have |
| 1131 | 1295 | // a higher z-index than other elements on the map |
| 1296 | + AbsolutePanel panelForLeftHeaderLabels = new AbsolutePanel(); |
|
| 1297 | + panelForLeftHeaderLabels.setHeight("60px"); |
|
| 1298 | + AbsolutePanel panelForRightHeaderLabels = new AbsolutePanel(); |
|
| 1299 | + panelForRightHeaderLabels.setHeight("60px"); |
|
| 1132 | 1300 | map.setControls(ControlPosition.TOP_LEFT, panelForLeftHeaderLabels); |
| 1133 | 1301 | panelForLeftHeaderLabels.getElement().getParentElement().getStyle().setProperty("zIndex", "1"); |
| 1134 | 1302 | panelForLeftHeaderLabels.getElement().getStyle().setProperty("overflow", "visible"); |
| ... | ... | @@ -1143,7 +1311,12 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme |
| 1143 | 1311 | headerPanel.ensureDebugId("headerPanel"); |
| 1144 | 1312 | // some sort of hack: not positioning TOP_LEFT because then the |
| 1145 | 1313 | // controls at RIGHT would not get the correct top setting |
| 1146 | - map.setControls(ControlPosition.TOP_RIGHT, headerPanel); |
|
| 1314 | + map.setControls(ControlPosition.TOP_RIGHT, panelForRightHeaderLabels); |
|
| 1315 | + rootPanel.add(headerPanel); |
|
| 1316 | + } |
|
| 1317 | + |
|
| 1318 | + public FlowPanel getHeaderPanel() { |
|
| 1319 | + return headerPanel; |
|
| 1147 | 1320 | } |
| 1148 | 1321 | |
| 1149 | 1322 | private Button createSettingsButton(MapWidget map) { |
| ... | ... | @@ -1309,17 +1482,6 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme |
| 1309 | 1482 | return simulationOverlay; |
| 1310 | 1483 | } |
| 1311 | 1484 | |
| 1312 | - /** |
|
| 1313 | - * @return the Panel where labels or other controls for the header can be positioned |
|
| 1314 | - */ |
|
| 1315 | - public AbsolutePanel getLeftHeaderPanel() { |
|
| 1316 | - return panelForLeftHeaderLabels; |
|
| 1317 | - } |
|
| 1318 | - |
|
| 1319 | - public AbsolutePanel getRightHeaderPanel() { |
|
| 1320 | - return panelForRightHeaderLabels; |
|
| 1321 | - } |
|
| 1322 | - |
|
| 1323 | 1485 | @Override |
| 1324 | 1486 | public void raceTimesInfosReceived(Map<RegattaAndRaceIdentifier, RaceTimesInfoDTO> raceTimesInfos, long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) { |
| 1325 | 1487 | timer.adjustClientServerOffset(clientTimeWhenRequestWasSent, serverTimeDuringRequest, clientTimeWhenResponseWasReceived); |
| ... | ... | @@ -2007,7 +2169,6 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme |
| 2007 | 2169 | new com.sap.sse.common.Util.Pair<Integer, CompetitorDTO>(legOfLeaderCompetitor, leadingCompetitorDTO); |
| 2008 | 2170 | } |
| 2009 | 2171 | |
| 2010 | - final static Distance advantageLineLength = new MeterDistance(1000); // TODO this should probably rather scale with the visible area of the map; bug 616 |
|
| 2011 | 2172 | private void showAdvantageLine(Iterable<CompetitorDTO> competitorsToShow, Date date, long timeForPositionTransitionMillis) { |
| 2012 | 2173 | if (map != null && lastRaceTimesInfo != null && !quickFlagDataProvider.getQuickRanks().isEmpty() |
| 2013 | 2174 | && lastCombinedWindTrackInfoDTO != null) { |
| ... | ... | @@ -2054,7 +2215,7 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme |
| 2054 | 2215 | } else { |
| 2055 | 2216 | switch (lastBoatFix.legType) { |
| 2056 | 2217 | case UPWIND: |
| 2057 | - case DOWNWIND: { |
|
| 2218 | + case DOWNWIND: |
|
| 2058 | 2219 | rotatedBearingDeg1 = bearingOfCombinedWindInDeg + 90.0; |
| 2059 | 2220 | if (rotatedBearingDeg1 >= 360.0) { |
| 2060 | 2221 | rotatedBearingDeg1 -= 360.0; |
| ... | ... | @@ -2063,9 +2224,8 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme |
| 2063 | 2224 | if (rotatedBearingDeg2 < 0.0) { |
| 2064 | 2225 | rotatedBearingDeg2 += 360.0; |
| 2065 | 2226 | } |
| 2066 | - break; |
|
| 2067 | - } |
|
| 2068 | - case REACHING: { |
|
| 2227 | + break; |
|
| 2228 | + case REACHING: |
|
| 2069 | 2229 | rotatedBearingDeg1 = legInfoDTO.legBearingInDegrees + 90.0; |
| 2070 | 2230 | if (rotatedBearingDeg1 >= 360.0) { |
| 2071 | 2231 | rotatedBearingDeg1 -= 360.0; |
| ... | ... | @@ -2074,8 +2234,7 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme |
| 2074 | 2234 | if (rotatedBearingDeg2 < 0.0) { |
| 2075 | 2235 | rotatedBearingDeg2 += 360.0; |
| 2076 | 2236 | } |
| 2077 | - break; |
|
| 2078 | - } |
|
| 2237 | + break; |
|
| 2079 | 2238 | } |
| 2080 | 2239 | MVCArray<LatLng> nextPath = MVCArray.newInstance(); |
| 2081 | 2240 | LatLng advantageLinePos1 = calculatePositionAlongRhumbline(posAheadOfFirstBoat, |
| ... | ... | @@ -3440,8 +3599,8 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme |
| 3440 | 3599 | } |
| 3441 | 3600 | // Adjust RaceMap headers to avoid overlapping based on the RaceMap width |
| 3442 | 3601 | boolean isCompactHeader = this.getOffsetWidth() <= 600; |
| 3443 | - getLeftHeaderPanel().setStyleName(COMPACT_HEADER_STYLE, isCompactHeader); |
|
| 3444 | - getRightHeaderPanel().setStyleName(COMPACT_HEADER_STYLE, isCompactHeader); |
|
| 3602 | + headerPanel.setStyleName(COMPACT_HEADER_STYLE, isCompactHeader); |
|
| 3603 | + headerPanel.setStyleName(RaceboardDropdownResources.INSTANCE.css().compactHeader(), isCompactHeader); |
|
| 3445 | 3604 | // Adjust combined wind and true north indicator panel indent, based on the RaceMap height |
| 3446 | 3605 | if (topLeftControlsWrapperPanel.getParent() != null) { |
| 3447 | 3606 | this.adjustLeftControlsIndent(); |
| ... | ... | @@ -3743,5 +3902,9 @@ public class RaceMap extends AbstractCompositeComponent<RaceMapSettings> impleme |
| 3743 | 3902 | public void setAddVideoToRaceButtonVisible(boolean visible) { |
| 3744 | 3903 | this.addVideoToRaceButton.setVisible(visible); |
| 3745 | 3904 | } |
| 3905 | + |
|
| 3906 | + private Distance getMapDiagonalVisibleDistance() { |
|
| 3907 | + return coordinateSystem.getPosition(currentMapBounds.getSouthWest()).getDistance(coordinateSystem.getPosition(currentMapBounds.getNorthEast())); |
|
| 3908 | + } |
|
| 3746 | 3909 | } |
| 3747 | 3910 |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java
| ... | ... | @@ -118,6 +118,7 @@ import com.sap.sse.common.settings.AbstractSettings; |
| 118 | 118 | import com.sap.sse.common.settings.Settings; |
| 119 | 119 | import com.sap.sse.gwt.client.ErrorReporter; |
| 120 | 120 | import com.sap.sse.gwt.client.async.AsyncActionsExecutor; |
| 121 | +import com.sap.sse.gwt.client.controls.dropdown.Dropdown; |
|
| 121 | 122 | import com.sap.sse.gwt.client.controls.slider.TimeSlider.BarOverlay; |
| 122 | 123 | import com.sap.sse.gwt.client.formfactor.DeviceDetector; |
| 123 | 124 | import com.sap.sse.gwt.client.panels.ResizableFlowPanel; |
| ... | ... | @@ -152,7 +153,7 @@ import com.sap.sse.security.ui.client.premium.PaywallResolver; |
| 152 | 153 | public class RaceBoardPanel |
| 153 | 154 | extends AbstractPerspectiveComposite<RaceBoardPerspectiveLifecycle, RaceBoardPerspectiveOwnSettings> |
| 154 | 155 | implements LeaderboardUpdateListener, PopupPositionProvider, RequiresResize { |
| 155 | - private static final String RACEBOARD_PATH = "/gwt/RaceBoard.html"; |
|
| 156 | + public static final String RACEBOARD_PATH = "/gwt/RaceBoard.html"; |
|
| 156 | 157 | private final SailingServiceAsync sailingService; |
| 157 | 158 | private SailingServiceWriteAsync sailingServiceWrite; |
| 158 | 159 | private final MediaServiceAsync mediaService; |
| ... | ... | @@ -194,7 +195,7 @@ public class RaceBoardPanel |
| 194 | 195 | private final RaceTimesInfoProvider raceTimesInfoProvider; |
| 195 | 196 | private final RaceMap raceMap; |
| 196 | 197 | |
| 197 | - private final FlowPanel raceInformationHeader; |
|
| 198 | + private final FlowPanel racePicker; |
|
| 198 | 199 | private final FlowPanel regattaAndRaceTimeInformationHeader; |
| 199 | 200 | private final AuthenticationMenuView userManagementMenuView; |
| 200 | 201 | private boolean currentRaceHasBeenSelectedOnce; |
| ... | ... | @@ -207,6 +208,7 @@ public class RaceBoardPanel |
| 207 | 208 | private static final RaceMapResources raceMapResources = GWT.create(RaceMapResources.class); |
| 208 | 209 | private TrackingConnectorInfoDTO trackingConnectorInfo; |
| 209 | 210 | private CompetitorFilterPanel competitorSearchTextBox; |
| 211 | + private final RaceboardContextDefinition raceboardContextDefinition; |
|
| 210 | 212 | |
| 211 | 213 | /** |
| 212 | 214 | * @param eventId |
| ... | ... | @@ -260,10 +262,11 @@ public class RaceBoardPanel |
| 260 | 262 | this.setRaceBoardName(selectedRaceIdentifier.getRaceName()); |
| 261 | 263 | this.asyncActionsExecutor = asyncActionsExecutor; |
| 262 | 264 | final RaceBoardPerspectiveOwnSettings parsedPerspectiveOwnSettings = settings.getPerspectiveOwnSettings(); |
| 265 | + this.raceboardContextDefinition = raceboardContextDefinition; |
|
| 263 | 266 | FlowPanel mainPanel = new ResizableFlowPanel(); |
| 264 | 267 | mainPanel.setSize("100%", "100%"); |
| 265 | - raceInformationHeader = new FlowPanel(); |
|
| 266 | - raceInformationHeader.setStyleName("RegattaRaceInformation-Header"); |
|
| 268 | + racePicker = new FlowPanel(); |
|
| 269 | + racePicker.setStyleName("RegattaRaceInformation-Header"); |
|
| 267 | 270 | regattaAndRaceTimeInformationHeader = new FlowPanel(); |
| 268 | 271 | regattaAndRaceTimeInformationHeader.setStyleName("RegattaAndRaceTime-Header"); |
| 269 | 272 | regattaAndRaceTimeInformationHeader.getElement().getStyle().setProperty("pointerEvents", "auto"); |
| ... | ... | @@ -297,6 +300,10 @@ public class RaceBoardPanel |
| 297 | 300 | mainCss.usermanagement_open(), mainCss.user_menu_premium()); |
| 298 | 301 | this.userManagementMenuView.asWidget().setStyleName(mainCss.usermanagement_icon()); |
| 299 | 302 | this.userManagementMenuView.asWidget().getElement().getStyle().setProperty("pointerEvents", "auto"); |
| 303 | + this.userManagementMenuView.asWidget().getElement().getStyle().setProperty("display", "inline-block"); |
|
| 304 | + this.userManagementMenuView.asWidget().getElement().getStyle().setProperty("position", "relative"); |
|
| 305 | + this.userManagementMenuView.asWidget().getElement().getStyle().setProperty("top", "0px"); |
|
| 306 | + this.userManagementMenuView.asWidget().getElement().getStyle().setProperty("right", "0px"); |
|
| 300 | 307 | timeRangeWithZoomModel = new TimeRangeWithZoomModel(); |
| 301 | 308 | final CompetitorColorProvider colorProvider = new CompetitorColorProviderImpl(selectedRaceIdentifier, competitorsAndTheirBoats); |
| 302 | 309 | competitorSelectionProvider = new RaceCompetitorSelectionModel(/* hasMultiSelection */ true, colorProvider, competitorsAndTheirBoats); |
| ... | ... | @@ -367,9 +374,15 @@ public class RaceBoardPanel |
| 367 | 374 | return leaderboardPanel.getLeaderboard(); |
| 368 | 375 | } |
| 369 | 376 | }, selectedRaceIdentifier, withSecurity.getUserService().getStorage()); |
| 370 | - raceMap.getLeftHeaderPanel().add(raceInformationHeader); |
|
| 371 | - raceMap.getRightHeaderPanel().add(regattaAndRaceTimeInformationHeader); |
|
| 372 | - raceMap.getRightHeaderPanel().add(userManagementMenuView); |
|
| 377 | + raceMap.getHeaderPanel().add(racePicker); |
|
| 378 | + final FlowPanel filler = new FlowPanel(); |
|
| 379 | + filler.setStyleName("RaceMap-Header-Filler"); // to create space between race picker and event/time/data-by display |
|
| 380 | + raceMap.getHeaderPanel().add(filler); |
|
| 381 | + raceMap.getHeaderPanel().add(regattaAndRaceTimeInformationHeader); |
|
| 382 | + final FlowPanel userManagementMenuPanel = new FlowPanel(); |
|
| 383 | + userManagementMenuPanel.addStyleName("AuthenticationButton"); |
|
| 384 | + userManagementMenuPanel.add(userManagementMenuView); |
|
| 385 | + raceMap.getHeaderPanel().add(userManagementMenuPanel); |
|
| 373 | 386 | addChildComponent(raceMap); |
| 374 | 387 | // add panel for tagging functionality, hidden if no URL parameter "tag" is passed |
| 375 | 388 | final String sharedTagURLParameter = parsedPerspectiveOwnSettings.getJumpToTag(); |
| ... | ... | @@ -761,35 +774,53 @@ public class RaceBoardPanel |
| 761 | 774 | } |
| 762 | 775 | quickFlagDataProvider.updateFlagData(leaderboard); |
| 763 | 776 | } |
| 777 | + |
|
| 778 | + private Dropdown createRaceDropDown(final RaceColumnDTO raceColumnOfSelectedRace, final FleetDTO fleetOfSelectedRace) { |
|
| 779 | + final Dropdown result = new Dropdown(RaceboardDropdownResources.INSTANCE); |
|
| 780 | + for (final RaceColumnDTO raceColumn : getLeaderboardPanel().getLeaderboard().getRaceList()) { |
|
| 781 | + for (final FleetDTO fleet : raceColumn.getFleets()) { |
|
| 782 | + final RaceIdentifier raceIdentifier = raceColumn.getRaceIdentifier(fleet); |
|
| 783 | + if (raceIdentifier != null) { |
|
| 784 | + final String displayName = (LeaderboardNameConstants.DEFAULT_SERIES_NAME.equals(raceColumn.getSeriesName())?"":(raceColumn.getSeriesName()+"/\u200b")) |
|
| 785 | + +raceColumn.getName() |
|
| 786 | + +(LeaderboardNameConstants.DEFAULT_FLEET_NAME.equals(fleet.getName())?"":("/\u200b"+fleet.getName())); |
|
| 787 | + final boolean selected = raceColumn.equals(raceColumnOfSelectedRace) && fleet.equals(fleetOfSelectedRace); |
|
| 788 | + result.addItem(displayName, /* link */ null, selected, ()->{ |
|
| 789 | + final RaceboardContextDefinition strippedRaceBoardContextDefinition = new RaceboardContextDefinition( |
|
| 790 | + raceIdentifier.getRegattaName(), raceIdentifier.getRaceName(), |
|
| 791 | + getLeaderboardPanel().getLeaderboard().getName(), raceboardContextDefinition.getLeaderboardGroupName(), |
|
| 792 | + raceboardContextDefinition.getLeaderboardGroupId(), raceboardContextDefinition.getEventId(), null); |
|
| 793 | + final LinkWithSettingsGenerator<Settings> linkWithSettingsGenerator = new LinkWithSettingsGenerator<>(RACEBOARD_PATH, strippedRaceBoardContextDefinition); |
|
| 794 | + final String url = linkWithSettingsGenerator.createUrl(getSettings()); // launch with the same settings as the current race |
|
| 795 | + Window.open(url, raceIdentifier.toString(), ""); |
|
| 796 | + result.hide(); |
|
| 797 | + }); |
|
| 798 | + if (selected) { |
|
| 799 | + // select the race selected for this RaceBoardPanel |
|
| 800 | + result.setDisplayedText(displayName); |
|
| 801 | + } |
|
| 802 | + } |
|
| 803 | + } |
|
| 804 | + } |
|
| 805 | + return result; |
|
| 806 | + } |
|
| 764 | 807 | |
| 765 | 808 | @Override |
| 766 | 809 | public void currentRaceSelected(RaceIdentifier raceIdentifier, RaceColumnDTO raceColumn) { |
| 767 | 810 | if (!currentRaceHasBeenSelectedOnce) { |
| 768 | - FleetDTO fleet = raceColumn.getFleet(raceIdentifier); |
|
| 769 | - String seriesName = raceColumn.getSeriesName(); |
|
| 770 | - if (LeaderboardNameConstants.DEFAULT_SERIES_NAME.equals(seriesName)) { |
|
| 771 | - seriesName = ""; |
|
| 772 | - } |
|
| 773 | - String fleetForRaceName = fleet==null?"":fleet.getName(); |
|
| 774 | - if (fleetForRaceName.equals(LeaderboardNameConstants.DEFAULT_FLEET_NAME)) { |
|
| 775 | - fleetForRaceName = ""; |
|
| 776 | - } else { |
|
| 777 | - fleetForRaceName = (seriesName.isEmpty() ? "" : " - ") + fleetForRaceName; |
|
| 778 | - } |
|
| 811 | + final FleetDTO fleet = raceColumn.getFleet(raceIdentifier); |
|
| 812 | + final Dropdown raceDropDown = createRaceDropDown(raceColumn, fleet); |
|
| 779 | 813 | final Label raceNameLabel = new Label(stringMessages.race() + " " + raceColumn.getRaceColumnName()); |
| 780 | 814 | raceNameLabel.setStyleName("RaceName-Label"); |
| 781 | - final Label raceAdditionalInformationLabel = new Label(seriesName + fleetForRaceName); |
|
| 782 | - raceAdditionalInformationLabel.setStyleName("RaceSeriesAndFleet-Label"); |
|
| 783 | - raceInformationHeader.clear(); |
|
| 784 | - raceInformationHeader.add(raceNameLabel); |
|
| 785 | - raceInformationHeader.add(raceAdditionalInformationLabel); |
|
| 815 | + racePicker.clear(); |
|
| 816 | + racePicker.add(raceDropDown); |
|
| 786 | 817 | final Anchor regattaNameAnchor = new Anchor(raceIdentifier.getRegattaName()); |
| 787 | 818 | regattaNameAnchor.setTitle(raceIdentifier.getRegattaName()); |
| 788 | 819 | if (eventId != null) { |
| 789 | 820 | String link = EntryPointLinkFactory.createRacesTabLink(eventId.toString(), leaderboardName); |
| 790 | 821 | regattaNameAnchor.setHref(link); |
| 791 | 822 | } else { |
| 792 | - String leaderboardGroupNameParam = Window.Location.getParameter("leaderboardGroupName"); |
|
| 823 | + String leaderboardGroupNameParam = getLeaderboardGroupName(); |
|
| 793 | 824 | if (leaderboardGroupNameParam != null) { |
| 794 | 825 | Map<String, String> leaderboardGroupLinkParameters = new HashMap<String, String>(); |
| 795 | 826 | leaderboardGroupLinkParameters.put("showRaceDetails", "true"); |
| ... | ... | @@ -805,16 +836,16 @@ public class RaceBoardPanel |
| 805 | 836 | final Label raceTimeLabel = computeRaceInformation(raceColumn, fleet); |
| 806 | 837 | raceTimeLabel.setStyleName("RaceTime-Label"); |
| 807 | 838 | regattaAndRaceTimeInformationHeader.clear(); |
| 808 | - final FlowPanel helpButtonPanel = new FlowPanel(); |
|
| 839 | + final FlowPanel helpButtonAndRaceTimePanel = new FlowPanel(); |
|
| 840 | + helpButtonAndRaceTimePanel.setStyleName("Help-And-RaceTime"); |
|
| 809 | 841 | final HelpButton helpButton = new HelpButton(HelpButtonResources.INSTANCE, |
| 810 | 842 | stringMessages.videoGuide(), "https://support.sapsailing.com/hc/en-us/articles/7275243525148-Tracking-Race-Player-Overview"); |
| 811 | 843 | if (!DeviceDetector.isMobile()) { |
| 812 | - helpButtonPanel.add(helpButton); |
|
| 813 | - helpButtonPanel.setStyleName("HelpButton"); |
|
| 814 | - regattaAndRaceTimeInformationHeader.add(helpButtonPanel); |
|
| 844 | + helpButtonAndRaceTimePanel.add(helpButton); |
|
| 815 | 845 | } |
| 846 | + helpButtonAndRaceTimePanel.add(raceTimeLabel); |
|
| 816 | 847 | regattaAndRaceTimeInformationHeader.add(regattaNameAnchor); |
| 817 | - regattaAndRaceTimeInformationHeader.add(raceTimeLabel); |
|
| 848 | + regattaAndRaceTimeInformationHeader.add(helpButtonAndRaceTimePanel); |
|
| 818 | 849 | final DataByLogo dataByLogo = new DataByLogo(); |
| 819 | 850 | dataByLogo.setUp(trackingConnectorInfo == null ? Collections.emptySet() |
| 820 | 851 | : Collections.singleton(trackingConnectorInfo), /** colorIfPossible **/ false, /** enforceTextColor **/ true); |
| ... | ... | @@ -827,6 +858,10 @@ public class RaceBoardPanel |
| 827 | 858 | } |
| 828 | 859 | } |
| 829 | 860 | |
| 861 | + private String getLeaderboardGroupName() { |
|
| 862 | + return raceboardContextDefinition == null ? null : raceboardContextDefinition.getLeaderboardGroupName(); |
|
| 863 | + } |
|
| 864 | + |
|
| 830 | 865 | @Override |
| 831 | 866 | public UIObject getXPositionUiObject() { |
| 832 | 867 | return racetimePanel; |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceboardDropdown.gss
| ... | ... | @@ -0,0 +1,130 @@ |
| 1 | +.jsdropdown { |
|
| 2 | + position: relative; |
|
| 3 | +} |
|
| 4 | +.jsdropdown_head, |
|
| 5 | +.jsdropdown_content { |
|
| 6 | + text-indent: 0; |
|
| 7 | + width: 100%; |
|
| 8 | +} |
|
| 9 | +.dropdown_panel { |
|
| 10 | + width: 100%; |
|
| 11 | +} |
|
| 12 | +.dropdown { |
|
| 13 | + font-size: 1rem; |
|
| 14 | + max-width: 100%; |
|
| 15 | + display: inline-block; |
|
| 16 | + text-align: left; |
|
| 17 | + margin-left: -0.666666666666667em; |
|
| 18 | + margin-right: 0.666666666666667em; /* compensate for margin-left, keeping browser from cropping the drop-down button */ |
|
| 19 | +} |
|
| 20 | +.dropdown_head { |
|
| 21 | + display: inline-block; |
|
| 22 | + position: relative; |
|
| 23 | + padding-left: 0.75em; |
|
| 24 | + padding-right: 0.75em; |
|
| 25 | + cursor: pointer; |
|
| 26 | + width: 100%; |
|
| 27 | +} |
|
| 28 | +.compactHeader .dropdown_head { |
|
| 29 | + display: inline-block; |
|
| 30 | + position: relative; |
|
| 31 | + padding-left: 0em; |
|
| 32 | + padding-right: 0.5em; |
|
| 33 | + border-radius: 0.107142857142857em; |
|
| 34 | + border: 1px solid transparent; |
|
| 35 | + cursor: pointer; |
|
| 36 | + width: 100%; |
|
| 37 | +} |
|
| 38 | +.dropdown_head:hover { |
|
| 39 | + background-color: rgba(255, 255, 255, 0.75); |
|
| 40 | +} |
|
| 41 | +.dropdown_head_title { |
|
| 42 | + display: flex; |
|
| 43 | + align-items: center; |
|
| 44 | + font-size: large; |
|
| 45 | + color: #fff; |
|
| 46 | + font-weight: bold; |
|
| 47 | + max-height: 60px; |
|
| 48 | + overflow: hidden; |
|
| 49 | +} |
|
| 50 | +.compactHeader .dropdown_head_title { |
|
| 51 | + font-size: small; |
|
| 52 | + color: #fff; |
|
| 53 | + font-weight: normal; |
|
| 54 | +} |
|
| 55 | +.dropdown_head_title_button { |
|
| 56 | + display: inline-block; |
|
| 57 | + height: 0.285714285714286em; |
|
| 58 | + width: 0.428571428571429em; |
|
| 59 | + background-image: resourceUrl("arrowDownFilledWhite"); |
|
| 60 | + background-position: center bottom; |
|
| 61 | + background-repeat: no-repeat; |
|
| 62 | + background-size: 100%; |
|
| 63 | + float: none; |
|
| 64 | + margin-left: 5px; |
|
| 65 | + flex-shrink: 0; |
|
| 66 | +} |
|
| 67 | +.compactHeader .dropdown_head_title_button { |
|
| 68 | + height: 0.5em; |
|
| 69 | + width: 0.7em; |
|
| 70 | +} |
|
| 71 | +.dropdown_content { |
|
| 72 | + position: absolute; |
|
| 73 | + z-index: 1; |
|
| 74 | + display: none; |
|
| 75 | + height: auto; |
|
| 76 | + max-height: 23.333333333333332em; |
|
| 77 | + overflow: auto; |
|
| 78 | + width: auto; |
|
| 79 | + background-color: #fff; |
|
| 80 | + border: 1px solid #ccc; |
|
| 81 | + box-shadow: 0.133333333333333em 0.133333333333333em 0.333333333333333em #ccc; |
|
| 82 | +} |
|
| 83 | +.dropdownregattas_content { |
|
| 84 | +} |
|
| 85 | +.jsdropdownactive .dropdown_content { |
|
| 86 | + display: block; |
|
| 87 | +} |
|
| 88 | +.dropdown_content_link { |
|
| 89 | + display: block; |
|
| 90 | + cursor: pointer; |
|
| 91 | + padding: 1.333333333333333em 3.333333333333333em 1em 1em; |
|
| 92 | + border-bottom: 1px solid #f4f4f4; |
|
| 93 | + position: relative; |
|
| 94 | + border: none; |
|
| 95 | +} |
|
| 96 | +.dropdown_content_link:hover, |
|
| 97 | +.dropdown_content_link:focus { |
|
| 98 | + background: #f8f8f8; |
|
| 99 | + text-decoration: none; |
|
| 100 | +} |
|
| 101 | +.dropdown_content_link:hover .dropdown_content_link_title, |
|
| 102 | +.dropdown_content_link:focus .dropdown_content_link_title { |
|
| 103 | + text-decoration: underline; |
|
| 104 | +} |
|
| 105 | +.dropdown_content_link_title { |
|
| 106 | + display: block; |
|
| 107 | + font-weight: 600; |
|
| 108 | + font-size: 1.2em; |
|
| 109 | + color: #666; |
|
| 110 | +} |
|
| 111 | +.dropdown_content_link_subtitle { |
|
| 112 | + color: #cfcfcf; |
|
| 113 | + font-size: 1.066666666666667em; |
|
| 114 | +} |
|
| 115 | +.dropdown_content_linkactive:after { |
|
| 116 | + content: ''; |
|
| 117 | + position: absolute; |
|
| 118 | + right: 1.333333333333333em; |
|
| 119 | + top: 50%; |
|
| 120 | + height: 0.933333333333333em; |
|
| 121 | + width: 0.933333333333333em; |
|
| 122 | + background-image: resourceUrl("dropdownCheck"); |
|
| 123 | + background-repeat: no-repeat; |
|
| 124 | + background-position: right center; |
|
| 125 | + background-size: contain; |
|
| 126 | + -webkit-transform: translate(0, -50%); |
|
| 127 | + -moz-transform: translate(0, -50%); |
|
| 128 | + -ms-transform: translate(0, -50%); |
|
| 129 | + transform: translate(0, -50%); |
|
| 130 | +} |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceboardDropdownResources.java
| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | +package com.sap.sailing.gwt.ui.raceboard; |
|
| 2 | + |
|
| 3 | +import com.google.gwt.core.client.GWT; |
|
| 4 | +import com.sap.sse.gwt.client.controls.dropdown.DropdownResources; |
|
| 5 | + |
|
| 6 | +public interface RaceboardDropdownResources extends DropdownResources { |
|
| 7 | + static final RaceboardDropdownResources INSTANCE = GWT.create(RaceboardDropdownResources.class); |
|
| 8 | + |
|
| 9 | + @Source("RaceboardDropdown.gss") |
|
| 10 | + @Override |
|
| 11 | + LocalCss css(); |
|
| 12 | + |
|
| 13 | + public interface LocalCss extends DropdownResources.LocalCss { |
|
| 14 | + String compactHeader(); |
|
| 15 | + } |
|
| 16 | +} |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/databylogo/DataByLogo.gss
| ... | ... | @@ -1,30 +1,46 @@ |
| 1 | 1 | img.databylogo_logo { |
| 2 | - height: 1.3333333em !important; |
|
| 3 | - vertical-align: middle; |
|
| 4 | - display: inline-block; |
|
| 2 | + height: 1.3333333em !important; |
|
| 3 | + vertical-align: middle; |
|
| 4 | + display: inline-block; |
|
| 5 | + overflow: hidden; |
|
| 6 | + width: auto; |
|
| 7 | + min-width: 0; |
|
| 8 | + flex-shrink: 0; |
|
| 5 | 9 | } |
| 6 | 10 | |
| 7 | 11 | .databylogo_text { |
| 8 | - vertical-align: middle; |
|
| 12 | + vertical-align: middle; |
|
| 13 | + overflow: hidden; |
|
| 14 | + text-overflow: ellipsis; |
|
| 15 | + white-space: nowrap; |
|
| 16 | + width: auto; |
|
| 17 | + min-width: 0; |
|
| 18 | + direction: rtl; |
|
| 9 | 19 | } |
| 10 | 20 | |
| 11 | 21 | .databylogo_white_text { |
| 12 | - color: white; |
|
| 22 | + color: white; |
|
| 13 | 23 | } |
| 14 | 24 | |
| 15 | 25 | .databylogo_black_text { |
| 16 | - color: black; |
|
| 26 | + color: black; |
|
| 17 | 27 | } |
| 18 | 28 | |
| 19 | 29 | .databylogo_subtlelink, .databylogo_subtlelink:hover, |
| 20 | - .databylogo_subtlelink:active, .databylogo_subtlelink:visited, |
|
| 21 | - .databylogo_subtlelink:focus { |
|
| 22 | - text-decoration: none; |
|
| 30 | + .databylogo_subtlelink:active, .databylogo_subtlelink:visited, |
|
| 31 | + .databylogo_subtlelink:focus { |
|
| 32 | + text-decoration: none; |
|
| 23 | 33 | } |
| 24 | 34 | |
| 25 | 35 | .databylogo_container { |
| 26 | - display: inline-block; |
|
| 27 | - white-space: nowrap; |
|
| 28 | - font-size: 0.75em; |
|
| 29 | - cursor: pointer; |
|
| 36 | + display: flex; |
|
| 37 | + white-space: nowrap; |
|
| 38 | + font-size: 0.75em; |
|
| 39 | + cursor: pointer; |
|
| 40 | + align-content: flex-end; |
|
| 41 | + flex-wrap: nowrap; |
|
| 42 | + justify-content: flex-end; |
|
| 43 | + align-items: center; |
|
| 44 | + flex-direction: row; |
|
| 45 | + width: 100%; |
|
| 30 | 46 | } |
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/LeaderboardGroupOverviewPanel.java
| ... | ... | @@ -45,6 +45,7 @@ import com.sap.sailing.gwt.settings.client.spectator.SpectatorSettings; |
| 45 | 45 | import com.sap.sailing.gwt.ui.adminconsole.LeaderboardConfigPanel.AnchorCell; |
| 46 | 46 | import com.sap.sailing.gwt.ui.client.SailingServiceAsync; |
| 47 | 47 | import com.sap.sailing.gwt.ui.client.StringMessages; |
| 48 | +import com.sap.sailing.gwt.ui.raceboard.RaceBoardPanel; |
|
| 48 | 49 | import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO; |
| 49 | 50 | import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; |
| 50 | 51 | import com.sap.sse.common.Util; |
| ... | ... | @@ -454,7 +455,7 @@ public class LeaderboardGroupOverviewPanel extends FormPanel { |
| 454 | 455 | StrippedLeaderboardDTO selectedLeaderboard = leaderboardsSelectionModel.getSelectedObject(); |
| 455 | 456 | RegattaNameAndRaceName raceId = (RegattaNameAndRaceName) race.getRaceIdentifier(fleet); |
| 456 | 457 | String debugParam = Window.Location.getParameter("gwt.codesvr"); |
| 457 | - String link = URLEncoder.encode("/gwt/RaceBoard.html?leaderboardName=" |
|
| 458 | + String link = URLEncoder.encode(RaceBoardPanel.RACEBOARD_PATH+"?leaderboardName=" |
|
| 458 | 459 | + selectedLeaderboard.getName() + "&raceName=" + raceId.getRaceName() + "®attaName=" |
| 459 | 460 | + raceId.getRegattaName() + "&leaderboardGroupId=" + selectedGroup.getId().toString() |
| 460 | 461 | + "&root=overview" |
java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/Leaderboard.gwt.xml
| ... | ... | @@ -17,22 +17,23 @@ |
| 17 | 17 | <inherits name="com.sap.sse.SSECommon" /> |
| 18 | 18 | <inherits name="com.sap.sailing.domain.SailingDomain" /> |
| 19 | 19 | <inherits name="com.sap.sse.gwt.AdminConsole" /> |
| 20 | - <inherits name="com.sap.sailing.landscape.Landscape" /> |
|
| 20 | + <inherits name="com.sap.sailing.landscape.Landscape" /> |
|
| 21 | 21 | <inherits name="com.sap.sse.security.ui.LoginPanel" /> |
| 22 | 22 | |
| 23 | 23 | <!-- Specify the app entry point class. --> |
| 24 | 24 | <entry-point class='com.sap.sailing.gwt.ui.leaderboard.LeaderboardEntryPoint' /> |
| 25 | 25 | |
| 26 | - <!-- module containing locale configuration --> |
|
| 27 | - <inherits name='com.sap.sailing.gwt.common.SailingLocalesSinglePermutation' /> |
|
| 26 | + <!-- module containing locale configuration --> |
|
| 27 | + <inherits name='com.sap.sailing.gwt.common.SailingLocalesSinglePermutation' /> |
|
| 28 | 28 | |
| 29 | 29 | <inherits name='com.sap.sailing.gwt.ui.SourceLeaderboard' /> |
| 30 | 30 | <inherits name='com.sap.sailing.gwt.ui.SourceCommon' /> |
| 31 | 31 | <inherits name='com.sap.sailing.gwt.ui.SourceClient' /> |
| 32 | 32 | <inherits name='com.sap.sailing.gwt.ui.SourceShared' /> |
| 33 | + <inherits name='com.sap.sailing.gwt.ui.SourceRaceboard' /> |
|
| 33 | 34 | <inherits name="com.sap.sailing.gwt.ui.SourceLeaderboard" /> |
| 34 | 35 | <inherits name="com.sap.sailing.gwt.ui.SourceAdminConsole" /> |
| 35 | - <inherits name='com.sap.sailing.gwt.settings.Settings'/> |
|
| 36 | + <inherits name='com.sap.sailing.gwt.settings.Settings'/> |
|
| 36 | 37 | |
| 37 | 38 | <inherits name='com.sap.sse.gwt.CommonControlCSS_Autoinject'/> |
| 38 | 39 | <inherits name='com.sap.sailing.gwt.common.AuthenticationCommon'/> |
java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/LeaderboardEditing.gwt.xml
| ... | ... | @@ -14,11 +14,12 @@ |
| 14 | 14 | <inherits name="com.sap.sse.SSECommon" /> |
| 15 | 15 | <inherits name="com.sap.sailing.domain.SailingDomain" /> |
| 16 | 16 | <inherits name="com.sap.sse.gwt.AdminConsole" /> |
| 17 | - <inherits name="com.sap.sailing.landscape.Landscape" /> |
|
| 17 | + <inherits name="com.sap.sailing.landscape.Landscape" /> |
|
| 18 | 18 | <inherits name="com.sap.sse.security.ui.LoginPanel" /> |
| 19 | 19 | <inherits name="com.sap.sse.security.ui.Settings" /> |
| 20 | - <inherits name='com.sap.sailing.gwt.settings.Settings'/> |
|
| 21 | - <inherits name="com.sap.sailing.ExpeditionConnectorCommon" /> |
|
| 20 | + <inherits name='com.sap.sailing.gwt.ui.SourceRaceboard' /> |
|
| 21 | + <inherits name='com.sap.sailing.gwt.settings.Settings'/> |
|
| 22 | + <inherits name="com.sap.sailing.ExpeditionConnectorCommon" /> |
|
| 22 | 23 | |
| 23 | 24 | <!-- Highcharts API --> |
| 24 | 25 | <inherits name="com.sap.sse.gwt.Highcharts_Autoinject" /> |
java/com.sap.sailing.landscape.common/META-INF/MANIFEST.MF
| ... | ... | @@ -11,3 +11,4 @@ Require-Bundle: com.sap.sse.security.common, |
| 11 | 11 | com.sap.sse.landscape.aws.common |
| 12 | 12 | Bundle-ActivationPolicy: lazy |
| 13 | 13 | Export-Package: com.sap.sailing.landscape.common |
| 14 | +Import-Package: software.amazon.awssdk.regions |
java/com.sap.sailing.landscape.common/src/com/sap/sailing/landscape/common/SharedLandscapeConstants.java
| ... | ... | @@ -20,6 +20,15 @@ public interface SharedLandscapeConstants { |
| 20 | 20 | */ |
| 21 | 21 | String DEFAULT_SECURITY_SERVICE_REPLICA_SET_NAME = "security-service"; |
| 22 | 22 | |
| 23 | + String RABBIT_IN_DEFAULT_REGION_HOSTNAME = "rabbit.internal.sapsailing.com"; |
|
| 24 | + |
|
| 25 | + String DEFAULT_REGION = "eu-west-1"; |
|
| 26 | + |
|
| 27 | + /** |
|
| 28 | + * We maintain a DNS entry for "rabbit.internal.sapsailing.com" (see {@link #RABBIT_IN_DEFAULT_REGION_HOSTNAME}) in this region |
|
| 29 | + */ |
|
| 30 | + String REGION_WITH_RABBITMQ_DNS_HOSTNAME = DEFAULT_REGION; |
|
| 31 | + |
|
| 23 | 32 | /** |
| 24 | 33 | * This is the region of the load balancer handling the default traffic for {@code *.sapsailing.com}. It is also |
| 25 | 34 | * called the "dynamic" load balancer because adding, removing or changing any hostname-based rule in its HTTPS |
| ... | ... | @@ -38,8 +47,14 @@ public interface SharedLandscapeConstants { |
| 38 | 47 | * for archived events. If such a state is reached, "dynamic" load balancing may potentially be used regardless |
| 39 | 48 | * the region. |
| 40 | 49 | */ |
| 41 | - String REGION_WITH_DEFAULT_LOAD_BALANCER = "eu-west-1"; |
|
| 42 | - |
|
| 50 | + String REGION_WITH_DEFAULT_LOAD_BALANCER = DEFAULT_REGION; |
|
| 51 | + |
|
| 52 | + /** |
|
| 53 | + * Tag name used to identify instances on which a RabbitMQ installation is running. The tag value is currently interpreted to |
|
| 54 | + * be the port number (usually 5672) on which the RabbitMQ endpoint can be reached. |
|
| 55 | + */ |
|
| 56 | + String RABBITMQ_TAG_NAME = "RabbitMQEndpoint"; |
|
| 57 | + |
|
| 43 | 58 | /** |
| 44 | 59 | * The tag value used to identify host images that can be launched in order to run one or more Sailing Analytics |
| 45 | 60 | * server processes on it. |
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/i18n/StringMessages.properties
| ... | ... | @@ -75,7 +75,7 @@ reallyRemoveApplicationReplicaSet=Really remove application replica set {0} with |
| 75 | 75 | pleaseSelectSshKeyPair=Please select an SSH key pair |
| 76 | 76 | defineLandingPage=Define landing page |
| 77 | 77 | successfullyUpdatedLandingPage=Successfully updated landing page |
| 78 | -defineDefaultRedirect=Default Default Redirect |
|
| 78 | +defineDefaultRedirect=Default Redirect |
|
| 79 | 79 | defineDefaultRedirectMessage=Defines where visitors of the default path "/" will be redirected |
| 80 | 80 | redirectPlain=Plain landing page (index.html) |
| 81 | 81 | redirectHome=Server''s Home.html page with all events |
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/i18n/StringMessages_cs.properties
| ... | ... | @@ -75,7 +75,7 @@ reallyRemoveApplicationReplicaSet=Chcete opravdu odebrat sadu replik aplikace {0 |
| 75 | 75 | pleaseSelectSshKeyPair=Vyberte pár klíčů SSH |
| 76 | 76 | defineLandingPage=Definovat cílovou stránku |
| 77 | 77 | successfullyUpdatedLandingPage=Aktualizace cílové stránky byla úspěšná. |
| 78 | -defineDefaultRedirect=Výchozí přesměrování výchozí cesty |
|
| 78 | +defineDefaultRedirect=Přesměrování výchozí cesty |
|
| 79 | 79 | defineDefaultRedirectMessage=Definuje, kam budou přesměrováni návštěvníci výchozí cesty „/“. |
| 80 | 80 | redirectPlain=Prostá cílová stránka (index.html) |
| 81 | 81 | redirectHome=Home.html stránka serveru se všemu událostmi |
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/i18n/StringMessages_es.properties
| ... | ... | @@ -75,7 +75,7 @@ reallyRemoveApplicationReplicaSet=¿Desea eliminar el conjunto de réplica de ap |
| 75 | 75 | pleaseSelectSshKeyPair=Seleccione un par clave SSH |
| 76 | 76 | defineLandingPage=Definir página de llegada |
| 77 | 77 | successfullyUpdatedLandingPage=Página de llegada actualizada correctamente |
| 78 | -defineDefaultRedirect=Predeterminar redireccionamiento predeterminado |
|
| 78 | +defineDefaultRedirect=Redireccionamiento predeterminado |
|
| 79 | 79 | defineDefaultRedirectMessage=Define a dónde se redireccionarán los visitantes de la vía de acceso predeterminada "/" |
| 80 | 80 | redirectPlain=Página de llegada sin formato (index.html) |
| 81 | 81 | redirectHome=Página del servidor Home.html con todos los eventos |
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/i18n/StringMessages_fr.properties
| ... | ... | @@ -75,7 +75,7 @@ reallyRemoveApplicationReplicaSet=Voulez-vous vraiment supprimer l''ensemble de |
| 75 | 75 | pleaseSelectSshKeyPair=Veuillez sélectionner une paire de clés SSH. |
| 76 | 76 | defineLandingPage=Définir la page d''arrivée |
| 77 | 77 | successfullyUpdatedLandingPage=Page d''arrivée correctement mise à jour |
| 78 | -defineDefaultRedirect=Définir la redirection par défaut |
|
| 78 | +defineDefaultRedirect=Redirection par défaut |
|
| 79 | 79 | defineDefaultRedirectMessage=Définit où les visiteur.e.s du chemin d''accès par défaut "/" seront redirigé.e.s. |
| 80 | 80 | redirectPlain=Page d''arrivée simple (index.html) |
| 81 | 81 | redirectHome=Page home.html du serveur avec toutes les manifestations |
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/i18n/StringMessages_it.properties
| ... | ... | @@ -75,7 +75,7 @@ reallyRemoveApplicationReplicaSet=Confermare la rimozione del set di replica app |
| 75 | 75 | pleaseSelectSshKeyPair=Selezionare una coppia di chiavi SSH |
| 76 | 76 | defineLandingPage=Definisci la landing page |
| 77 | 77 | successfullyUpdatedLandingPage=Landing page aggiornata correttamente |
| 78 | -defineDefaultRedirect=Imposta reindirizzamento predefinito |
|
| 78 | +defineDefaultRedirect=Reindirizzamento predefinito |
|
| 79 | 79 | defineDefaultRedirectMessage=Stabilisce dove verranno reindirizzati i visitatori del percorso predefinito "/" |
| 80 | 80 | redirectPlain=Landing page semplice (index.html) |
| 81 | 81 | redirectHome=Pagina home.html del server con tutti gli eventi |
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/i18n/StringMessages_pt.properties
| ... | ... | @@ -75,7 +75,7 @@ reallyRemoveApplicationReplicaSet=Remover conjunto de réplicas de aplicação { |
| 75 | 75 | pleaseSelectSshKeyPair=Selecione um par de chaves SSH |
| 76 | 76 | defineLandingPage=Definir página de chegada |
| 77 | 77 | successfullyUpdatedLandingPage=Página de chegada atualizada com êxito |
| 78 | -defineDefaultRedirect=Predefinir redirecionamento padrão |
|
| 78 | +defineDefaultRedirect=Redirecionamento padrão |
|
| 79 | 79 | defineDefaultRedirectMessage=Define para onde serão direcionados os visitantes do caminho padrão "/" |
| 80 | 80 | redirectPlain=Página de chegada simples (index.html) |
| 81 | 81 | redirectHome=Página Home.html do servidor com todos os eventos |
java/com.sap.sailing.landscape.ui/src/com/sap/sailing/landscape/ui/client/i18n/StringMessages_zh.properties
| ... | ... | @@ -75,7 +75,7 @@ reallyRemoveApplicationReplicaSet=是否确定要移除具有所有入口规则 |
| 75 | 75 | pleaseSelectSshKeyPair=请选择 SSH 密钥对 |
| 76 | 76 | defineLandingPage=定义登录页面 |
| 77 | 77 | successfullyUpdatedLandingPage=已成功更新登录页面 |
| 78 | -defineDefaultRedirect=定义默认重定向 |
|
| 78 | +defineDefaultRedirect=默认重定向 |
|
| 79 | 79 | defineDefaultRedirectMessage=定义将重定向默认路径 "/" 的访客的位置 |
| 80 | 80 | redirectPlain=普通登录页面 (index.html) |
| 81 | 81 | redirectHome=服务器的 Home.html 页面,包含所有活动 |
java/com.sap.sailing.server.interface/META-INF/MANIFEST.MF
| ... | ... | @@ -5,6 +5,7 @@ Bundle-SymbolicName: com.sap.sailing.server.interface |
| 5 | 5 | Bundle-Version: 1.0.0.qualifier |
| 6 | 6 | Bundle-Vendor: SAP |
| 7 | 7 | Automatic-Module-Name: com.sap.sailing.server.interface |
| 8 | +Bundle-ActivationPolicy: lazy |
|
| 8 | 9 | Bundle-RequiredExecutionEnvironment: JavaSE-1.8 |
| 9 | 10 | Require-Bundle: com.sap.sailing.domain;bundle-version="1.0.0", |
| 10 | 11 | com.sap.sse.common;bundle-version="1.0.0", |
java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/TrailingSlashInEventBaseNameTest.java
| ... | ... | @@ -0,0 +1,60 @@ |
| 1 | +package com.sap.sailing.server.test; |
|
| 2 | + |
|
| 3 | +import static org.junit.Assert.assertFalse; |
|
| 4 | +import static org.junit.Assert.assertTrue; |
|
| 5 | +import static org.mockito.Mockito.mock; |
|
| 6 | +import static org.mockito.Mockito.when; |
|
| 7 | + |
|
| 8 | +import java.net.MalformedURLException; |
|
| 9 | +import java.net.URL; |
|
| 10 | +import java.util.Locale; |
|
| 11 | +import java.util.UUID; |
|
| 12 | + |
|
| 13 | +import org.junit.Before; |
|
| 14 | +import org.junit.Test; |
|
| 15 | + |
|
| 16 | +import com.sap.sailing.domain.base.Event; |
|
| 17 | +import com.sap.sailing.domain.common.RegattaNameAndRaceName; |
|
| 18 | +import com.sap.sailing.domain.leaderboard.Leaderboard; |
|
| 19 | +import com.sap.sailing.domain.leaderboard.LeaderboardGroup; |
|
| 20 | +import com.sap.sailing.domain.tracking.TrackedRace; |
|
| 21 | +import com.sap.sailing.server.util.RaceBoardLinkFactory; |
|
| 22 | + |
|
| 23 | +public class TrailingSlashInEventBaseNameTest { |
|
| 24 | + private TrackedRace trackedRace; |
|
| 25 | + private Leaderboard leaderboard; |
|
| 26 | + private Event event; |
|
| 27 | + private LeaderboardGroup leaderboardGroup; |
|
| 28 | + private static final String raceboardMode = "PLAYER"; |
|
| 29 | + private static final String eventBaseUrlWithoutTrailingSlash = "https://myevent.example.com"; |
|
| 30 | + |
|
| 31 | + @Before |
|
| 32 | + public void setUp() throws MalformedURLException { |
|
| 33 | + trackedRace = mock(TrackedRace.class); |
|
| 34 | + when(trackedRace.getRaceIdentifier()).thenReturn(new RegattaNameAndRaceName("Regatta with spaces", "Race with spaces")); |
|
| 35 | + leaderboard = mock(Leaderboard.class); |
|
| 36 | + when(leaderboard.getName()).thenReturn("Leaderboard"); |
|
| 37 | + event = mock(Event.class); |
|
| 38 | + when(event.getId()).thenReturn(UUID.randomUUID()); |
|
| 39 | + leaderboardGroup = mock(LeaderboardGroup.class); |
|
| 40 | + when(leaderboardGroup.getId()).thenReturn(UUID.randomUUID()); |
|
| 41 | + } |
|
| 42 | + |
|
| 43 | + @Test |
|
| 44 | + public void testTrailingSlashInEventBaseNameCausesNoHarmInRaceBoardLinks() throws MalformedURLException { |
|
| 45 | + when(event.getBaseURL()).thenReturn(new URL(eventBaseUrlWithoutTrailingSlash+"/")); |
|
| 46 | + final String link = RaceBoardLinkFactory.createRaceBoardLink(trackedRace, leaderboard, event, leaderboardGroup, raceboardMode, Locale.US); |
|
| 47 | + assertTrue(link.contains(eventBaseUrlWithoutTrailingSlash+"/")); |
|
| 48 | + assertFalse(link.contains(eventBaseUrlWithoutTrailingSlash+"//")); |
|
| 49 | + assertFalse(link.contains(" ")); |
|
| 50 | + } |
|
| 51 | + |
|
| 52 | + @Test |
|
| 53 | + public void testNoTrailingSlashInEventBaseNameCausesNoHarmInRaceBoardLinks() throws MalformedURLException { |
|
| 54 | + when(event.getBaseURL()).thenReturn(new URL(eventBaseUrlWithoutTrailingSlash)); |
|
| 55 | + final String link = RaceBoardLinkFactory.createRaceBoardLink(trackedRace, leaderboard, event, leaderboardGroup, raceboardMode, Locale.US); |
|
| 56 | + assertTrue(link.contains(eventBaseUrlWithoutTrailingSlash+"/")); |
|
| 57 | + assertFalse(link.contains(eventBaseUrlWithoutTrailingSlash+"//")); |
|
| 58 | + assertFalse(link.contains(" ")); |
|
| 59 | + } |
|
| 60 | +} |
java/com.sap.sailing.server/src/com/sap/sailing/server/util/RaceBoardLinkFactory.java
| ... | ... | @@ -1,7 +1,9 @@ |
| 1 | 1 | package com.sap.sailing.server.util; |
| 2 | 2 | |
| 3 | +import java.io.UnsupportedEncodingException; |
|
| 3 | 4 | import java.net.MalformedURLException; |
| 4 | 5 | import java.net.URL; |
| 6 | +import java.net.URLEncoder; |
|
| 5 | 7 | import java.util.Locale; |
| 6 | 8 | |
| 7 | 9 | import com.sap.sailing.domain.base.Event; |
| ... | ... | @@ -38,12 +40,20 @@ public class RaceBoardLinkFactory { |
| 38 | 40 | public static String createRaceBoardLink(TrackedRace trackedRace, Leaderboard leaderboard, Event event, |
| 39 | 41 | LeaderboardGroup leaderboardGroup, String raceboardMode, Locale locale) { |
| 40 | 42 | RegattaAndRaceIdentifier raceIdentifier = trackedRace.getRaceIdentifier(); |
| 41 | - String link = getBaseURL(event).toString() + "/gwt/RaceBoard.html?" |
|
| 42 | - + (locale == null ? "" : (locale.toLanguageTag()+"&")) |
|
| 43 | - + "eventId=" + event.getId() + "&leaderboardName=" + leaderboard.getName() |
|
| 44 | - + "&leaderboardGroupId=" + leaderboardGroup.getId().toString() +"&raceName=" |
|
| 45 | - + raceIdentifier.getRaceName() + "&showMapControls=true®attaName=" |
|
| 46 | - + raceIdentifier.getRegattaName() + "&mode=" + raceboardMode; |
|
| 43 | + String link; |
|
| 44 | + try { |
|
| 45 | + final String eventBaseUrl = getBaseURL(event).toString(); |
|
| 46 | + link = eventBaseUrl |
|
| 47 | + + (eventBaseUrl.endsWith("/") ? "" : "/") |
|
| 48 | + + "gwt/RaceBoard.html?" |
|
| 49 | + + (locale == null ? "" : (locale.toLanguageTag()+"&")) |
|
| 50 | + + "eventId=" + event.getId() + "&leaderboardName=" + leaderboard.getName() |
|
| 51 | + + (leaderboardGroup != null ? ("&leaderboardGroupId=" + leaderboardGroup.getId().toString()) : "") |
|
| 52 | + +"&raceName=" + URLEncoder.encode(raceIdentifier.getRaceName(), "UTF-8") + "&showMapControls=true®attaName=" |
|
| 53 | + + URLEncoder.encode(raceIdentifier.getRegattaName(), "UTF-8") + "&mode=" + raceboardMode; |
|
| 54 | + } catch (UnsupportedEncodingException e) { |
|
| 55 | + throw new RuntimeException("Internal error: charset UTF-8 unknown"); |
|
| 56 | + } |
|
| 47 | 57 | return link; |
| 48 | 58 | } |
| 49 | 59 | } |
java/com.sap.sailing.targetplatform.base/features/target-base/feature.xml
| ... | ... | @@ -107,42 +107,42 @@ |
| 107 | 107 | id="org.mongodb.bson" |
| 108 | 108 | download-size="0" |
| 109 | 109 | install-size="0" |
| 110 | - version="4.5.1" |
|
| 110 | + version="4.11.1" |
|
| 111 | 111 | unpack="false"/> |
| 112 | 112 | |
| 113 | 113 | <plugin |
| 114 | 114 | id="org.mongodb.bson.source" |
| 115 | 115 | download-size="0" |
| 116 | 116 | install-size="0" |
| 117 | - version="4.5.1" |
|
| 117 | + version="4.11.1" |
|
| 118 | 118 | unpack="false"/> |
| 119 | 119 | |
| 120 | 120 | <plugin |
| 121 | 121 | id="org.mongodb.driver-core" |
| 122 | 122 | download-size="0" |
| 123 | 123 | install-size="0" |
| 124 | - version="4.5.1" |
|
| 124 | + version="4.11.1" |
|
| 125 | 125 | unpack="false"/> |
| 126 | 126 | |
| 127 | 127 | <plugin |
| 128 | 128 | id="org.mongodb.driver-core.source" |
| 129 | 129 | download-size="0" |
| 130 | 130 | install-size="0" |
| 131 | - version="4.5.1" |
|
| 131 | + version="4.11.1" |
|
| 132 | 132 | unpack="false"/> |
| 133 | 133 | |
| 134 | 134 | <plugin |
| 135 | 135 | id="org.mongodb.driver-sync" |
| 136 | 136 | download-size="0" |
| 137 | 137 | install-size="0" |
| 138 | - version="4.5.1" |
|
| 138 | + version="4.11.1" |
|
| 139 | 139 | unpack="false"/> |
| 140 | 140 | |
| 141 | 141 | <plugin |
| 142 | 142 | id="org.mongodb.driver-sync.source" |
| 143 | 143 | download-size="0" |
| 144 | 144 | install-size="0" |
| 145 | - version="4.5.1" |
|
| 145 | + version="4.11.1" |
|
| 146 | 146 | unpack="false"/> |
| 147 | 147 | |
| 148 | 148 | <plugin |
java/com.sap.sailing.targetplatform.base/plugins/target-base/bson-4.11.1-sources.jar
| ... | ... | Binary files /dev/null and b/java/com.sap.sailing.targetplatform.base/plugins/target-base/bson-4.11.1-sources.jar differ |
java/com.sap.sailing.targetplatform.base/plugins/target-base/bson-4.11.1.jar
| ... | ... | Binary files /dev/null and b/java/com.sap.sailing.targetplatform.base/plugins/target-base/bson-4.11.1.jar differ |
java/com.sap.sailing.targetplatform.base/plugins/target-base/bson-4.5.1-sources.jar
| ... | ... | Binary files a/java/com.sap.sailing.targetplatform.base/plugins/target-base/bson-4.5.1-sources.jar and /dev/null differ |
java/com.sap.sailing.targetplatform.base/plugins/target-base/bson-4.5.1.jar
| ... | ... | Binary files a/java/com.sap.sailing.targetplatform.base/plugins/target-base/bson-4.5.1.jar and /dev/null differ |
java/com.sap.sailing.targetplatform.base/plugins/target-base/mongodb-driver-core-4.11.1-sources.jar
| ... | ... | Binary files /dev/null and b/java/com.sap.sailing.targetplatform.base/plugins/target-base/mongodb-driver-core-4.11.1-sources.jar differ |
java/com.sap.sailing.targetplatform.base/plugins/target-base/mongodb-driver-core-4.11.1.jar
| ... | ... | Binary files /dev/null and b/java/com.sap.sailing.targetplatform.base/plugins/target-base/mongodb-driver-core-4.11.1.jar differ |
java/com.sap.sailing.targetplatform.base/plugins/target-base/mongodb-driver-core-4.5.1-sources.jar
| ... | ... | Binary files a/java/com.sap.sailing.targetplatform.base/plugins/target-base/mongodb-driver-core-4.5.1-sources.jar and /dev/null differ |
java/com.sap.sailing.targetplatform.base/plugins/target-base/mongodb-driver-core-4.5.1.jar
| ... | ... | Binary files a/java/com.sap.sailing.targetplatform.base/plugins/target-base/mongodb-driver-core-4.5.1.jar and /dev/null differ |
java/com.sap.sailing.targetplatform.base/plugins/target-base/mongodb-driver-sync-4.11.1-sources.jar
| ... | ... | Binary files /dev/null and b/java/com.sap.sailing.targetplatform.base/plugins/target-base/mongodb-driver-sync-4.11.1-sources.jar differ |
java/com.sap.sailing.targetplatform.base/plugins/target-base/mongodb-driver-sync-4.11.1.jar
| ... | ... | Binary files /dev/null and b/java/com.sap.sailing.targetplatform.base/plugins/target-base/mongodb-driver-sync-4.11.1.jar differ |
java/com.sap.sailing.targetplatform.base/plugins/target-base/mongodb-driver-sync-4.5.1-sources.jar
| ... | ... | Binary files a/java/com.sap.sailing.targetplatform.base/plugins/target-base/mongodb-driver-sync-4.5.1-sources.jar and /dev/null differ |
java/com.sap.sailing.targetplatform.base/plugins/target-base/mongodb-driver-sync-4.5.1.jar
| ... | ... | Binary files a/java/com.sap.sailing.targetplatform.base/plugins/target-base/mongodb-driver-sync-4.5.1.jar and /dev/null differ |
java/com.sap.sailing.targetplatform.base/pom.xml
| ... | ... | @@ -1,42 +1,42 @@ |
| 1 | 1 | <?xml version="1.0" encoding="UTF-8"?> |
| 2 | 2 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001 XMLSchema-instance" |
| 3 | - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> |
|
| 4 | - <modelVersion>4.0.0</modelVersion> |
|
| 3 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> |
|
| 4 | + <modelVersion>4.0.0</modelVersion> |
|
| 5 | 5 | |
| 6 | - <!-- Do not set parent: this would also inherit the tycho target platform |
|
| 7 | - configuration. By building this artifact, we create the local p2 repo that |
|
| 8 | - can then be referenced by the race-analysis-p2-local.target definition. So |
|
| 9 | - we can't recursively use the local repo as target platform for building the |
|
| 10 | - local repo - and apart from that don't need it. Skipping the tycho-maven-plugin |
|
| 11 | - and target-platform-configuration plugins via <skip> parameter in the build |
|
| 12 | - config does not skip resolving the target definition defined in the parent |
|
| 13 | - pom. Therefore, the parent was not set here - which means we have to define |
|
| 14 | - $tycho-version again. --> |
|
| 15 | - <properties> |
|
| 16 | - <tycho-version>1.6.0</tycho-version> |
|
| 17 | - </properties> |
|
| 18 | - <groupId>com.sap.sailing</groupId> |
|
| 19 | - <version>1.0.0-SNAPSHOT</version> |
|
| 20 | - <artifactId>com.sap.sailing.targetplatform.base</artifactId> |
|
| 21 | - <build> |
|
| 22 | - <plugins> |
|
| 23 | - <plugin> |
|
| 24 | - <groupId>org.eclipse.tycho.extras</groupId> |
|
| 25 | - <artifactId>tycho-p2-extras-plugin</artifactId> |
|
| 26 | - <version>${tycho-version}</version> |
|
| 27 | - <configuration> |
|
| 28 | - <sourceLocation>${basedir}</sourceLocation> |
|
| 29 | - </configuration> |
|
| 30 | - <executions> |
|
| 31 | - <execution> |
|
| 32 | - <id>generate-full-metadata</id> |
|
| 33 | - <phase>compile</phase> |
|
| 34 | - <goals> |
|
| 35 | - <goal>publish-features-and-bundles</goal> |
|
| 36 | - </goals> |
|
| 37 | - </execution> |
|
| 38 | - </executions> |
|
| 39 | - </plugin> |
|
| 40 | - </plugins> |
|
| 41 | - </build> |
|
| 6 | + <!-- Do not set parent: this would also inherit the tycho target platform |
|
| 7 | + configuration. By building this artifact, we create the local p2 repo that |
|
| 8 | + can then be referenced by the race-analysis-p2-local.target definition. So |
|
| 9 | + we can't recursively use the local repo as target platform for building the |
|
| 10 | + local repo - and apart from that don't need it. Skipping the tycho-maven-plugin |
|
| 11 | + and target-platform-configuration plugins via <skip> parameter in the build |
|
| 12 | + config does not skip resolving the target definition defined in the parent |
|
| 13 | + pom. Therefore, the parent was not set here - which means we have to define |
|
| 14 | + $tycho-version again. --> |
|
| 15 | + <properties> |
|
| 16 | + <tycho-version>1.7.0</tycho-version> |
|
| 17 | + </properties> |
|
| 18 | + <groupId>com.sap.sailing</groupId> |
|
| 19 | + <version>1.0.0-SNAPSHOT</version> |
|
| 20 | + <artifactId>com.sap.sailing.targetplatform.base</artifactId> |
|
| 21 | + <build> |
|
| 22 | + <plugins> |
|
| 23 | + <plugin> |
|
| 24 | + <groupId>org.eclipse.tycho.extras</groupId> |
|
| 25 | + <artifactId>tycho-p2-extras-plugin</artifactId> |
|
| 26 | + <version>${tycho-version}</version> |
|
| 27 | + <configuration> |
|
| 28 | + <sourceLocation>${basedir}</sourceLocation> |
|
| 29 | + </configuration> |
|
| 30 | + <executions> |
|
| 31 | + <execution> |
|
| 32 | + <id>generate-full-metadata</id> |
|
| 33 | + <phase>compile</phase> |
|
| 34 | + <goals> |
|
| 35 | + <goal>publish-features-and-bundles</goal> |
|
| 36 | + </goals> |
|
| 37 | + </execution> |
|
| 38 | + </executions> |
|
| 39 | + </plugin> |
|
| 40 | + </plugins> |
|
| 41 | + </build> |
|
| 42 | 42 | </project> |
java/com.sap.sailing.www/release_notes_admin.html
| ... | ... | @@ -23,6 +23,15 @@ |
| 23 | 23 | <div class="mainContent"> |
| 24 | 24 | <h2 class="releaseHeadline">Release Notes - Administration Console</h2> |
| 25 | 25 | <div class="innerContent"> |
| 26 | + <h2 class="articleSubheadline">January 2024</h2> |
|
| 27 | + <ul class="bulletList"> |
|
| 28 | + <li>When launching a new application replica set in a region, the choice of the default RabbitMQ |
|
| 29 | + server now depends on the region: in our "default" region "eu-west-1", RabbitMQ is identified |
|
| 30 | + by the DNS-mapped host name "rabbit.internal.sapsailing.com". Elsewhere, the RabbitMQ server |
|
| 31 | + in the region is explored using the RabbitMQEndpoint tag. If not found, again |
|
| 32 | + "rabbit.internal.sapsailing.com" will be used, assuming there may be a VPC peering across |
|
| 33 | + regions.</li> |
|
| 34 | + </ul> |
|
| 26 | 35 | <h2 class="articleSubheadline">October 2023</h2> |
| 27 | 36 | <ul class="bulletList"> |
| 28 | 37 | <li>TracTrac and YellowBrick passwords are no longer sent back to the client; there were ways to discover |
java/com.sap.sse.feature.runtime/feature.xml
| ... | ... | @@ -21,42 +21,42 @@ |
| 21 | 21 | id="org.mongodb.driver-core" |
| 22 | 22 | download-size="0" |
| 23 | 23 | install-size="0" |
| 24 | - version="4.5.1" |
|
| 24 | + version="4.11.1" |
|
| 25 | 25 | unpack="false"/> |
| 26 | 26 | |
| 27 | 27 | <plugin |
| 28 | 28 | id="org.mongodb.driver-core.source" |
| 29 | 29 | download-size="0" |
| 30 | 30 | install-size="0" |
| 31 | - version="4.5.1" |
|
| 31 | + version="4.11.1" |
|
| 32 | 32 | unpack="false"/> |
| 33 | 33 | |
| 34 | 34 | <plugin |
| 35 | 35 | id="org.mongodb.bson" |
| 36 | 36 | download-size="0" |
| 37 | 37 | install-size="0" |
| 38 | - version="4.5.1" |
|
| 38 | + version="4.11.1" |
|
| 39 | 39 | unpack="false"/> |
| 40 | 40 | |
| 41 | 41 | <plugin |
| 42 | 42 | id="org.mongodb.bson.source" |
| 43 | 43 | download-size="0" |
| 44 | 44 | install-size="0" |
| 45 | - version="4.5.1" |
|
| 45 | + version="4.11.1" |
|
| 46 | 46 | unpack="false"/> |
| 47 | 47 | |
| 48 | 48 | <plugin |
| 49 | 49 | id="org.mongodb.driver-sync" |
| 50 | 50 | download-size="0" |
| 51 | 51 | install-size="0" |
| 52 | - version="4.5.1" |
|
| 52 | + version="4.11.1" |
|
| 53 | 53 | unpack="false"/> |
| 54 | 54 | |
| 55 | 55 | <plugin |
| 56 | 56 | id="org.mongodb.driver-sync" |
| 57 | 57 | download-size="0" |
| 58 | 58 | install-size="0" |
| 59 | - version="4.5.1" |
|
| 59 | + version="4.11.1" |
|
| 60 | 60 | unpack="false"/> |
| 61 | 61 | |
| 62 | 62 | <plugin |
java/com.sap.sse.gwt/META-INF/MANIFEST.MF
| ... | ... | @@ -35,6 +35,7 @@ Export-Package: com.google.gwt.user.client.rpc.core.java.util, |
| 35 | 35 | com.sap.sse.gwt.client.controls.busyindicator, |
| 36 | 36 | com.sap.sse.gwt.client.controls.carousel, |
| 37 | 37 | com.sap.sse.gwt.client.controls.datetime, |
| 38 | + com.sap.sse.gwt.client.controls.dropdown, |
|
| 38 | 39 | com.sap.sse.gwt.client.controls.filestorage, |
| 39 | 40 | com.sap.sse.gwt.client.controls.languageselect, |
| 40 | 41 | com.sap.sse.gwt.client.controls.listedit, |
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/LinkUtil.java
| ... | ... | @@ -28,7 +28,7 @@ public final class LinkUtil { |
| 28 | 28 | * @return true if the event is a "normal" click event, false otherwise |
| 29 | 29 | */ |
| 30 | 30 | public static boolean handleLinkClick(Event event) { |
| 31 | - if(event.getTypeInt() != Event.ONCLICK) { |
|
| 31 | + if (event.getTypeInt() != Event.ONCLICK) { |
|
| 32 | 32 | return false; |
| 33 | 33 | } |
| 34 | 34 | return HYPERLINK_IMPL.handleAsClick(event); |
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/dropdown/Dropdown.gss
| ... | ... | @@ -0,0 +1,118 @@ |
| 1 | +.jsdropdown { |
|
| 2 | + position: relative; |
|
| 3 | +} |
|
| 4 | +.jsdropdown_head, |
|
| 5 | +.jsdropdown_content { |
|
| 6 | + text-indent: 0; |
|
| 7 | +} |
|
| 8 | +.dropdown_panel { |
|
| 9 | + width: 100%; |
|
| 10 | +} |
|
| 11 | +.dropdown { |
|
| 12 | + font-size: 1rem; |
|
| 13 | + width: 100%; |
|
| 14 | + max-width: 100%; |
|
| 15 | + display: inline-block; |
|
| 16 | + text-align: left; |
|
| 17 | + min-width: 21.333333333333332em; |
|
| 18 | + margin-left: -0.666666666666667em; |
|
| 19 | +} |
|
| 20 | +.dropdown_head { |
|
| 21 | + display: inline-block; |
|
| 22 | + position: relative; |
|
| 23 | + padding-left: 0.75em; |
|
| 24 | + padding-right: 1.75em; |
|
| 25 | + border-radius: 0.107142857142857em; |
|
| 26 | + border: 1px solid transparent; |
|
| 27 | + cursor: pointer; |
|
| 28 | +} |
|
| 29 | +.dropdown_head:hover { |
|
| 30 | + border: 1px solid #cfcfcf; |
|
| 31 | + background-color: #eee; |
|
| 32 | + text-decoration: none; |
|
| 33 | +} |
|
| 34 | +.dropdown_head_title { |
|
| 35 | + font-size: 1.866666666666667em; |
|
| 36 | + color: #333; |
|
| 37 | + height: 1.464285714285714em; |
|
| 38 | + line-height: 1.428571428571429em; |
|
| 39 | + font-weight: 700; |
|
| 40 | +} |
|
| 41 | +.dropdown_head_title_button { |
|
| 42 | + position: absolute; |
|
| 43 | + top: 0.75em; |
|
| 44 | + right: 0; |
|
| 45 | + display: inline-block; |
|
| 46 | + margin-left: 0.714285714285714em; |
|
| 47 | + height: 0.285714285714286em; |
|
| 48 | + width: 0.428571428571429em; |
|
| 49 | + background-image: resourceUrl("arrowDownFilledBlack"); |
|
| 50 | + background-position: center bottom; |
|
| 51 | + background-repeat: no-repeat; |
|
| 52 | + background-size: 100%; |
|
| 53 | + -webkit-transform: translate(-50%, -50%); |
|
| 54 | + -moz-transform: translate(-50%, -50%); |
|
| 55 | + -ms-transform: translate(-50%, -50%); |
|
| 56 | + transform: translate(-50%, -50%); |
|
| 57 | + float: none; |
|
| 58 | +} |
|
| 59 | +.dropdown_content { |
|
| 60 | + position: absolute; |
|
| 61 | + z-index: 1; |
|
| 62 | + display: none; |
|
| 63 | + height: auto; |
|
| 64 | + max-height: 23.333333333333332em; |
|
| 65 | + overflow: auto; |
|
| 66 | + width: auto; |
|
| 67 | + background-color: #fff; |
|
| 68 | + border: 1px solid #ccc; |
|
| 69 | + box-shadow: 0.133333333333333em 0.133333333333333em 0.333333333333333em #ccc; |
|
| 70 | +} |
|
| 71 | +.dropdownregattas_content { |
|
| 72 | +} |
|
| 73 | +.jsdropdownactive .dropdown_content { |
|
| 74 | + display: block; |
|
| 75 | +} |
|
| 76 | +.dropdown_content_link { |
|
| 77 | + display: block; |
|
| 78 | + cursor: pointer; |
|
| 79 | + padding: 1.333333333333333em 3.333333333333333em 1em 1em; |
|
| 80 | + border-bottom: 1px solid #f4f4f4; |
|
| 81 | + position: relative; |
|
| 82 | + border: none; |
|
| 83 | +} |
|
| 84 | +.dropdown_content_link:hover, |
|
| 85 | +.dropdown_content_link:focus { |
|
| 86 | + background: #f8f8f8; |
|
| 87 | + text-decoration: none; |
|
| 88 | +} |
|
| 89 | +.dropdown_content_link:hover .dropdown_content_link_title, |
|
| 90 | +.dropdown_content_link:focus .dropdown_content_link_title { |
|
| 91 | + text-decoration: underline; |
|
| 92 | +} |
|
| 93 | +.dropdown_content_link_title { |
|
| 94 | + display: block; |
|
| 95 | + font-weight: 600; |
|
| 96 | + font-size: 1.2em; |
|
| 97 | + color: #666; |
|
| 98 | +} |
|
| 99 | +.dropdown_content_link_subtitle { |
|
| 100 | + color: #cfcfcf; |
|
| 101 | + font-size: 1.066666666666667em; |
|
| 102 | +} |
|
| 103 | +.dropdown_content_linkactive:after { |
|
| 104 | + content: ''; |
|
| 105 | + position: absolute; |
|
| 106 | + right: 1.333333333333333em; |
|
| 107 | + top: 50%; |
|
| 108 | + height: 0.933333333333333em; |
|
| 109 | + width: 0.933333333333333em; |
|
| 110 | + background-image: resourceUrl("dropdownCheck"); |
|
| 111 | + background-repeat: no-repeat; |
|
| 112 | + background-position: right center; |
|
| 113 | + background-size: contain; |
|
| 114 | + -webkit-transform: translate(0, -50%); |
|
| 115 | + -moz-transform: translate(0, -50%); |
|
| 116 | + -ms-transform: translate(0, -50%); |
|
| 117 | + transform: translate(0, -50%); |
|
| 118 | +} |
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/dropdown/Dropdown.java
| ... | ... | @@ -0,0 +1,97 @@ |
| 1 | +package com.sap.sse.gwt.client.controls.dropdown; |
|
| 2 | + |
|
| 3 | +import com.google.gwt.core.client.GWT; |
|
| 4 | +import com.google.gwt.dom.client.AnchorElement; |
|
| 5 | +import com.google.gwt.dom.client.DivElement; |
|
| 6 | +import com.google.gwt.dom.client.SpanElement; |
|
| 7 | +import com.google.gwt.event.dom.client.ClickEvent; |
|
| 8 | +import com.google.gwt.event.dom.client.ClickHandler; |
|
| 9 | +import com.google.gwt.safehtml.shared.SafeUri; |
|
| 10 | +import com.google.gwt.uibinder.client.UiBinder; |
|
| 11 | +import com.google.gwt.uibinder.client.UiField; |
|
| 12 | +import com.google.gwt.user.client.Event; |
|
| 13 | +import com.google.gwt.user.client.ui.Composite; |
|
| 14 | +import com.google.gwt.user.client.ui.FlowPanel; |
|
| 15 | +import com.google.gwt.user.client.ui.HTMLPanel; |
|
| 16 | +import com.google.gwt.user.client.ui.Widget; |
|
| 17 | +import com.sap.sse.gwt.client.LinkUtil; |
|
| 18 | + |
|
| 19 | +public class Dropdown extends Composite { |
|
| 20 | + private static DropdownUiBinder uiBinder = GWT.create(DropdownUiBinder.class); |
|
| 21 | + |
|
| 22 | + interface DropdownUiBinder extends UiBinder<Widget, Dropdown> { |
|
| 23 | + } |
|
| 24 | + |
|
| 25 | + @UiField FlowPanel dropdownContent; |
|
| 26 | + @UiField AnchorElement dropdownTrigger; |
|
| 27 | + @UiField DivElement dropdownDisplayedText; |
|
| 28 | + @UiField DivElement dropdownTitle; |
|
| 29 | + @UiField DivElement dropdownHeadTitleButton; |
|
| 30 | + @UiField SpanElement dropdownHeadTitle; |
|
| 31 | + @UiField HTMLPanel dropdownPanel; |
|
| 32 | + private final DropdownResources local_res; |
|
| 33 | + final DropdownHandler dropdownHandler; // final to force initialization; default scope to avoid unused warning |
|
| 34 | + |
|
| 35 | + public Dropdown() { |
|
| 36 | + this(GWT.create(DropdownResources.class)); |
|
| 37 | + } |
|
| 38 | + |
|
| 39 | + public Dropdown(DropdownResources resources) { |
|
| 40 | + initWidget(uiBinder.createAndBindUi(this)); |
|
| 41 | + local_res = resources; |
|
| 42 | + resources.css().ensureInjected(); |
|
| 43 | + dropdownHandler = createDropdownHandler(); |
|
| 44 | + dropdownPanel.addStyleName(local_res.css().dropdown_panel()); |
|
| 45 | + dropdownTitle.addClassName(local_res.css().dropdown()); |
|
| 46 | + dropdownTitle.addClassName(local_res.css().jsdropdown()); |
|
| 47 | + dropdownTrigger.addClassName(local_res.css().dropdown_head()); |
|
| 48 | + dropdownTrigger.addClassName(local_res.css().jsdropdown_head()); |
|
| 49 | + dropdownContent.addStyleName(local_res.css().dropdown_content()); |
|
| 50 | + dropdownContent.addStyleName(local_res.css().jsdropdown_content()); |
|
| 51 | + dropdownHeadTitle.addClassName(local_res.css().dropdown_head_title()); |
|
| 52 | + dropdownHeadTitleButton.addClassName(local_res.css().dropdown_head_title_button()); |
|
| 53 | + } |
|
| 54 | + |
|
| 55 | + /** |
|
| 56 | + * Must be invoked after <tt>uiBinder.createAndBindUi(this)</tt> has been called |
|
| 57 | + */ |
|
| 58 | + private DropdownHandler createDropdownHandler() { |
|
| 59 | + return new DropdownHandler(dropdownTrigger, dropdownContent.getElement()) { |
|
| 60 | + @Override |
|
| 61 | + protected void dropdownStateChanged(boolean dropdownShown) { |
|
| 62 | + if (dropdownShown) { |
|
| 63 | + dropdownTitle.addClassName(local_res.css().jsdropdownactive()); |
|
| 64 | + } else { |
|
| 65 | + dropdownTitle.removeClassName(local_res.css().jsdropdownactive()); |
|
| 66 | + } |
|
| 67 | + } |
|
| 68 | + }; |
|
| 69 | + } |
|
| 70 | + |
|
| 71 | + public void hide() { |
|
| 72 | + dropdownHandler.hide(); |
|
| 73 | + } |
|
| 74 | + |
|
| 75 | + public void setDisplayedText(String displayedText) { |
|
| 76 | + dropdownDisplayedText.setInnerText(displayedText); |
|
| 77 | + } |
|
| 78 | + |
|
| 79 | + public void addItem(String itemText, SafeUri link, boolean selected, Runnable callback) { |
|
| 80 | + final DropdownItem dropdownItem = new DropdownItem(local_res, itemText, link, selected); |
|
| 81 | + dropdownItem.addDomHandler(new ClickHandler() { |
|
| 82 | + @Override |
|
| 83 | + public void onClick(ClickEvent event) { |
|
| 84 | + if (LinkUtil.handleLinkClick((Event) event.getNativeEvent())) { |
|
| 85 | + event.preventDefault(); |
|
| 86 | + if (callback != null) { |
|
| 87 | + callback.run(); |
|
| 88 | + } |
|
| 89 | + } |
|
| 90 | + } |
|
| 91 | + }, ClickEvent.getType()); |
|
| 92 | + dropdownContent.add(dropdownItem); |
|
| 93 | + if (selected) { |
|
| 94 | + setDisplayedText(itemText); |
|
| 95 | + } |
|
| 96 | + } |
|
| 97 | +} |
|
| ... | ... | \ No newline at end of file |
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/dropdown/Dropdown.ui.xml
| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | +<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"> |
|
| 2 | +<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" |
|
| 3 | + xmlns:g="urn:import:com.google.gwt.user.client.ui"> |
|
| 4 | + <g:HTMLPanel ui:field="dropdownPanel" debugId="DropdownPanel"> |
|
| 5 | + <div ui:field="dropdownTitle"> |
|
| 6 | + <a ui:field="dropdownTrigger"> |
|
| 7 | + <span ui:field="dropdownHeadTitle"> |
|
| 8 | + <div ui:field="dropdownDisplayedText"></div> |
|
| 9 | + <div ui:field="dropdownHeadTitleButton"></div> |
|
| 10 | + </span> |
|
| 11 | + </a> |
|
| 12 | + <g:FlowPanel ui:field="dropdownContent"> |
|
| 13 | + <!-- programmatically add DropdownItem elements here --> |
|
| 14 | + </g:FlowPanel> |
|
| 15 | + </div> |
|
| 16 | + </g:HTMLPanel> |
|
| 17 | +</ui:UiBinder> |
|
| ... | ... | \ No newline at end of file |
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/dropdown/DropdownHandler.java
| ... | ... | @@ -0,0 +1,92 @@ |
| 1 | +package com.sap.sse.gwt.client.controls.dropdown; |
|
| 2 | + |
|
| 3 | +import com.google.gwt.core.client.Scheduler; |
|
| 4 | +import com.google.gwt.core.client.Scheduler.ScheduledCommand; |
|
| 5 | +import com.google.gwt.dom.client.Element; |
|
| 6 | +import com.google.gwt.dom.client.EventTarget; |
|
| 7 | +import com.google.gwt.dom.client.Style.Display; |
|
| 8 | +import com.google.gwt.user.client.Event; |
|
| 9 | +import com.google.gwt.user.client.Event.NativePreviewEvent; |
|
| 10 | +import com.google.gwt.user.client.Event.NativePreviewHandler; |
|
| 11 | +import com.google.gwt.user.client.EventListener; |
|
| 12 | +import com.google.gwt.user.client.ui.FocusWidget; |
|
| 13 | +import com.google.web.bindery.event.shared.HandlerRegistration; |
|
| 14 | + |
|
| 15 | +public class DropdownHandler { |
|
| 16 | + private HandlerRegistration reg; |
|
| 17 | + private boolean dropdownShown = false; |
|
| 18 | + private final Element dropdownContainer; |
|
| 19 | + |
|
| 20 | + public DropdownHandler(Element dropdownTrigger, Element dropdownContainer) { |
|
| 21 | + this.dropdownContainer = dropdownContainer; |
|
| 22 | + Event.sinkEvents(dropdownTrigger, Event.ONCLICK); |
|
| 23 | + Event.setEventListener(dropdownTrigger, new EventListener() { |
|
| 24 | + @Override |
|
| 25 | + public void onBrowserEvent(Event event) { |
|
| 26 | + if (!dropdownShown) { |
|
| 27 | + show(); |
|
| 28 | + } |
|
| 29 | + } |
|
| 30 | + }); |
|
| 31 | + } |
|
| 32 | + |
|
| 33 | + public DropdownHandler(FocusWidget dropdownTrigger, Element dropdownContainer) { |
|
| 34 | + this.dropdownContainer = dropdownContainer; |
|
| 35 | + dropdownTrigger.addClickHandler(event -> { |
|
| 36 | + if (!dropdownShown) { |
|
| 37 | + show(); |
|
| 38 | + } |
|
| 39 | + }); |
|
| 40 | + } |
|
| 41 | + |
|
| 42 | + private void show() { |
|
| 43 | + dropdownShown = true; |
|
| 44 | + dropdownStateChanged(true); |
|
| 45 | + reg = Event.addNativePreviewHandler(new NativePreviewHandler() { |
|
| 46 | + @Override |
|
| 47 | + public void onPreviewNativeEvent(NativePreviewEvent event) { |
|
| 48 | + EventTarget eventTarget = event.getNativeEvent().getEventTarget(); |
|
| 49 | + if (Element.is(eventTarget)) { |
|
| 50 | + Element evtElement = Element.as(eventTarget); |
|
| 51 | + if (event.getTypeInt() == Event.ONCLICK && !dropdownContainer.isOrHasChild(evtElement)) { |
|
| 52 | + hide(); |
|
| 53 | + } |
|
| 54 | + } |
|
| 55 | + } |
|
| 56 | + |
|
| 57 | + }); |
|
| 58 | + } |
|
| 59 | + |
|
| 60 | + void hide() { |
|
| 61 | + Scheduler.get().scheduleDeferred(new ScheduledCommand() { |
|
| 62 | + @Override |
|
| 63 | + public void execute() { |
|
| 64 | + dropdownShown = false; |
|
| 65 | + dropdownStateChanged(false); |
|
| 66 | + if (reg != null) { |
|
| 67 | + reg.removeHandler(); |
|
| 68 | + reg = null; |
|
| 69 | + } |
|
| 70 | + } |
|
| 71 | + }); |
|
| 72 | + } |
|
| 73 | + |
|
| 74 | + public void setVisible(boolean visible) { |
|
| 75 | + if (visible != dropdownShown) { |
|
| 76 | + if (visible) { |
|
| 77 | + show(); |
|
| 78 | + } else { |
|
| 79 | + hide(); |
|
| 80 | + } |
|
| 81 | + } |
|
| 82 | + } |
|
| 83 | + |
|
| 84 | + protected void dropdownStateChanged(boolean dropdownShown) { |
|
| 85 | + if (dropdownShown) { |
|
| 86 | + dropdownContainer.getStyle().clearDisplay(); |
|
| 87 | + } else { |
|
| 88 | + dropdownContainer.getStyle().setDisplay(Display.NONE); |
|
| 89 | + } |
|
| 90 | + } |
|
| 91 | + |
|
| 92 | +} |
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/dropdown/DropdownItem.java
| ... | ... | @@ -0,0 +1,51 @@ |
| 1 | +package com.sap.sse.gwt.client.controls.dropdown; |
|
| 2 | + |
|
| 3 | +import com.google.gwt.core.client.GWT; |
|
| 4 | +import com.google.gwt.dom.client.AnchorElement; |
|
| 5 | +import com.google.gwt.dom.client.DivElement; |
|
| 6 | +import com.google.gwt.dom.client.Element; |
|
| 7 | +import com.google.gwt.safehtml.shared.SafeUri; |
|
| 8 | +import com.google.gwt.uibinder.client.UiBinder; |
|
| 9 | +import com.google.gwt.uibinder.client.UiField; |
|
| 10 | +import com.google.gwt.user.client.ui.Widget; |
|
| 11 | + |
|
| 12 | +public class DropdownItem extends Widget { |
|
| 13 | + private static DropdownItemUiBinder uiBinder = GWT.create(DropdownItemUiBinder.class); |
|
| 14 | + |
|
| 15 | + interface DropdownItemUiBinder extends UiBinder<Element, DropdownItem> { |
|
| 16 | + } |
|
| 17 | + |
|
| 18 | + @UiField |
|
| 19 | + AnchorElement link; |
|
| 20 | + |
|
| 21 | + @UiField |
|
| 22 | + DivElement title; |
|
| 23 | + |
|
| 24 | + private final String text; |
|
| 25 | + |
|
| 26 | + public DropdownItem(String text, SafeUri link, boolean active) { |
|
| 27 | + this(GWT.create(DropdownResources.class), text, link, active); |
|
| 28 | + } |
|
| 29 | + |
|
| 30 | + public DropdownItem(DropdownResources local_res, String text, SafeUri link, boolean active) { |
|
| 31 | + this.text = text; |
|
| 32 | + setElement(uiBinder.createAndBindUi(this)); |
|
| 33 | + local_res.css().ensureInjected(); |
|
| 34 | + if (active) { |
|
| 35 | + getElement().addClassName(local_res.css().dropdown_content_linkactive()); |
|
| 36 | + } |
|
| 37 | + this.link.addClassName(local_res.css().dropdown_content_link()); |
|
| 38 | + if (link != null) { |
|
| 39 | + this.link.setHref(link); |
|
| 40 | + } |
|
| 41 | + this.title.setInnerText(text); |
|
| 42 | + this.title.addClassName(local_res.css().dropdown_content_link_title()); |
|
| 43 | + } |
|
| 44 | + |
|
| 45 | + /** |
|
| 46 | + * @return the text passed to the constructor of this item |
|
| 47 | + */ |
|
| 48 | + public String getText() { |
|
| 49 | + return text; |
|
| 50 | + } |
|
| 51 | +} |
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/dropdown/DropdownItem.ui.xml
| ... | ... | @@ -0,0 +1,14 @@ |
| 1 | +<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"> |
|
| 2 | +<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"> |
|
| 3 | + <ui:with field="local_res" type="com.sap.sse.gwt.client.controls.dropdown.DropdownResources" /> |
|
| 4 | + <ui:style> |
|
| 5 | + .important { |
|
| 6 | + font-weight: bold; |
|
| 7 | + } |
|
| 8 | + </ui:style> |
|
| 9 | + <a ui:field="link"> |
|
| 10 | + <div ui:field="title"> |
|
| 11 | + <!-- regatta.name --> |
|
| 12 | + </div> |
|
| 13 | + </a> |
|
| 14 | +</ui:UiBinder> |
|
| ... | ... | \ No newline at end of file |
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/dropdown/DropdownResources.java
| ... | ... | @@ -0,0 +1,26 @@ |
| 1 | +package com.sap.sse.gwt.client.controls.dropdown; |
|
| 2 | + |
|
| 3 | +import com.google.gwt.resources.client.CssResource; |
|
| 4 | +import com.sap.sse.gwt.common.CommonIcons; |
|
| 5 | + |
|
| 6 | +public interface DropdownResources extends CommonIcons { |
|
| 7 | + @Source("Dropdown.gss") |
|
| 8 | + LocalCss css(); |
|
| 9 | + |
|
| 10 | + public interface LocalCss extends CssResource { |
|
| 11 | + String jsdropdown(); |
|
| 12 | + String jsdropdown_head(); |
|
| 13 | + String jsdropdown_content(); |
|
| 14 | + String dropdown(); |
|
| 15 | + String dropdown_head(); |
|
| 16 | + String dropdown_head_title(); |
|
| 17 | + String dropdown_head_title_button(); |
|
| 18 | + String dropdown_content(); |
|
| 19 | + String jsdropdownactive(); |
|
| 20 | + String dropdown_content_link(); |
|
| 21 | + String dropdown_content_link_title(); |
|
| 22 | + String dropdown_content_link_subtitle(); |
|
| 23 | + String dropdown_content_linkactive(); |
|
| 24 | + String dropdown_panel(); |
|
| 25 | + } |
|
| 26 | +} |
java/com.sap.sse.gwt/src/com/sap/sse/gwt/common/CommonIcons.java
| ... | ... | @@ -33,4 +33,13 @@ public interface CommonIcons extends ClientBundle { |
| 33 | 33 | |
| 34 | 34 | @Source("email-icon.png") |
| 35 | 35 | ImageResource email(); |
| 36 | + |
|
| 37 | + @Source("dropdown__check@2x.png") |
|
| 38 | + ImageResource dropdownCheck(); |
|
| 39 | + |
|
| 40 | + @Source("arrow-down-filled-black.png") |
|
| 41 | + ImageResource arrowDownFilledBlack(); |
|
| 42 | + |
|
| 43 | + @Source("arrow-down-filled-white.png") |
|
| 44 | + ImageResource arrowDownFilledWhite(); |
|
| 36 | 45 | } |
java/com.sap.sse.gwt/src/com/sap/sse/gwt/common/arrow-down-filled-black.png
| ... | ... | Binary files /dev/null and b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/common/arrow-down-filled-black.png differ |
java/com.sap.sse.gwt/src/com/sap/sse/gwt/common/arrow-down-filled-white.png
| ... | ... | Binary files /dev/null and b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/common/arrow-down-filled-white.png differ |
java/com.sap.sse.gwt/src/com/sap/sse/gwt/common/dropdown__check@2x.png
| ... | ... | Binary files /dev/null and b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/common/dropdown__check@2x.png differ |
java/com.sap.sse.landscape.aws.test/src/com/sap/sse/landscape/aws/ConnectivityTest.java
| ... | ... | @@ -51,6 +51,7 @@ import com.sap.sse.landscape.aws.orchestration.CreateDNSBasedLoadBalancerMapping |
| 51 | 51 | import com.sap.sse.landscape.impl.ReleaseRepositoryImpl; |
| 52 | 52 | import com.sap.sse.landscape.mongodb.MongoEndpoint; |
| 53 | 53 | import com.sap.sse.landscape.mongodb.impl.DatabaseImpl; |
| 54 | +import com.sap.sse.landscape.rabbitmq.RabbitMQEndpoint; |
|
| 54 | 55 | import com.sap.sse.landscape.ssh.SSHKeyPair; |
| 55 | 56 | import com.sap.sse.landscape.ssh.SshCommandChannel; |
| 56 | 57 | |
| ... | ... | @@ -535,4 +536,25 @@ public class ConnectivityTest<ProcessT extends AwsApplicationProcess<String, Sai |
| 535 | 536 | assertEquals(200, healthCheckConnection.getResponseCode()); |
| 536 | 537 | healthCheckConnection.disconnect(); |
| 537 | 538 | } |
| 539 | + |
|
| 540 | + @Test |
|
| 541 | + public void getDefaultRabbitConfigForEuWest1() { |
|
| 542 | + final RabbitMQEndpoint rabbitConfig = landscape.getDefaultRabbitConfiguration(new AwsRegion(Region.EU_WEST_1, landscape)); |
|
| 543 | + assertEquals("rabbit.internal.sapsailing.com", rabbitConfig.getNodeName()); |
|
| 544 | + assertEquals(5672, rabbitConfig.getPort()); |
|
| 545 | + } |
|
| 546 | + |
|
| 547 | + @Test |
|
| 548 | + public void getDefaultRabbitConfigForEuWest2() { |
|
| 549 | + final RabbitMQEndpoint rabbitConfig = landscape.getDefaultRabbitConfiguration(new AwsRegion(Region.EU_WEST_2, landscape)); |
|
| 550 | + assertTrue(rabbitConfig.getNodeName().startsWith("172.31.")); |
|
| 551 | + assertEquals(5672, rabbitConfig.getPort()); |
|
| 552 | + } |
|
| 553 | + |
|
| 554 | + @Test |
|
| 555 | + public void getDefaultRabbitConfigForRegionWithNoTaggedInstanceInIt() { |
|
| 556 | + final RabbitMQEndpoint rabbitConfig = landscape.getDefaultRabbitConfiguration(new AwsRegion(Region.US_EAST_2, landscape)); |
|
| 557 | + assertEquals("rabbit.internal.sapsailing.com", rabbitConfig.getNodeName()); |
|
| 558 | + assertEquals(5672, rabbitConfig.getPort()); |
|
| 559 | + } |
|
| 538 | 560 | } |
java/com.sap.sse.landscape.aws/META-INF/MANIFEST.MF
| ... | ... | @@ -26,7 +26,8 @@ Require-Bundle: com.amazon.aws.aws-java-api;bundle-version="2.13.50", |
| 26 | 26 | com.sap.sse.replication.interfaces, |
| 27 | 27 | com.sap.sse.operationaltransformation, |
| 28 | 28 | org.mongodb.driver-core;bundle-version="4.3.1", |
| 29 | - org.mongodb.driver-sync;bundle-version="4.3.1" |
|
| 29 | + org.mongodb.driver-sync;bundle-version="4.3.1", |
|
| 30 | + com.sap.sailing.landscape.common |
|
| 30 | 31 | Web-ContextPath: /landscape |
| 31 | 32 | Import-Package: org.apache.shiro;version="1.2.2", |
| 32 | 33 | org.osgi.framework;version="1.8.0", |
java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/AwsLandscape.java
| ... | ... | @@ -34,7 +34,6 @@ import com.sap.sse.landscape.mongodb.MongoProcess; |
| 34 | 34 | import com.sap.sse.landscape.mongodb.MongoProcessInReplicaSet; |
| 35 | 35 | import com.sap.sse.landscape.mongodb.MongoReplicaSet; |
| 36 | 36 | import com.sap.sse.landscape.mongodb.impl.MongoProcessImpl; |
| 37 | -import com.sap.sse.landscape.rabbitmq.RabbitMQEndpoint; |
|
| 38 | 37 | import com.sap.sse.landscape.ssh.SSHKeyPair; |
| 39 | 38 | |
| 40 | 39 | import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; |
| ... | ... | @@ -129,12 +128,6 @@ public interface AwsLandscape<ShardingKey> extends Landscape<ShardingKey> { |
| 129 | 128 | |
| 130 | 129 | String MONGO_REPLICA_SET_NAME_AND_PORT_SEPARATOR = ":"; |
| 131 | 130 | |
| 132 | - /** |
|
| 133 | - * Tag name used to identify instances on which a RabbitMQ installation is running. The tag value is currently interpreted to |
|
| 134 | - * be the port number (usually 5672) on which the RabbitMQ endpoint can be reached. |
|
| 135 | - */ |
|
| 136 | - String RABBITMQ_TAG_NAME = "RabbitMQEndpoint"; |
|
| 137 | - |
|
| 138 | 131 | |
| 139 | 132 | |
| 140 | 133 | /** |
| ... | ... | @@ -677,13 +670,6 @@ public interface AwsLandscape<ShardingKey> extends Landscape<ShardingKey> { |
| 677 | 670 | |
| 678 | 671 | Iterable<MongoEndpoint> getMongoEndpoints(Region region); |
| 679 | 672 | |
| 680 | - /** |
|
| 681 | - * Gets a default RabbitMQ configuration for the {@code region} specified.<p> |
|
| 682 | - * |
|
| 683 | - * TODO For now, the method searches for accordingly-tagged instances and picks the first one it finds. We need to extend this to RabbitMQ replication. |
|
| 684 | - */ |
|
| 685 | - RabbitMQEndpoint getDefaultRabbitConfiguration(AwsRegion region); |
|
| 686 | - |
|
| 687 | 673 | Database getDatabase(Region region, String databaseName); |
| 688 | 674 | |
| 689 | 675 | /** |
java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/impl/AwsLandscapeImpl.java
| ... | ... | @@ -1484,26 +1484,32 @@ public class AwsLandscapeImpl<ShardingKey> implements AwsLandscape<ShardingKey> |
| 1484 | 1484 | } |
| 1485 | 1485 | |
| 1486 | 1486 | @Override |
| 1487 | - public RabbitMQEndpoint getDefaultRabbitConfiguration(AwsRegion region) { |
|
| 1487 | + public RabbitMQEndpoint getDefaultRabbitConfiguration(com.sap.sse.landscape.Region region) { |
|
| 1488 | + final RabbitMQEndpoint defaultRabbitMQInDefaultRegion = ()->SharedLandscapeConstants.RABBIT_IN_DEFAULT_REGION_HOSTNAME; // using default port RabbitMQEndpoint.DEFAULT_PORT |
|
| 1488 | 1489 | final RabbitMQEndpoint result; |
| 1489 | - final Iterable<AwsInstance<ShardingKey>> rabbitMQHostsInRegion = getRunningHostsWithTag(region, RABBITMQ_TAG_NAME, AwsInstanceImpl::new); |
|
| 1490 | - if (rabbitMQHostsInRegion.iterator().hasNext()) { |
|
| 1491 | - final AwsInstance<ShardingKey> anyRabbitMQHost = rabbitMQHostsInRegion.iterator().next(); |
|
| 1492 | - result = new RabbitMQEndpoint() { |
|
| 1493 | - @Override |
|
| 1494 | - public int getPort() { |
|
| 1495 | - return getTag(anyRabbitMQHost, RABBITMQ_TAG_NAME) |
|
| 1496 | - .map(t -> t.trim().isEmpty() ? RabbitMQEndpoint.DEFAULT_PORT : Integer.valueOf(t.trim())) |
|
| 1497 | - .orElse(RabbitMQEndpoint.DEFAULT_PORT); |
|
| 1498 | - } |
|
| 1499 | - |
|
| 1500 | - @Override |
|
| 1501 | - public String getNodeName() { |
|
| 1502 | - return anyRabbitMQHost.getPrivateAddress().getHostAddress(); |
|
| 1503 | - } |
|
| 1504 | - }; |
|
| 1490 | + if (region.getId().equals(Region.EU_WEST_1.id())) { |
|
| 1491 | + result = defaultRabbitMQInDefaultRegion; |
|
| 1505 | 1492 | } else { |
| 1506 | - result = null; |
|
| 1493 | + final Iterable<AwsInstance<ShardingKey>> rabbitMQHostsInRegion = getRunningHostsWithTag( |
|
| 1494 | + region, SharedLandscapeConstants.RABBITMQ_TAG_NAME, AwsInstanceImpl::new); |
|
| 1495 | + if (rabbitMQHostsInRegion.iterator().hasNext()) { |
|
| 1496 | + final AwsInstance<ShardingKey> anyRabbitMQHost = rabbitMQHostsInRegion.iterator().next(); |
|
| 1497 | + result = new RabbitMQEndpoint() { |
|
| 1498 | + @Override |
|
| 1499 | + public int getPort() { |
|
| 1500 | + return getTag(anyRabbitMQHost, SharedLandscapeConstants.RABBITMQ_TAG_NAME) |
|
| 1501 | + .map(t -> t.trim().isEmpty() ? RabbitMQEndpoint.DEFAULT_PORT : Integer.valueOf(t.trim())) |
|
| 1502 | + .orElse(RabbitMQEndpoint.DEFAULT_PORT); |
|
| 1503 | + } |
|
| 1504 | + |
|
| 1505 | + @Override |
|
| 1506 | + public String getNodeName() { |
|
| 1507 | + return anyRabbitMQHost.getPrivateAddress().getHostAddress(); |
|
| 1508 | + } |
|
| 1509 | + }; |
|
| 1510 | + } else { |
|
| 1511 | + result = defaultRabbitMQInDefaultRegion; // no instance with tag found; hope for VPC peering and use RabbitMQ hostname from default region |
|
| 1512 | + } |
|
| 1507 | 1513 | } |
| 1508 | 1514 | return result; |
| 1509 | 1515 | } |
| ... | ... | @@ -1514,17 +1520,6 @@ public class AwsLandscapeImpl<ShardingKey> implements AwsLandscape<ShardingKey> |
| 1514 | 1520 | } |
| 1515 | 1521 | |
| 1516 | 1522 | @Override |
| 1517 | - public RabbitMQEndpoint getMessagingConfigurationForDefaultCluster(com.sap.sse.landscape.Region region) { |
|
| 1518 | - final RabbitMQEndpoint result; |
|
| 1519 | - if (region.getId().equals(Region.EU_WEST_1.id())) { |
|
| 1520 | - result = ()->"rabbit.internal.sapsailing.com"; |
|
| 1521 | - } else { |
|
| 1522 | - result = null; |
|
| 1523 | - } |
|
| 1524 | - return result; |
|
| 1525 | - } |
|
| 1526 | - |
|
| 1527 | - @Override |
|
| 1528 | 1523 | public <MetricsT extends ApplicationProcessMetrics, ProcessT extends AwsApplicationProcess<ShardingKey, MetricsT, ProcessT>, |
| 1529 | 1524 | HostT extends ApplicationProcessHost<ShardingKey, MetricsT, ProcessT>> |
| 1530 | 1525 | Iterable<HostT> getApplicationProcessHostsByTag(com.sap.sse.landscape.Region region, String tagName, |
java/com.sap.sse.landscape.aws/src/com/sap/sse/landscape/aws/orchestration/AwsApplicationConfiguration.java
| ... | ... | @@ -9,6 +9,7 @@ import com.sap.sse.landscape.DefaultProcessConfigurationVariables; |
| 9 | 9 | import com.sap.sse.landscape.InboundReplicationConfiguration; |
| 10 | 10 | import com.sap.sse.landscape.OutboundReplicationConfiguration; |
| 11 | 11 | import com.sap.sse.landscape.ProcessConfigurationVariable; |
| 12 | +import com.sap.sse.landscape.Region; |
|
| 12 | 13 | import com.sap.sse.landscape.Release; |
| 13 | 14 | import com.sap.sse.landscape.UserDataProvider; |
| 14 | 15 | import com.sap.sse.landscape.application.ApplicationProcess; |
| ... | ... | @@ -43,7 +44,7 @@ implements UserDataProvider { |
| 43 | 44 | * {@link #getServerName() server name}.</li> |
| 44 | 45 | * <li>The {@link #setInboundReplicationConfiguration(InboundReplicationConfiguration) inbound replication} |
| 45 | 46 | * {@link InboundReplicationConfiguration#getInboundRabbitMQEndpoint() RabbitMQ endpoint} defaults to the region's |
| 46 | - * {@link AwsLandscape#getDefaultRabbitConfiguration(com.sap.sse.landscape.aws.impl.AwsRegion) default RabbitMQ |
|
| 47 | + * {@link AwsLandscape#getDefaultRabbitConfiguration(Region) default RabbitMQ |
|
| 47 | 48 | * configuration}. Note that this setting will take effect only if auto-replication is activated for one or more |
| 48 | 49 | * replicables (see {@link InboundReplicationConfiguration#getReplicableIds()}).</li> |
| 49 | 50 | * <li>The {@link #setOutboundReplicationConfiguration() outbound replication} |
java/com.sap.sse.landscape/src/com/sap/sse/landscape/Landscape.java
| ... | ... | @@ -49,10 +49,15 @@ public interface Landscape<ShardingKey> { |
| 49 | 49 | /** |
| 50 | 50 | * Obtains the default RabbitMQ configuration for the {@code region} specified. If nothing else is specified |
| 51 | 51 | * explicitly, application server replica sets launched in the {@code region} shall use this for their replication |
| 52 | - * message channels and exchanges. |
|
| 52 | + * message channels and exchanges.<p> |
|
| 53 | + * |
|
| 54 | + * For our default region, this will return a DNS name always pointing to the current private IP of |
|
| 55 | + * the instance running the default RabbitMQ service in the region. In other regions, the private IP |
|
| 56 | + * of the regional default RabbitMQ instance is discovered by scanning for running instances tagged |
|
| 57 | + * with {@link SharedLandscapeConstants#RABBITMQ_TAG_NAME}. |
|
| 53 | 58 | */ |
| 54 | - RabbitMQEndpoint getMessagingConfigurationForDefaultCluster(Region region); |
|
| 55 | - |
|
| 59 | + RabbitMQEndpoint getDefaultRabbitConfiguration(Region region); |
|
| 60 | + |
|
| 56 | 61 | /** |
| 57 | 62 | * Tells the regions supported. The underlying hyperscaler may have more, but we may not want to run in all. |
| 58 | 63 | */ |
java/com.sap.sse.landscape/src/com/sap/sse/landscape/mongodb/impl/MongoEndpointImpl.java
| ... | ... | @@ -59,7 +59,7 @@ public abstract class MongoEndpointImpl implements MongoEndpoint { |
| 59 | 59 | if (i>=BATCH_SIZE) { |
| 60 | 60 | targetCollection.insertMany(documentsToInsert); |
| 61 | 61 | i = 0; |
| 62 | - documentsToInsert = new ArrayList<>(BATCH_SIZE); |
|
| 62 | + documentsToInsert.clear(); |
|
| 63 | 63 | } |
| 64 | 64 | } |
| 65 | 65 | if (i>0) { |
java/com.sap.sse.security.test/src/com/sap/sse/security/test/SecurityServiceAndHasPermissionsProviderTest.java
| ... | ... | @@ -36,7 +36,8 @@ public class SecurityServiceAndHasPermissionsProviderTest { |
| 36 | 36 | PersistenceFactory.INSTANCE.getDefaultMajorityMongoObjectFactory(), TEST_DEFAULT_TENANT); |
| 37 | 37 | userStore.ensureDefaultRolesExist(); |
| 38 | 38 | userStore.loadAndMigrateUsers(); |
| 39 | - accessControlStore = new AccessControlStoreImpl(userStore); |
|
| 39 | + accessControlStore = new AccessControlStoreImpl(PersistenceFactory.INSTANCE.getDefaultMajorityDomainObjectFactory(), |
|
| 40 | + PersistenceFactory.INSTANCE.getDefaultMajorityMongoObjectFactory(), userStore); |
|
| 40 | 41 | } |
| 41 | 42 | |
| 42 | 43 | @After |
java/com.sap.sse.security/src/com/sap/sse/security/impl/Activator.java
| ... | ... | @@ -97,7 +97,7 @@ public class Activator implements BundleActivator { |
| 97 | 97 | * shared across different sub-domains; otherwise, the base URL where {@code /gwt-base/StorageMessaging.html} can be |
| 98 | 98 | * loaded from shall be provided, and this base URL will then form the "origin" for the local browser storage used |
| 99 | 99 | * for session storage and other local data stored in the browser which will then be available to all servers using |
| 100 | - * the same setting fo rthis field. Example value: {@code "https://www.sapsailing.com"}. |
|
| 100 | + * the same setting for this field. Example value: {@code "https://security-service.sapsailing.com"}. |
|
| 101 | 101 | * <p> |
| 102 | 102 | * |
| 103 | 103 | * The value can be specified by a system property whose name is provided by |
java/com.sap.sse/src/com/sap/sse/util/impl/CountingOutputStream.java
| ... | ... | @@ -43,7 +43,7 @@ public class CountingOutputStream extends FilterOutputStream { |
| 43 | 43 | } |
| 44 | 44 | |
| 45 | 45 | private void log() { |
| 46 | - logger.log(level, "wrote "+count+" bytes "+(count>1024*1024?"("+count/1024/1024+"MB)":"")+" to "+name); |
|
| 46 | + logger.log(level, "wrote "+count+" bytes "+(count>1024*1024?"("+count/1024/1024+"MB) ":"")+"to "+name); |
|
| 47 | 47 | } |
| 48 | 48 | |
| 49 | 49 | @Override |
java/target/env-default-rules.sh
| ... | ... | @@ -58,7 +58,13 @@ if [ -z "${MONGODB_PORT}" ]; then |
| 58 | 58 | MONGODB_PORT=27017 |
| 59 | 59 | fi |
| 60 | 60 | if [ -z "${MONGODB_HOST}" -a -z "${MONGODB_URI}" ]; then |
| 61 | - MONGODB_URI="mongodb://mongo0.internal.sapsailing.com,mongo1.internal.sapsailing.com/${MONGODB_NAME}?replicaSet=live&retryWrites=true&readPreference=nearest" |
|
| 61 | + if [ -n "$AUTO_REPLICATE" ]; then |
|
| 62 | + # An auto-replication replica by default assumes it has a local MongoDB replica set running on localhost, |
|
| 63 | + # called "replica" and running on the default port 27017: |
|
| 64 | + MONGODB_URI="mongodb://localhost/${MONGODB_NAME}?replicaSet=replica&retryWrites=true&readPreference=nearest" |
|
| 65 | + else |
|
| 66 | + MONGODB_URI="mongodb://mongo0.internal.sapsailing.com,mongo1.internal.sapsailing.com/${MONGODB_NAME}?replicaSet=live&retryWrites=true&readPreference=nearest" |
|
| 67 | + fi |
|
| 62 | 68 | fi |
| 63 | 69 | if [ -z "${EXPEDITION_PORT}" ]; then |
| 64 | 70 | EXPEDITION_PORT=2010 |
wiki/howto/downloading-and-archiving-tractrac-events.md
| ... | ... | @@ -57,7 +57,7 @@ For example, within the production landscape of sapsailing.com, you could try th |
| 57 | 57 | |
| 58 | 58 | ## Automation |
| 59 | 59 | |
| 60 | -For `wiki@sapsailing.com` the process of updating the `configuration/tractrac-json-urls` file from the ARCHIVE server is automated by means of two cron jobs as specified in `wiki@sapsailing.com:crontab`: |
|
| 60 | +For `wiki@sapsailing.com` the process of updating the `configuration/tractrac-json-urls` file from the ARCHIVE server is automated by means of two cron jobs as specified in `wiki@sapsailing.com:crontab` or at `$OUR_GIT_HOME/configuration/crontabs/users/crontab-wiki`: |
|
| 61 | 61 | |
| 62 | 62 | ``` |
| 63 | 63 | 10 12 * * * /home/wiki/gitwiki/configuration/update-tractrac-urls-to-archive.sh >/home/wiki/update-tractrac-urls-to-archive.out 2>/home/wiki/update-tractrac-urls-to-archive.err |
wiki/info/landscape/amazon-ec2-backup-strategy.md
| ... | ... | @@ -240,7 +240,7 @@ TARGET_DIR=/var/lib/mysql/backup |
| 240 | 240 | |
| 241 | 241 | # Configuration for MySQL |
| 242 | 242 | MYSQL_DATABASES="bugs mysql" |
| 243 | -MYSQLEXPORT_CMD="mysqldump -u root --password=sailaway" |
|
| 243 | +MYSQLEXPORT_CMD="mysqldump -u root --password=..." |
|
| 244 | 244 | |
| 245 | 245 | [...] |
| 246 | 246 |
wiki/info/landscape/amazon-ec2.md
| ... | ... | @@ -196,14 +196,21 @@ A failover instance is kept ready to switch to in case the primary production ar |
| 196 | 196 | ### Important Amazon Machine Images (AMIs) |
| 197 | 197 | |
| 198 | 198 | In our default region ``eu-west-1`` there are four Amazon Machine Image (AMI) types that are relevant for the operation of the landscape. They all have a base name to which, separated by a space character, a version number consisting of a major and minor version, separated by a dot, is appended. Each of these AMIs has a tag ``image-type`` whose value reflects the type of the image. |
| 199 | -- SAP Sailing Analytics, ``image-type`` is ``sailing-analytics-server`` |
|
| 200 | -- MongoDB Live Replica Set NVMe, ``image-type`` is ``mongodb-server`` |
|
| 201 | -- Hudson Ubuntu Slave, ``image-type`` is ``hudson-slave`` |
|
| 202 | -- Webserver, ``image-type`` is ``webserver`` |
|
| 199 | +- SAP Sailing Analytics, ``image-type`` is ``sailing-analytics-server``, see [here](/wiki/info/landscape/creating-ec2-image-from-scratch) |
|
| 200 | +- MongoDB Live Replica Set NVMe, ``image-type`` is ``mongodb-server``, see [here](/wiki/info/landscape/creating-ec2-mongodb-image-from-scratch) |
|
| 201 | +- Hudson Debian/Ubuntu Slave, ``image-type`` is ``hudson-slave`` |
|
| 202 | +- Webserver, ``image-type`` is ``webserver``, see [here](/wiki/info/landscape/creating-ec2-image-for-webserver-from-scratch) |
|
| 203 | + |
|
| 204 | +There are furthermore instance types that we can configure automatically, based on a clean Amazon Linux 2 instance launched from the respective default Amazon image: |
|
| 205 | +- Hudson / dev.sapsailing.com server, see [here](/wiki/info/landscape/creating-ec2-image-for-hudson-from-scratch) |
|
| 206 | +- MySQL / MariaDB database server holding the data for our ``bugzilla.sapsailing.com`` bug/issue tracker, see [here](/wiki/info/landscape/creating-ec2-image-for-mysql-from-scratch) |
|
| 207 | +- RabbitMQ default instance used by all default sailing servers for replication, see [here](/wiki/info/landscape/creating-ec2-image-for-rabbitmq-from-scratch) |
|
| 208 | + |
|
| 209 | +We try to maintain setup scripts that help us with setting up those instance types from scratch. See the respective Wiki pages referenced from the lists above for more details. |
|
| 203 | 210 | |
| 204 | 211 | The SAP Sailing Analytics image is used to launch new instances, shared or dedicated, that host one or more Sailing Analytics application processes. The image contains an installation of the SAP JVM 8 under /opt/sapjvm_8, an Apache httpd service that is not currently used by default for reverse proxying / rewriting / logging activities, an initially empty directory ``/home/sailing/servers`` used to host default application process configurations, and an initialization script under ``/etc/init.d/sailing`` that handles the instance's initialization with a default application process from the EC2 instance's user data. Instructions for setting up such an image from scratch can be found [here](/wiki/info/landscape/creating-ec2-image-from-scratch). |
| 205 | 212 | |
| 206 | -The user data line ``image-upgrade`` will cause the image to ignore all application configuration data and only bring the new instance to an updated state. For this, the Git content under ``/home/sailing/code`` is brought to the latest master branch commit, a ``yum update`` is carried out to install all operating system package updates available, log directories and the ``/home/sailing/servers`` directory are cleared, and the ``root`` user's crontab is brought up to date from the Git ``configuration/crontab`` file. If the ``no-shutdown`` line is provided in the instance's user data, the instance will be left running. Otherwise, it will shut down which would be a good default for creating a new image. See also procedures that automate much of this upgrade process. |
|
| 213 | +The user data line ``image-upgrade`` will cause the image to ignore all application configuration data and only bring the new instance to an updated state. For this, the Git content under ``/home/sailing/code`` is brought to the latest master branch commit, a ``yum update`` is carried out to install all operating system package updates available, log directories and the ``/home/sailing/servers`` directory are cleared, and the ``root`` user's crontab is brought up to date by running `crontab /root/crontab`, under the assumption it points to the appropriately named crontab in `$OUR_GIT_HOME/configuration/crontabs` (as we have different crontabs for different instances/users). If the ``no-shutdown`` line is provided in the instance's user data, the instance will be left running. Otherwise, it will shut down which would be a good default for creating a new image. See also procedures that automate much of this upgrade process. |
|
| 207 | 214 | |
| 208 | 215 | The MongoDB Live Replica Set NVMe image is used to scale out or upgrade existing MongoDB replica sets. It also reads the EC2 instance's user data during start-up and can be parameterized by the following variables: ``REPLICA_SET_NAME``, ``REPLICA_SET_PRIMARY``, ``REPLICA_SET_PRIORITY``, and ``REPLICA_SET_VOTES``. An example configuration could look like this: |
| 209 | 216 | ``` |
| ... | ... | @@ -361,11 +368,11 @@ With this, the three REST API end points `/landscape/api/landscape/get_time_poin |
| 361 | 368 | Two new scripts and a crontab file are provided under the configuration/ folder: |
| 362 | 369 | - `update_authorized_keys_for_landscape_managers_if_changed` |
| 363 | 370 | - `update_authorized_keys_for_landscape_managers` |
| 364 | -- `crontab` |
|
| 371 | +- `crontab` (found within configuration for historical reasons, but we should be using those in configuration/crontabs) |
|
| 365 | 372 | |
| 366 | 373 | The first makes a call to `/landscape/api/landscape/get_time_point_of_last_change_in_ssh_keys_of_aws_landscape_managers` (currently coded to `https://security-service.sapsailing.com` in the crontab file). If no previous time stamp for the last change exists under `/var/run/last_change_aws_landscape_managers_ssh_keys` or the time stamp received in the response is newer, the `update_authorized_keys_for_landscape_managers` script is invoked using the bearer token provided in `/root/ssh-key-reader.token` as argument, granting the script READ access to the user list and their SSH key pairs. That script first asks for `/security/api/restsecurity/users_with_permission?permission=LANDSCAPE:MANAGE:AWS` and then uses `/landscape/api/landscape/get_ssh_keys_owned_by_user?username[]=..`. to obtain the actual SSH public key information for the landscape managers. The original `/root/.ssh/authorized_keys` file is copied to `/root/.ssh/authorized_keys.org` once and then used to insert the single public SSH key inserted by AWS, then appending all public keys received for the landscape-managing users. |
| 367 | 374 | |
| 368 | -The `crontab` file which is used during image-upgrade (see `configuration/imageupdate.sh`) has a randomized sleeping period within a one minute duration after which it calls the `update_authorized_keys_for_landscape_managers_if_changed` script which transitively invokes `update_authorized_keys_for_landscape_managers` in case of changes possible. |
|
| 375 | +The `crontab` file which is used during image-upgrade (see `configuration/imageupgrade.sh`) has a randomized sleeping period within a one minute duration after which it calls the `update_authorized_keys_for_landscape_managers_if_changed` script which transitively invokes `update_authorized_keys_for_landscape_managers` in case of changes possible. |
|
| 369 | 376 | |
| 370 | 377 | ## Legacy Documentation for Manual Operations |
| 371 | 378 |
wiki/info/landscape/creating-ec2-image-for-hudson-from-scratch.md
| ... | ... | @@ -1,112 +1,55 @@ |
| 1 | 1 | # Setting up an image for the hudson.sapsailing.com server |
| 2 | 2 | |
| 3 | -This is an add-on to the regular EC2 image set-up described [here](https://wiki.sapsailing.com/wiki/info/landscape/creating-ec2-image-from-scratch). An Android SDK needs to be installed. |
|
| 4 | - |
|
| 5 | - |
|
| 6 | -* Create a ``hudson`` user/group |
|
| 7 | -* Make sure ``/home/hudson`` is a separate mount; probably just mount the existing volume of a previous installation |
|
| 8 | -* Install an Android SDK under ``/home/hudson/android-sdk-linux``; if you simply re-used an old ``/home/hudson`` mount this should already be in place. |
|
| 9 | -* Install Eclipse to ``/home/hudson/eclipse`` to allow sharing it in case a large AWS instance is needed, e.g., for heap dump analysis. |
|
| 10 | -* export ``/home/hudson/android-sdk-linux`` and ``/home/hudson/eclipse`` as follows in ``/etc/exports``: |
|
| 3 | +Like when setting up a regular sailing application server instance, start with a fresh Amazon Linux 2 image and create an instance with a 16GB root volume. Use the "Sailing Analytics Server" security group and something like a ``t3.small`` instance type for creating the image. Then, invoke the script |
|
| 11 | 4 | ``` |
| 12 | -/home/hudson/android-sdk-linux 172.31.0.0/16(rw,nohide,no_root_squash) |
|
| 13 | -/home/hudson/eclipse 172.31.0.0/16(rw,nohide,no_root_squash) |
|
| 5 | + configuration/hudson_instance_setup/setup-hudson-server.sh {external-IP-of-new-instance} |
|
| 14 | 6 | ``` |
| 7 | +This will first run the regular sailing server set-up which allows the instance to run the ``dev.sapsailing.com`` Sailing Analytics instance later. Then, the script will continue to obtain the Hudson WAR file from ``https://static.sapsailing.com/hudson.war.patched-with-mail-1.6.2`` and deploy it to ``/usr/lib/hudson``, obtain and install the default system-wide Hudson configuration, get adjusted dev server secrets from ``ssh://root@sapsailing.com/root/dev-secrets`` as well as ``mail.properties``, and install a ``hudson.service`` unit under ``/etc/systemd/system``. A ``hudson`` user is created, and its ``/home/hudson`` home directory is emptied so it can act as a mount point. A latest version of the SAP Sailing Analytics is installed to ``/home/sailing/servers/DEV``. |
|
| 15 | 8 | |
| 16 | -* Ensure you have EC2 / EBS snapshot backups for the volumes by tagging them as follows: ``WeeklySailingInfrastructureBackup=Yes`` for ``/`` and ``/home/hudson``. |
|
| 9 | +The ``/home/hudson/android-sdk-linux`` folder that is later expected to be mounted into the ``/home/hudson`` mount point is exported through NFS by appending a corresponding entry to ``/etc/exports``. The script will also allow the ``hudson`` user to run the ``/usr/local/bin/launchhudsonslave`` script with ``sudo``. In order to elastically scale our build / CI infrastructure, we use AWS to provide Hudson build slaves on demand. The Hudson Master (https://hudson.sapsailing.com) has a script obtained from our git at ``./configuration/launchhudsonslave`` which takes an Amazon Machine Image (AMI), launches it in our default region (eu-west-1) and connects to it. The AWS credentials are stored in the ``root`` account on ``hudson.sapsailing.com``, and the ``hudson`` user is granted access to the script by means of an ``/etc/sudoers.d`` entry. |
|
| 17 | 10 | |
| 18 | -``/home/hudson/repo`` has the Hudson build repository. The Hudson WAR file is under ``/usr/lib/hudson/hudson.war`` which has to be taken from [here](https://static.sapsailing.com/hudson.war.patched-with-mail-1.6.2). ``/etc/init.d/hudson``, linked to from ``/etc/rc0.d/K29hudson``, ``/etc/rc1.d/K29hudson``, ``/etc/rc2.d/K29hudson``, ``/etc/rc3.d/S81hudson``, ``/etc/rc4.d/K29hudson``, ``/etc/rc5.d/S81hudson``, and ``/etc/rc6.d/K29hudson``, takes care of spinning up Hudson during instance re-boot. It can be obtained from [here](https://static.sapsailing.com/etc-init.d-hudson). Hudson systemwide configuration is under ``/etc/sysconfig/hudson``: |
|
| 11 | +When the script has finished, proceed as follows: |
|
| 12 | + |
|
| 13 | +* make a volume available that holds the ``/home/hudson`` content, including the ``android-sdk-linux`` folder. This can happen, e.g., by creating a snapshot of the existing ``/home/hudson`` volume of the running "Build/Dev/Test" server, or you may search the weekly backup snapshots for the latest version to start with. Make sure to create the volume in the same availability zone (AZ) as your instance is running in. |
|
| 14 | +* Based on how full the volume already is, consider re-sizing it by creating the volume larger than the snapshot. |
|
| 15 | +* Attach it. |
|
| 16 | +* In the instance, as ``root`` user, call ``dmesg``. This will show the new volume that just got attached, as well as its partition names. |
|
| 17 | +* If you chose the volume bigger than the snapshot, use ``resize2fs`` to resize accordingly. |
|
| 18 | +* Enter an ``/etc/fstab`` entry for the volume, e.g., like this: |
|
| 19 | +``` |
|
| 20 | +UUID={the-UUID-of-the-partition-to-mount} /home/hudson ext4 defaults,noatime,commit=30 0 0 |
|
| 21 | +``` |
|
| 22 | +To find out ``{the-UUID-of-the-partition-to-mount}``, use ``blkid``. |
|
| 23 | +* Mount with ``mount -a`` |
|
| 24 | +* Adjust ownerships in case the new instance's ``hudson`` user/group IDs have changed compared to the old instance: |
|
| 19 | 25 | ``` |
| 20 | -## Path: Development/Hudson |
|
| 21 | -## Description: Configuration for the Hudson continuous build server |
|
| 22 | -## Type: string |
|
| 23 | -## Default: "/var/lib/hudson" |
|
| 24 | -## ServiceRestart: hudson |
|
| 25 | -# |
|
| 26 | -# Directory where Hudson store its configuration and working |
|
| 27 | -# files (checkouts, build reports, artifacts, ...). |
|
| 28 | -# |
|
| 29 | -HUDSON_HOME="/home/hudson/repo" |
|
| 26 | + sudo chmod -R hudson:hudson /home/hudson |
|
| 27 | +``` |
|
| 28 | +* If you'd like to keep in sync with the latest version of a still running live Hudson environment, keep copying its ``/home/hudson`` contents with ``rsync -av root@dev.internal.sapsailing.com:/home/hudson/ /home/hudson/`` until you switch |
|
| 29 | +* Ensure you have EC2 / EBS snapshot backups for the volumes by tagging them as follows: ``WeeklySailingInfrastructureBackup=Yes`` for ``/`` and ``/home/hudson``. |
|
| 30 | 30 | |
| 31 | -## Type: string |
|
| 32 | -## Default: "" |
|
| 33 | -## ServiceRestart: hudson |
|
| 34 | -# |
|
| 35 | -# Java executable to run Hudson |
|
| 36 | -# When left empty, we'll try to find the suitable Java. |
|
| 37 | -# |
|
| 31 | +## In-Place Start-Up |
|
| 38 | 32 | |
| 39 | -HUDSON_JAVA_CMD="/opt/sapjvm_8/bin/java" |
|
| 40 | -# The following line choses JavaSE-1.7 |
|
| 41 | -#HUDSON_JAVA_CMD="/opt/jdk1.7.0_02/bin/java" |
|
| 42 | -# The following line choses JavaSE-1.8 |
|
| 43 | -#HUDSON_JAVA_CMD="/opt/jdk1.8.0_20/bin/java" |
|
| 33 | +You can then either use the instance right away by starting the two essential services, as follows: |
|
| 34 | +``` |
|
| 35 | + sudo systemctl start hudson.service |
|
| 36 | + sudo systemctl start sailing.service |
|
| 37 | +``` |
|
| 38 | +## Creating an AMI to Launch |
|
| 44 | 39 | |
| 45 | -## Type: string |
|
| 46 | -## Default: "hudson" |
|
| 47 | -## ServiceRestart: hudson |
|
| 48 | -# |
|
| 49 | -# Unix user account that runs the Hudson daemon |
|
| 50 | -# Be careful when you change this, as you need to update |
|
| 51 | -# permissions of $HUDSON_HOME and /var/log/hudson. |
|
| 52 | -# |
|
| 53 | -HUDSON_USER="hudson" |
|
| 40 | +Or you stop the instance, either from within using ``shutdown -h now`` or from the AWS Console, then create an image (AMI) that you can use to create a new instance at any time. Keep in mind, though, that keeping such an image around incurs cost for the relatively large ``/home/hudson`` volume's snapshot. As the volume is part of the weekly backup strategy anyhow, and due to the fair degree of automation during producing a new version of this instance type, this may not be necessary. |
|
| 54 | 41 | |
| 55 | -## Type: string |
|
| 56 | -## Default: "-Djava.awt.headless=true" |
|
| 57 | -## ServiceRestart: hudson |
|
| 58 | -# |
|
| 59 | -# Options to pass to java when running Hudson. |
|
| 60 | -# |
|
| 61 | -HUDSON_JAVA_OPTIONS="-Djava.awt.headless=true -Xmx2G -Dhudson.slaves.ChannelPinger.pingInterval=60 -Dhudson.slaves.ChannelPinger.pingIntervalSeconds=60 -Dhudson.slaves.ChannelPinger.pingTimeoutSeconds=60" |
|
| 42 | +The AMI should be labeled accordingly, and so should the snapshots. For allowing for automated image upgrades, ensure the AMI and snapshots follow a common naming and versioning pattern. Name your AMI something like "Build/Dev/Test x.y" with x and y being major and minor version numbers, such as 2.0. Then, your snapshots shall be named "Build/Dev/Test x.y ({volume-name})" where {volume-name} can be any human-readable identifier that lets you recognize the volume. Examples for {volume-name} may be "Root" or "Home" or "HudsonHome" or similar. |
|
| 62 | 43 | |
| 63 | -## Type: integer(0:65535) |
|
| 64 | -## Default: 8080 |
|
| 65 | -## ServiceRestart: hudson |
|
| 66 | -# |
|
| 67 | -# Port Hudson is listening on. |
|
| 68 | -# |
|
| 69 | -HUDSON_PORT="8080" |
|
| 44 | +Then terminate your instance used only for image creation and launch from the AMI, then, as in "In-Place Start-Up", place the instance into the "Hudson" target group and adjust the elastic IP to point to the new instance. |
|
| 70 | 45 | |
| 71 | -## Type: integer(1:9) |
|
| 72 | -## Default: 5 |
|
| 73 | -## ServiceRestart: hudson |
|
| 74 | -# |
|
| 75 | -# Debug level for logs -- the higher the value, the more verbose. |
|
| 76 | -# 5 is INFO. |
|
| 77 | -# |
|
| 78 | -HUDSON_DEBUG_LEVEL="5" |
|
| 46 | +## Steps to Perform to Activate the New Server |
|
| 79 | 47 | |
| 80 | -## Type: yesno |
|
| 81 | -## Default: no |
|
| 82 | -## ServiceRestart: hudson |
|
| 83 | -# |
|
| 84 | -# Whether to enable access logging or not. |
|
| 85 | -# |
|
| 86 | -HUDSON_ENABLE_ACCESS_LOG="no" |
|
| 48 | +* Replace the old "Build/Test/Dev" instance in the "Hudson" and "S-DEV" and "S-DEV-m" target groups with the new one |
|
| 49 | +* Switch the elastic IP ``52.17.217.83`` to the new instance, too |
|
| 50 | +* Change the ``dev.internal.sapsailing.com`` Route53 DNS entry from the old instance's internal IP address to the new instance's internal IP address |
|
| 87 | 51 | |
| 88 | -## Type: integer |
|
| 89 | -## Default: 100 |
|
| 90 | -## ServiceRestart: hudson |
|
| 91 | -# |
|
| 92 | -# Maximum number of HTTP worker threads. |
|
| 93 | -# |
|
| 94 | -HUDSON_HANDLER_MAX="100" |
|
| 95 | 52 | |
| 96 | -## Type: integer |
|
| 97 | -## Default: 20 |
|
| 98 | -## ServiceRestart: hudson |
|
| 99 | -# |
|
| 100 | -# Maximum number of idle HTTP worker threads. |
|
| 101 | -# |
|
| 102 | -HUDSON_HANDLER_IDLE="20" |
|
| 53 | +## Testing the New and Terminating the Old Instance |
|
| 103 | 54 | |
| 104 | -## Type: string |
|
| 105 | -## Default: "" |
|
| 106 | -## ServiceRestart: hudson |
|
| 107 | -# |
|
| 108 | -# Pass arbitrary arguments to Hudson. |
|
| 109 | -# Full option list: java -jar hudson.war --help |
|
| 110 | -# |
|
| 111 | -HUDSON_ARGS="" |
|
| 112 | -``` |
|
| 55 | +After a while of testing the new environment successfully you may choose to terminate the old instance. Make sure the large ``/home/hudson`` volume is deleted with it, and it not, delete it manually. |
|
| ... | ... | \ No newline at end of file |
wiki/info/landscape/creating-ec2-image-for-mysql-from-scratch.md
| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | +# Setting up an Instance for the MySQL / MariaDB Bugzilla Database |
|
| 2 | + |
|
| 3 | +Our Bugzilla system at [bugzilla.sapsailing.com](https://bugzilla.sapsailing.com) uses a relational database to store all the bugs and issues. This used to be a MySQL database and has been migrated to MariaDB at the beginning of 2024. |
|
| 4 | + |
|
| 5 | +We don't provide a dedicated AMI for this because we don't need to scale this out or replicate this by any means. Instead, we provide a script to set this up, starting from a clean Amazon Linux 2023 instance. |
|
| 6 | + |
|
| 7 | +Launch a new instance, based on the latest Amazon Linux 2023 AMI maintained by AWS, and configure the root volume size to be, e.g., 16GB. As of this writing, the total size consumed by the database contents on disk is less than 1GB. Tag the volume with a tag key ``WeeklySailingInfrastructureBackup`` and value ``Yes`` to include it in the weekly backup schedule. |
|
| 8 | + |
|
| 9 | +When the instance has finished booting up, run the following script, passing the external IP address of the instance as mandatory argument: |
|
| 10 | +``` |
|
| 11 | + configuration/mysql_instance_setup/setup-mysql-server.sh a.b.c.d |
|
| 12 | +``` |
|
| 13 | +where ``a.b.c.d`` stands for the external IP address you have to specify. Before the IP address you may optionally specify the passwords for the ``root`` and the ``bugs`` user of the existing database to be cloned to the new instance. Provide the ``root`` password with the ``-r`` option, the ``bugs`` password with the ``-b`` option. Passwords not provided this way will be prompted for. |
|
| 14 | + |
|
| 15 | +The script will then transfer itself to the instance and execute itself there, forwarding the passwords required. On the instance, it will then establish the periodic management of the login user's ``authorized_keys`` file for all landscape managers' keys, install the packages required (in particular mariadb105-server and cronie), then run a backup on the existing ``mysql.internal.sapsailing.com`` database using the ``root`` user and its password. The ``mysqldump`` client for this is run on ``sapsailing.com``, and the result is stored in the ``/tmp`` folder on the new instance where it is then imported. The import is a bit tricky in case this is a migration from MySQL to MariaDB where the users table has become a view. Therefore, a few additional ``DROP TABLE`` and ``DROP VIEW`` commands are issued before importing the data. When the import is complete, user privileges are flushed so they match with what has been imported. The DB is then re-started in "safe" mode so that the user passwords can be adjusted, in case this was a migration from MySQL to MariaDB. Finally, the DB is restarted properly with the new user passwords. |
|
| 16 | + |
|
| 17 | +The instance then is generally available for testing. Run a few ``mysql`` commands, check out the ``bugs`` database and its contents, especially those of the ``bugs.bugs`` table. If this all looks good, switch the DNS record for ``mysql.internal.sapsailing.com`` to the private IP of the new instance. This will be used by the Bugzilla installation running on our central reverse proxy. When this is done you can consider stopping and ultimately terminating the old DB server. |
wiki/info/landscape/creating-ec2-image-for-rabbitmq-from-scratch.md
| ... | ... | @@ -0,0 +1,19 @@ |
| 1 | +# Setting up a RabbitMQ Server Instance |
|
| 2 | + |
|
| 3 | +RabbitMQ is hard to install on latest versions of Amazon Linux (e.g., 2, or 2023). Therefore, we use a latest Debian 12 default image to start with. |
|
| 4 | + |
|
| 5 | +Configure the root volume to be at least 8GB. The empty installation takes about 1.6GB, so you will have enough room for messages queued persistently. |
|
| 6 | + |
|
| 7 | +When the instance has finished booting and SSH access is possible, invoke the following script, providing the instance's external IP address as only parameter: |
|
| 8 | +``` |
|
| 9 | + configuration/rabbitmq_instance_setup/setup-rabbitmq-server.sh a.b.c.d`` |
|
| 10 | +``` |
|
| 11 | +where ``a.b.c.d`` is the external IP address of your fresh instance. |
|
| 12 | + |
|
| 13 | +The script will ensure the login user's ``authorized_keys`` are updated periodically to contain those of the landscape managers, then will install the necessary packages, particularly ``rabbitmq-server`` and, to get real log files under ``/var/log``, the ``syslog-ng`` package. It then enables the ``rabbitmq_management`` plugin, so access to the management UI becomes possible through port ``15672``. The configuration file under ``/etc/rabbitmq/rabbitmq.conf`` is patched such that guest logins are possible also from non-localhost addresses, by adding the ``loopback_users = none`` directive to the config file. It finally (re-)starts the RabbitMQ server to let these config changes take effect. |
|
| 14 | + |
|
| 15 | +Your RabbitMQ server then should be ready to handle requests. Test this by invoking the management UI, e.g., through an ssh port forward to port ``15672``. When this seems good, pick a smart time to change the DNS record for ``rabbit.internal.sapsailing.com`` because there will be a short time of interruptions on all application processes currently connected to the old RabbitMQ which you then have to stop. Those client applications will temporarily lose connection, but our replication component will re-establish these connections, using the DNS name which gets resolved again based on the DNS entry's TTL. |
|
| 16 | + |
|
| 17 | +Then associate the elastic IP ``54.76.64.42`` as the external IP of the new instance. This will let ``rabbit.sapsailing.com`` point to the public IP of the instance. |
|
| 18 | + |
|
| 19 | +Add a tag with key ``RabbitMQEndpoint`` and value ``5672``, specifying the port on which the RabbitMQ server listens. This tag can be used by our landscape automation procedures to discover the RabbitMQ default instance in the region. |
wiki/info/landscape/creating-ec2-image-for-webserver-from-scratch.md
| ... | ... | @@ -123,13 +123,13 @@ lrwxrwxrwx 1 root root 75 Oct 20 09:00 notify-operators -> /home/wiki/git |
| 123 | 123 | lrwxrwxrwx 1 root root 78 Feb 8 2021 update_authorized_keys_for_landscape_managers -> /home/wiki/gitwiki/configuration/update_authorized_keys_for_landscape_managers |
| 124 | 124 | lrwxrwxrwx 1 root root 89 Feb 8 2021 update_authorized_keys_for_landscape_managers_if_changed -> /home/wiki/gitwiki/configuration/update_authorized_keys_for_landscape_managers_if_changed |
| 125 | 125 | ``` |
| 126 | -* set up ``crontab`` for ``root`` user (remove the symbolic link to ``/home/sailing/code/configuration/crontab`` if that had been created earlier) |
|
| 126 | +* set up ``crontab`` for ``root`` user (remove the symbolic link to ``/home/sailing/code/configuration/crontab`` if that had been created earlier). Note that ``configuration/crontabs`` contains a selection of crontab files for different use cases, including the ``environments/crontab-reverse-proxy-instance``, which should be pointed to by a symbolic link in /root. |
|
| 127 | 127 | ``` |
| 128 | 128 | 0 10 1 * * export PATH=/bin:/usr/bin:/usr/local/bin; mail-events-on-my >/dev/null 2>/dev/null |
| 129 | 129 | * * * * * export PATH=/bin:/usr/bin:/usr/local/bin; sleep $(( $RANDOM * 60 / 32768 )); update_authorized_keys_for_landscape_managers_if_changed $( cat /root/ssh-key-reader.token ) https://security-service.sapsailing.com /root 2>&1 >>/var/log/sailing.err |
| 130 | 130 | 0 7 2 * * export PATH=/bin:/usr/bin:/usr/local/bin; docker exec -it registry-registry-1 registry garbage-collect /etc/docker/registry/config.yml |
| 131 | 131 | ``` |
| 132 | -* set up crontab for user `wiki` as `*/10 * * * * /home/wiki/syncgit` and make sure the script is in place |
|
| 132 | +* set up crontab for user `wiki` as a symbolic link to /configuration/crontabs/users/crontab-wiki. |
|
| 133 | 133 | * ensure that ``/var/log/old/cache/docker`` makes it across from any previous installation to the new one; it contains the docker registry contents. See in particular ``/var/log/old/cache/docker/registry/docker/registry/v2/repositories``. |
| 134 | 134 | * [install docker registry](https://wiki.sapsailing.com/wiki/info/landscape/docker-registry) so that the following containers are up and running: |
| 135 | 135 | ``` |
| ... | ... | @@ -190,9 +190,9 @@ write and quit, to install the cronjob. |
| 190 | 190 | * * * * * /home/wiki/gitwiki/configuration/switchoverArchive.sh "/etc/httpd/conf.d/000-macros.conf" 2 9 |
| 191 | 191 | ``` |
| 192 | 192 | |
| 193 | -If you want to quickly run this script, consider installing it in /usr/local/bin, via `ln -s TARGET_PATH LINK_NAME`, in that directory. |
|
| 193 | +If you want to quickly run this script, consider installing it in /usr/local/bin, via `ln -s TARGET_PATH LINK_NAME`. |
|
| 194 | 194 | |
| 195 | -## Basic setup for reverse proxy instance |
|
| 195 | +## Basic setup for disposable reverse proxy instance |
|
| 196 | 196 | |
| 197 | 197 | From a fresh amazon linux 2023 instance (HVM) install perl, httpd, mod_proxy_html, tmux, nfs-utils, git, whois and jq. Then type `amazon-linux-extras install epel`, which adds the epel repo so you can then run install apachetop. |
| 198 | 198 | Then you need to remove the automatic ec2 code which disabled root access; reconfigure the sshd_config; setup the keys update script; and initialise the crontab. Store a bearer token in the home dir. |
| ... | ... | @@ -211,7 +211,7 @@ Postmail is useful. The script for this procedure is in configuration and is tit |
| 211 | 211 | |
| 212 | 212 | Setup the logrotate target. |
| 213 | 213 | |
| 214 | -Setup the fstab (not automated). |
|
| 215 | 214 | Update amazon cli (because pricing list requires it) |
| 216 | 215 | |
| 217 | 216 | |
| 217 | + |
wiki/info/landscape/docker-registry.md
| ... | ... | @@ -104,7 +104,7 @@ This process is automated by adding the line |
| 104 | 104 | 0 7 2 * * export PATH=/bin:/usr/bin:/usr/local/bin; docker exec -it registry-registry-1 registry garbage-collect /etc/docker/registry/config.yml |
| 105 | 105 | ``` |
| 106 | 106 | |
| 107 | -to /root/crontab and running ``crontab crontab`` as the ``root`` user. See also ``crontab -l`` for whether this has already been set up. |
|
| 107 | +to /root/crontab and running ``crontab crontab`` as the ``root`` user. See also ``crontab -l`` for whether this has already been set up. This line can also be found in the `/configuration/crontabs/environments/crontab-application-server` file. |
|
| 108 | 108 | |
| 109 | 109 | If you want to delete an entire repository, e.g., because you pushed images under an incorrect repository tag, try this: |
| 110 | 110 | ``` |