diff --git a/.github/workflows/tr-ultimate-test-harness.yml b/.github/workflows/tr-ultimate-test-harness.yml new file mode 100644 index 0000000000..3a106a17b7 --- /dev/null +++ b/.github/workflows/tr-ultimate-test-harness.yml @@ -0,0 +1,212 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# https://apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +name: TR Ultimate Test Harness + +env: + DOCKER_BUILDKIT: '1' + COMPOSE_DOCKER_CLI_BUILD: '1' + +on: + push: + paths: + - .github/actions/tr-ultimate-test-harness/** + - .github/workflows/tr-ultimate-test-harness.yml + - dev/traffic_router/** + - traffic_router/** + create: + pull_request: + paths: + - .github/actions/tr-ultimate-test-harness/** + - .github/workflows/tr-ultimate-test-harness.yml + - dev/traffic_router/** + - traffic_router/** + types: [ opened, reopened, ready_for_review, synchronize ] + +jobs: + tests: + if: github.event.pull_request.draft == false + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@master + - name: Fetch environment variables + run: | + source dev/atc.dev.sh && + env | grep -e^TO_ -e^UID= >> .env + - name: Load environment + run: | + set -o pipefail && + cat infrastructure/cdn-in-a-box/variables.env .env | + sed /^#/d | + tee --append "${{ github.env }}" + - name: Install Go + uses: actions/setup-go@v2 + with: + go-version: ${{ env.GO_VERSION }} + - name: Vendor dependencies + run: go mod vendor + - name: Build docker-compose services + run: | + docker-compose build --parallel && + docker-compose pull db + - name: Cache local Maven repository + uses: actions/cache@v2 + with: + path: ${{ github.workspace }}/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + - name: Cache node modules + uses: actions/cache@v2 + with: + path: ${{ github.workspace }}/.npm + key: ${{ runner.os }}-node-modules-${{ hashFiles('traffic_portal/**/package*.json') }}- + restore-keys: | + ${{ runner.os }}-node-modules- + - name: Start the Dev CDN in a Box + run: docker-compose up -d + - name: Wait until Traffic Ops is ready to accept requests + run: | + trap 'echo "Error on line ${LINENO} of setting interface IP addresses"; exit 1' ERR + set -o errexit -o nounset -o pipefail + + # Get atc-ready function + source dev/atc.dev.sh + export -f atc-ready + + echo 'Waiting until Traffic Ops is ready to accept requests...' + if ! timeout 10m bash -c 'atc-ready -w'; then + echo 'Traffic Ops was not available within 10 minutes!' + trap - ERR + echo 'Exiting...' + exit 1 + fi + + - name: Set interface IP addresses + run: |2 + trap 'echo "Error on line ${LINENO} of setting interface IP addresses"; exit 1' ERR + # set -o xtrace + # uncomment this line ^ to check interface data + set -o errexit -o nounset -o pipefail + + source infrastructure/cdn-in-a-box/traffic_ops/to-access.sh + + declare -A service_by_hostname + service_by_hostname[trafficrouter]=trafficrouter + service_by_hostname[edge]=t3c + + for hostname in trafficrouter edge; do + container_id="$(docker-compose ps -q "${service_by_hostname[$hostname]}")" + interface="$(<<'JSON' jq + { + "mtu": 1500, + "monitor": true, + "ipAddresses": [], + "name": "eth0" + } + JSON + )" + docker_network="$(docker network inspect dev.ciab.test)" + for ip_address_field in IPv4Address IPv6Address; do + ip_address="$(<<<"$docker_network" jq -r --arg CONTAINER_ID "$container_id" --arg IP_ADDRESS_FIELD "$ip_address_field" '.[0].Containers[$CONTAINER_ID][$IP_ADDRESS_FIELD]')" + if [[ "$ip_address" == null ]]; then + echo "Could not find ${ip_address_field} for ${hostname} service!" + exit 1 + fi + interface="$(<<<"$interface" jq --arg IP_ADDRESS "$ip_address" '.ipAddresses += [{} | .address = $IP_ADDRESS | .serviceAddress = true]')" + done + + # Get server JSON + server="$(to-get "api/$TO_API_VERSION/servers?hostName=${hostname}" | jq '.response[0]')" + if [[ -z "$server" ]]; then + echo "Could not get JSON for server ${hostname}" + exit 1 + fi + + # Update server's interface with its IP addresses + server="$(<<<"$server" jq ".interfaces = [${interface}]")" + server_id="$(<<<"$server" jq .id)" + if ! to-put "api/$TO_API_VERSION/servers/${server_id}" "$server"; then + echo "Could not update server ${hostname} with ${server}" + exit 1 + fi + done + + # Snapshot + cdn_id="$(<<<"$server" jq .cdnId)" + to-put "api/$TO_API_VERSION/snapshot?cdnID=${cdn_id}" + + - name: Wait for Traffic Monitor to serve a snapshot + run: | + echo "Waiting for Traffic Monitor to serve a snapshot..." + if ! timeout 10m curl \ + --retry 99999 \ + --retry-delay 5 \ + --show-error \ + -fIso/dev/null \ + http://localhost/publish/CrConfig + then + echo "CrConfig was not available from Traffic Monitor within 10 minutes!" + trap - ERR + echo 'Exiting...' + exit 1 + fi + + - name: Compile the tests + run: go test -c ./traffic_router/ultimate-test-harness + + - name: Wait for Delivery Service to become available + id: wait-for-ds + run: | + trap 'echo "Error on line ${LINENO} of waiting for the Delivery Service to become available"; exit 1' ERR + set -o errexit -o nounset -o pipefail + + # Get atc-ready function + source dev/atc.dev.sh + export -f atc-ready + + deliveryservice=cdn.dev-ds.ciab.test + echo "Waiting for Delivery Service ${deliveryservice} to become available..." + if ! timeout 10m bash -c 'atc-ready -d'; then + echo "Delivery Service ${deliveryservice} was not available within 10 minutes!" + trap - ERR + echo 'Exiting...' + exit 1 + fi + + - name: Run Traffic Router Ultimate Test Harness HTTP tests + run: ./ultimate-test-harness.test -test.v -test.run=^TestHTTPLoad$ -http_requests_threshold=1200 + + - name: Run Traffic Router Ultimate Test Harness DNS tests + run: ./ultimate-test-harness.test -test.v -test.run=^TestDNSLoad$ -dns_requests_threshold=2500 + if: ${{ steps.wait-for-ds.outcome == 'success' && always() }} + + - name: Write Dev CDN-in-a-Box logs to files + run: | + set -o errexit -o nounset + mkdir -p dev/logs + for service in $(docker-compose ps --services); do + docker-compose logs --no-color --timestamps "$service" >"dev/logs/${service}.log" + done + if: ${{ failure() }} + - name: Upload Dev CDN-in-a-Box logs + uses: actions/upload-artifact@v2 + with: + name: dev-ciab-logs + path: dev/logs/*.log + if: ${{ failure() }} diff --git a/dev/atc.dev.sh b/dev/atc.dev.sh index 88b0575971..5faf7236f6 100644 --- a/dev/atc.dev.sh +++ b/dev/atc.dev.sh @@ -28,25 +28,38 @@ function atc-restart { } function atc-ready { - local url="https://localhost:6443/api/4.0/ping"; + local usage url="https://localhost:6443/api/4.0/ping"; + usage="$(<<-USAGE cat + Usage: ${0} [-h] [-w] + + -h, --help + print usage information and exit + -w, --wait + wait for ATC to be ready, instead of just checking if it is ready + -d, --delivery-service + wait for the ATC delivery service to be ready + USAGE + )" if [[ $# -gt 0 ]]; then case "$1" in -w|--wait) - while ! curl -skL "$url" >/dev/null 2>&1; do + until curl -skL "$url" >/dev/null 2>&1; do + sleep 1; + done + return 0;; + -d|--delivery-service) + local deliveryservice=cdn.dev-ds.ciab.test + until curl -4sfH "Host: ${deliveryservice}" localhost:3080 && + <<<"$(dig +short -4 @localhost -p 3053 "$deliveryservice")" grep -q '^[0-9.]\+$'; + do sleep 1; done return 0;; -h|--help) - echo "Usage: $0 [-h] [-w]"; - echo ""; - echo "-h, --help print usage information and exit"; - echo "-w, --wait wait for ATC to be ready, instead of just checking if it is ready"; + echo "$usage"; return 0;; *) - echo "Usage: $0 [-h] [-w]" >&2; - echo "" >&2; - echo "-h, --help print usage information and exit" >&2; - echo "-w, --wait wait for ATC to be ready, instead of just checking if it is ready" >&2; + echo "$usage" >&2; return 1;; esac fi @@ -165,20 +178,3 @@ function tm-health-client { export TO_URL="https://localhost:6443" export TO_USER="admin" export TO_PASSWORD="twelve12" - - -# On some shell/system combinations, either or both of these are available as -# shell variables but aren't exported to the execution environment. In others, -# they may just not be set. In any case, trying to set one or both of these to -# certain values - or even at all, on some systems - will fail, so we hope this -# isn't necessary. -if [[ -z "$USER" ]]; then - USER="$(id -un)"; -fi -export USER; - -if [[ -z "$UID" ]]; then - UID="$(id -u)"; -fi -export UID; - diff --git a/dev/traffic_ops/seed.psql b/dev/traffic_ops/seed.psql index 548f584231..7fb5faaf73 100644 --- a/dev/traffic_ops/seed.psql +++ b/dev/traffic_ops/seed.psql @@ -46,7 +46,7 @@ INSERT INTO snapshot ( monitoring ) VALUES ( 'dev', - '{"config":{"coveragezone.polling.url":"file:///root/go/src/github.com/apache/trafficcontrol/dev/traffic_router/czf.json","dnssec.enabled":"false","domain_name":"ciab.test","geolocation.polling.url":"file:///root/go/src/github.com/apache/trafficcontrol/traffic_router/core/src/test/resources/geo/GeoLite2-City.mmdb.gz"},"contentServers":{"edge":{"cacheGroup":"dev","fqdn":"edge.dev.ciab.test","hashCount":999,"hashId":"edge","httpsPort":443,"interfaceName":"eth0","ip":"129.0.0.2","ip6":"","locationId":"dev","port":80,"profile":"EDGE_dev","status":"REPORTED","type":"EDGE","deliveryServices":{"dev-ds":["edge.dev-ds.ciab.test"]},"routingDisabled":0}},"contentRouters":{"trafficrouter":{"fqdn":"trafficrouter.dev.ciab.test","httpsPort":443,"ip":"","ip6":"","location":"tr-cg","port":80,"profile":"CCR_dev","status":"ONLINE"}},"deliveryServices":{"dev-ds":{"anonymousBlockingEnabled":"false","coverageZoneOnly":"false","deepCachingType":"NEVER","dispersion":{"limit":1,"shuffled":"true"},"domains":["dev-ds.ciab.test"],"ecsEnabled":"false","geolocationProvider":"maxmindGeolocationService","ip6RoutingEnabled":"false","matchsets":[{"protocol":"HTTP","matchlist":[{"regex":".*\\.dev-ds\\..*","match-type":"HOST"}]}],"missLocation":{"lat":1,"long":1},"protocol":{"acceptHttps":"false","redirectToHttps":"false"},"regionalGeoBlocking":"false","routingName":"cdn","soa":{"admin":"traffic_ops","expire":"604800","minimum":"30","refresh":"28800","retry":"7200"},"sslEnabled":"false","ttls":{"A":"","AAAA":"","NS":"3600","SOA":"86400"}}},"edgeLocations":{"dev":{"latitude":1,"longitude":1,"backupLocations":{"fallbackToClosest":"true"},"localizationMethods":["GEO","CZ","DEEP_CZ"]}},"trafficRouterLocations":{"tr-cg":{"latitude":1,"longitude":1,"backupLocations":{"fallbackToClosest":"false"},"localizationMethods":["GEO","CZ","DEEP_CZ"]}},"monitors":{"trafficmonitor":{"fqdn":"trafficmonitor.dev.ciab.test","httpsPort":null,"ip":"129.0.0.1","ip6":"","location":"dev","port":80,"profile":"RASCAL_TM_dev","status":"ONLINE"}},"stats":{"CDN_name":"dev","date":1649361181,"tm_host":"trafficops","tm_user":"admin","tm_version":"development"}}', + '{"config":{"coveragezone.polling.url":"file:///go/src/github.com/apache/trafficcontrol/dev/traffic_router/czf.json","dnssec.enabled":"false","domain_name":"ciab.test","geolocation.polling.url":"file:///go/src/github.com/apache/trafficcontrol/traffic_router/core/src/test/resources/geo/GeoLite2-City.mmdb.gz"},"contentServers":{"edge":{"cacheGroup":"dev","fqdn":"edge.dev.ciab.test","hashCount":999,"hashId":"edge","httpsPort":443,"interfaceName":"eth0","ip":"129.0.0.2","ip6":"","locationId":"dev","port":80,"profile":"EDGE_dev","status":"REPORTED","type":"EDGE","deliveryServices":{"dev-ds":["edge.dev-ds.ciab.test"]},"routingDisabled":0}},"contentRouters":{"trafficrouter":{"fqdn":"trafficrouter.dev.ciab.test","httpsPort":443,"ip":"","ip6":"","location":"tr-cg","port":80,"profile":"CCR_dev","status":"ONLINE"}},"deliveryServices":{"dev-ds":{"anonymousBlockingEnabled":"false","coverageZoneOnly":"false","deepCachingType":"NEVER","dispersion":{"limit":1,"shuffled":"true"},"domains":["dev-ds.ciab.test"],"ecsEnabled":"false","geolocationProvider":"maxmindGeolocationService","ip6RoutingEnabled":"false","matchsets":[{"protocol":"HTTP","matchlist":[{"regex":".*\\.dev-ds\\..*","match-type":"HOST"}]}],"missLocation":{"lat":1,"long":1},"protocol":{"acceptHttps":"false","redirectToHttps":"false"},"regionalGeoBlocking":"false","routingName":"cdn","soa":{"admin":"traffic_ops","expire":"604800","minimum":"30","refresh":"28800","retry":"7200"},"sslEnabled":"false","ttls":{"A":"","AAAA":"","NS":"3600","SOA":"86400"}}},"edgeLocations":{"dev":{"latitude":1,"longitude":1,"backupLocations":{"fallbackToClosest":"true"},"localizationMethods":["GEO","CZ","DEEP_CZ"]}},"trafficRouterLocations":{"tr-cg":{"latitude":1,"longitude":1,"backupLocations":{"fallbackToClosest":"false"},"localizationMethods":["GEO","CZ","DEEP_CZ"]}},"monitors":{"trafficmonitor":{"fqdn":"trafficmonitor.dev.ciab.test","httpsPort":null,"ip":"129.0.0.1","ip6":"","location":"dev","port":80,"profile":"RASCAL_TM_dev","status":"ONLINE"}},"stats":{"CDN_name":"dev","date":1649361181,"tm_host":"trafficops","tm_user":"admin","tm_version":"development"}}', '{"trafficServers":[{"profile":"EDGE_dev","status":"REPORTED","port":80,"cachegroup":"dev","hostname":"edge","fqdn":"edge.dev.ciab.test","interfaces":[{"ipAddresses":[{"address":"129.0.0.2","gateway":null,"serviceAddress":true}],"maxBandwidth":null,"monitor":true,"mtu":1500,"name":"eth0"}],"type":"EDGE","hashid":"","deliveryServices":[{"xmlId":"dev-ds"}]}],"trafficMonitors":[{"profile":"RASCAL_TM_dev","status":"ONLINE","port":80,"cachegroup":"dev","hostname":"trafficmonitor","fqdn":"trafficmonitor.dev.ciab.test","ip":"129.0.0.1","ip6":""}],"cacheGroups":[{"name":"dev","coordinates":{"latitude":1,"longitude":1}},{"name":"tr-cg","coordinates":{"latitude":1,"longitude":1}}],"profiles":[{"name":"CCR_dev","type":"CCR","parameters":null},{"name":"EDGE_dev","type":"EDGE","parameters":{"health.polling.format":"stats_over_http","health.polling.url":"http://edge:8080/_stats?application=\\u0026inf.name=${interface_name}"}}],"deliveryServices":[{"xmlId":"dev-ds","totalTpsThreshold":0,"status":"REPORTED","totalKbpsThreshold":0,"type":"HTTP","topology":"","hostRegexes":[".*\\.dev-ds\\..*"]}],"config":{"health.polling.interval":6000,"heartbeat.polling.interval":3000,"peers.polling.interval":3000,"tm.polling.interval":2000},"topologies":{}}' ) ON CONFLICT DO NOTHING; @@ -105,11 +105,11 @@ INSERT INTO parameter ( ), ( 'CRConfig.json', 'geolocation.polling.url', - 'file:///root/go/src/github.com/apache/trafficcontrol/traffic_router/core/src/test/resources/geo/GeoLite2-City.mmdb.gz' + 'file:///go/src/github.com/apache/trafficcontrol/traffic_router/core/src/test/resources/geo/GeoLite2-City.mmdb.gz' ), ( 'CRConfig.json', 'coveragezone.polling.url', - 'file:///root/go/src/github.com/apache/trafficcontrol/dev/traffic_router/czf.json' + 'file:///go/src/github.com/apache/trafficcontrol/dev/traffic_router/czf.json' ), ( 'global', 'tm.url', diff --git a/dev/traffic_router/Dockerfile b/dev/traffic_router/Dockerfile index 1423202ede..c30c9bf2cc 100644 --- a/dev/traffic_router/Dockerfile +++ b/dev/traffic_router/Dockerfile @@ -13,26 +13,31 @@ # FROM alpine:latest AS trafficrouter-dev -ENV TC=/root/go/src/github.com/apache/trafficcontrol -VOLUME /root/go/src/github.com/apache/trafficcontrol -ENV JPDA_OPTS="-agentlib:jdwp=transport=dt_socket,address=*:5005,server=y,suspend=n" JAVA_HOME=/usr/lib/jvm/java-11-openjdk M2_HOME=/root/go/src/github.com/apache/trafficcontrol/.m2 CATALINA_BASE=/opt/traffic_router TRAFFIC_MONITOR_HOSTS=trafficmonitor CATALINA_OPTS=-Dlog4j.configurationFile=/opt/traffic_router/conf/log4j2.xml -EXPOSE 3053:53/tcp 3053:53/udp 3080:80 3443:443 3333:3333 2222:3443 5005:5005 +ENV TC=/go/src/github.com/apache/trafficcontrol +VOLUME "$TC" +ENV JPDA_OPTS="-agentlib:jdwp=transport=dt_socket,address=*:5005,server=y,suspend=n" \ + JAVA_HOME=/usr/lib/jvm/java-11-openjdk M2_HOME=${TC}/trafficcontrol/.m2 \ + CATALINA_BASE=/opt/traffic_router \ + TRAFFIC_MONITOR_HOSTS=trafficmonitor \ + CATALINA_OPTS=-Dlog4j.configurationFile=/opt/traffic_router/conf/log4j2.xml +EXPOSE 3053:53/tcp \ + 3053:53/udp \ + 3080:80 \ + 3443:443 \ + 3333:3333 \ + 2222:3443 \ + 5005:5005 RUN apk add --no-cache openjdk11 inotify-tools maven tomcat-native openssl && ln -s /usr/lib/jvm/java-11-openjdk/bin/jdb /bin/jdb ADD https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.43/bin/apache-tomcat-9.0.43.tar.gz /opt/tomcat.tgz -ARG USER -ARG UID -RUN adduser -H -D ${USER} -u ${UID} -RUN chown -R ${USER} /opt /usr/share/java/ /root -USER ${USER} RUN cd /opt && \ tar -xf tomcat.tgz && \ mv apache-tomcat-* tomcat && \ rm -r tomcat.tgz tomcat/webapps/* /usr/share/java/maven-3/conf/settings.xml && \ - ln -s /root/go/src/github.com/apache/trafficcontrol/dev/traffic_router /opt/traffic_router + ln -s "${TC}/dev/traffic_router" /opt/traffic_router COPY settings.xml /usr/share/java/maven-3/conf/settings.xml -CMD /root/go/src/github.com/apache/trafficcontrol/dev/traffic_router/run.sh +CMD "${TC}/dev/traffic_router/run.sh" diff --git a/dev/traffic_router/conf/log4j2.xml b/dev/traffic_router/conf/log4j2.xml index 0b3e097975..17cbf2d899 100644 --- a/dev/traffic_router/conf/log4j2.xml +++ b/dev/traffic_router/conf/log4j2.xml @@ -27,7 +27,7 @@ - + diff --git a/dev/traffic_router/run.sh b/dev/traffic_router/run.sh index 3b40c2f9e7..9ec211f188 100755 --- a/dev/traffic_router/run.sh +++ b/dev/traffic_router/run.sh @@ -18,11 +18,18 @@ set -o errexit + + + cd "$TC/traffic_router" +user=trafficrouter +uid="$(stat -c%u .)" +gid="$(stat -c%g .)" +adduser -Du"$uid" "$user" +sed -Ei "s/^(${user}:.*:)[0-9]+(:)$/\1${gid}\2/" /etc/group +chown -R "${uid}:${gid}" /opt -mvn -Dmaven.test.skip=true compile package -P \!rpm-build -chmod -R a+rw "$TC/dev/traffic_router/" +su "$user" -- /usr/bin/mvn -Dmaven.test.skip=true compile package -P \!rpm-build cd "$TC/dev/traffic_router" - -/opt/tomcat/bin/catalina.sh jpda run +exec su "$user" -- /opt/tomcat/bin/catalina.sh jpda run diff --git a/dev/traffic_router/settings.xml b/dev/traffic_router/settings.xml index ad86e7e4b9..3784210481 100644 --- a/dev/traffic_router/settings.xml +++ b/dev/traffic_router/settings.xml @@ -17,5 +17,5 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd" > - /root/go/src/github.com/apache/trafficcontrol/.m2/repository/ + /go/src/github.com/apache/trafficcontrol/.m2/repository/ diff --git a/docker-compose.yml b/docker-compose.yml index 639f7307c7..cb5ce192c2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,7 +16,7 @@ # under the License. --- -version: '3.5' +version: '3.8' services: trafficops: @@ -59,6 +59,7 @@ services: - 444:443 volumes: - .:/root/go/src/github.com/apache/trafficcontrol + - ./.npm:/root/.npm tpv2: build: @@ -112,9 +113,6 @@ services: trafficrouter: build: context: dev/traffic_router - args: - - USER=${USER:-atc} - - UID=${UID:-1000} depends_on: - trafficmonitor hostname: trafficrouter @@ -130,8 +128,13 @@ services: - 2222:3443 - 5005:5005 volumes: - - .:/root/go/src/github.com/apache/trafficcontrol + - .:/go/src/github.com/apache/trafficcontrol networks: ciab: name: dev.ciab.test driver: bridge + enable_ipv6: true + ipam: + driver: default + config: + - subnet: 2001:3984:3989::/64 diff --git a/infrastructure/cdn-in-a-box/traffic_ops/to-access.sh b/infrastructure/cdn-in-a-box/traffic_ops/to-access.sh index cfd539dbf3..1863b2d58e 100755 --- a/infrastructure/cdn-in-a-box/traffic_ops/to-access.sh +++ b/infrastructure/cdn-in-a-box/traffic_ops/to-access.sh @@ -109,29 +109,29 @@ to-post() { local t local data if [[ -z "$2" ]]; then - data="" + data=() elif [[ -f "$2" ]]; then - data="--data @$2" + data=(--data "@${2}") else t=$(mktemp) - echo $2 >$t - data="--data @$t" + echo "$2" >$t + data=(--data "@${t}") fi to-auth && \ - curl $CURLAUTH $CURLOPTS -H 'Content-Type: application/json;charset=UTF-8' --cookie "$COOKIEJAR" -X POST $data "$TO_URL/$1" + curl $CURLAUTH $CURLOPTS -H 'Content-Type: application/json;charset=UTF-8' --cookie "$COOKIEJAR" -X POST "${data[@]}" "$TO_URL/$1" [[ -n $t ]] && rm -f "$t" } to-put() { if [[ $# -lt 2 || -z "$2" ]]; then - data="" + data=() elif [[ -f "$2" ]]; then - data="--data @$2" + data=(--data "@${2}") else - data="--data $2" + data=(--data "${2}") fi to-auth && \ - curl $CURLAUTH $CURLOPTS --cookie "$COOKIEJAR" -X PUT $data "$TO_URL/$1" + curl $CURLAUTH $CURLOPTS --cookie "$COOKIEJAR" -X PUT "${data[@]}" "$TO_URL/$1" } to-delete() {