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">&#x25BC;</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() + "&regattaName="
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&regattaName="
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&regattaName="
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
```