zrhoffman commented on a change in pull request #3707:
URL: https://github.com/apache/trafficcontrol/pull/3707#discussion_r495219828
##########
File path: traffic_monitor/variables.env
##########
@@ -0,0 +1,22 @@
+# environment variables for the Traffic Monitor Docker Compose files
Review comment:
This file needs an Apache license header
##########
File path: traffic_monitor/tests/integration/traffic_monitor_test.go
##########
@@ -0,0 +1,125 @@
+package integration
+
+/*
+
+ Licensed 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
+
+ http://www.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.
+*/
+
+import (
+ // "database/sql"
+ "flag"
+ "fmt"
+ "net/http"
+ "os"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/apache/trafficcontrol/lib/go-log"
+
"github.com/apache/trafficcontrol/traffic_monitor/tests/integration/config"
+ "github.com/apache/trafficcontrol/traffic_monitor/tmclient"
+)
+
+var Config config.Config
+var TMClient *tmclient.TMClient
+
+func TestMain(m *testing.M) {
+ var err error
+ configFileName := flag.String("cfg", "traffic-monitor-test.conf", "The
config file path")
+ flag.Parse()
+
+ if Config, err = config.LoadConfig(*configFileName); err != nil {
+ fmt.Printf("Error Loading Config %v %v\n", Config, err)
+ os.Exit(1)
+ }
+
+ if err = log.InitCfg(Config); err != nil {
+ fmt.Printf("Error initializing loggers: %v\n", err)
+ os.Exit(1)
+ }
+
+ log.Infof(`Using Config values:
+ TM Config File: %s
+ TM URL: %s
+ TM Session Timeout: %ds
+`, *configFileName, Config.TrafficMonitor.URL,
Config.Default.Session.TimeoutInSecs)
+
+ // //Load the test data
+ // LoadFixtures(*tcFixturesFileName)
+
+ // var db *sql.DB
+ // db, err = OpenConnection()
+ // if err != nil {
+ // fmt.Printf("\nError opening connection to %s - %s, %v\n",
Config.TrafficOps.URL, Config.TrafficOpsDB.User, err)
+ // os.Exit(1)
+ // }
+ // defer db.Close()
+
+ // err = Teardown(db)
+ // if err != nil {
+ // fmt.Printf("\nError tearingdown data %s - %s, %v\n",
Config.TrafficOps.URL, Config.TrafficOpsDB.User, err)
+ // os.Exit(1)
+ // }
+
+ // err = SetupTestData(db)
+ // if err != nil {
+ // fmt.Printf("\nError setting up data %s - %s, %v\n",
Config.TrafficOps.URL, Config.TrafficOpsDB.User, err)
+ // os.Exit(1)
+ // }
Review comment:
This is all commented out because it's still *todo*, right? Could we get
a `TODO` comment about this code block?
##########
File path: traffic_monitor/tools/testcaches/fakesrvr/cmd.go
##########
@@ -0,0 +1,220 @@
+package fakesrvr
+
+/*
+ * 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
+ *
+ * http://www.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.
+ */
+
+import (
+ "net/http"
+ "strconv"
+ "strings"
+ "sync/atomic"
+ "unsafe"
+
+
"github.com/apache/trafficcontrol/traffic_monitor/tools/testcaches/fakesrvrdata"
+)
+
+type CmdFunc = func(http.ResponseWriter, *http.Request, fakesrvrdata.Ths)
+
+var cmds = map[string]CmdFunc{
+ "setstat": cmdSetStat,
+ "setdelay": cmdSetDelay,
+ "setsystem": cmdSetSystem,
+}
+
+// cmdSetStat sets the rate of the given stat increase for the given remap.
+//
+// query parameters:
+// remap: string; required; the full name of the remap whose kbps to set.
+// stat: string; required; the stat to set (in_bytes, out_bytes, status_2xx,
status_3xx, status_4xx, status_5xx).
+// min: unsigned integer; required; new minimum of kbps increase of
InBytes stat for the given remap.
+// max: unsigned integer; required; new maximum of kbps increase of
InBytes stat for the given remap.
+//
+func cmdSetStat(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs
fakesrvrdata.Ths) {
+ urlQry := r.URL.Query()
+
+ newMinStr := urlQry.Get("min")
+ newMin, err := strconv.ParseUint(newMinStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'min': must be a
positive integer: " + err.Error() + "\n"))
+ return
+ }
+
+ newMaxStr := urlQry.Get("max")
+ newMax, err := strconv.ParseUint(newMaxStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'max': must be a
positive integer: " + err.Error() + "\n"))
+ return
+ }
+
+ remap := urlQry.Get("remap")
+ if remap == "" {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("missing query parameter 'remap': must specify a
remap to set\n"))
+ return
+ }
+
+ stat := urlQry.Get("stat")
+
+ validStats := map[string]struct{}{
+ "in_bytes": {},
+ "out_bytes": {},
+ "status_2xx": {},
+ "status_3xx": {},
+ "status_4xx": {},
+ "status_5xx": {},
+ }
+
+ if _, ok := validStats[stat]; !ok {
+ w.WriteHeader(http.StatusBadRequest)
+ statNames := []string{}
+ for statName, _ := range validStats {
+ statNames = append(statNames, statName)
+ }
+ w.Write([]byte("error with query parameter 'stat' '" + stat +
"': not found. Valid stats are: [" + strings.Join(statNames, ",") + "\n"))
+ return
+ }
+
+ srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get())
+ if _, ok := srvr.ATS.Remaps[remap]; !ok {
+ w.WriteHeader(http.StatusBadRequest)
+ remapNames := []string{}
+ for remapName, _ := range srvr.ATS.Remaps {
+ remapNames = append(remapNames, remapName)
+ }
+ w.Write([]byte("error with query parameter 'remap' '" + remap +
"': not found. Valid remaps are: [" + strings.Join(remapNames, ",") + "\n"))
+ return
+ }
+
+ incs := <-fakeSrvrDataThs.GetIncrementsChan
+ inc := incs[remap]
+
+ switch stat {
+ case "in_bytes":
+ inc.Min.InBytes = newMin
+ inc.Max.InBytes = newMax
+ case "out_bytes":
+ inc.Min.OutBytes = newMin
+ inc.Max.OutBytes = newMax
+ case "status_2xx":
+ inc.Min.Status2xx = newMin
+ inc.Max.Status2xx = newMax
+ case "status_3xx":
+ inc.Min.Status3xx = newMin
+ inc.Max.Status3xx = newMax
+ case "status_4xx":
+ inc.Min.Status4xx = newMin
+ inc.Max.Status4xx = newMax
+ case "status_5xx":
+ inc.Min.Status5xx = newMin
+ inc.Max.Status5xx = newMax
+ default:
+ panic("unknown stat; should never happen")
+ }
+
+ fakeSrvrDataThs.IncrementChan <- fakesrvrdata.IncrementChanT{RemapName:
remap, BytesPerSec: inc}
+
+ w.WriteHeader(http.StatusNoContent)
+}
+
+func cmdSetDelay(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs
fakesrvrdata.Ths) {
+ urlQry := r.URL.Query()
+
+ newMinStr := urlQry.Get("min")
+ newMin, err := strconv.ParseUint(newMinStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'min': must be a
non-negative integer: " + err.Error() + "\n"))
+ return
+ }
+
+ newMaxStr := urlQry.Get("max")
+ newMax, err := strconv.ParseUint(newMaxStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'max': must be a
non-negative integer: " + err.Error() + "\n"))
+ return
+ }
+
+ newMinMax := fakesrvrdata.MinMaxUint64{Min: newMin, Max: newMax}
+ newMinMaxPtr := &newMinMax
+
+ p := (unsafe.Pointer)(newMinMaxPtr)
+ atomic.StorePointer(fakeSrvrDataThs.DelayMS, p)
+ w.WriteHeader(http.StatusNoContent)
+}
+
+func cmdSetSystem(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs
fakesrvrdata.Ths) {
+ urlQry := r.URL.Query()
+
+ if newSpeedStr := urlQry.Get("speed"); newSpeedStr != "" {
+ newSpeed, err := strconv.ParseUint(newSpeedStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'speed':
must be a non-negative integer: " + err.Error() + "\n"))
+ return
+ }
+
+ srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get())
+ srvr.System.Speed = int(newSpeed)
+ fakeSrvrDataThs.Set(fakesrvrdata.ThsT(srvr))
+ }
+
+ if newLoadAvg1MStr := urlQry.Get("loadavg1m"); newLoadAvg1MStr != "" {
+ newLoadAvg1M, err := strconv.ParseFloat(newLoadAvg1MStr, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter
'loadavg1m': must be a number: " + err.Error() + "\n"))
+ return
+ }
+
+ srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get())
+ srvr.System.ProcLoadAvg.CPU1m = newLoadAvg1M
+ fakeSrvrDataThs.Set(fakesrvrdata.ThsT(srvr))
Review comment:
This too can be
```go
fakeSrvrDataThs.Set(srvr)
```
##########
File path: traffic_monitor/tests/integration/Dockerfile_run.sh
##########
@@ -0,0 +1,394 @@
+#!/usr/bin/env bash
+# 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
+#
+# http://www.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.
+
+# The following environment variables must be set (ordinarily by `docker run
-e` arguments):
+# TO_URI
+# TO_USER
+# TO_PASS
+# CDN
+
+# Check that env vars are set
+envvars=( TESTTO_URI TESTTO_PORT TESTCACHES_URI TESTCACHES_PORT_START TM_URI )
+for v in $envvars
+do
+ if [[ -z $$v ]]; then echo "$v is unset"; exit 1; fi
+done
+
+
+CFG_FILE=/traffic-monitor-integration-test.cfg
+
+start() {
+ printf "DEBUG traffic_monitor_integration starting\n"
+
+ exec /traffic_monitor_integration_test -test.v -cfg $CFG_FILE
+}
+
+init() {
+ wait_for_to
+
+ curl -Lvsk ${TESTTO_URI}/api/1.2/cdns/fake/snapshot -X POST -d '
+{
+ "config": {
+ "api.cache-control.max_age": "30",
+ "consistent.dns.routing": "true",
+ "coveragezone.polling.interval": "30",
+ "coveragezone.polling.url": "30",
+ "dnssec.dynamic.response.expiration": "60",
+ "dnssec.enabled": "false",
+ "domain_name": "monitor-integration.test",
+ "federationmapping.polling.interval": "60",
+ "federationmapping.polling.url": "foo",
+ "geolocation.polling.interval": "30",
+ "geolocation.polling.url": "foo",
+ "keystore.maintenance.interval": "30",
+ "neustar.polling.interval": "30",
+ "neustar.polling.url": "foo",
+ "soa": {
+
+ },
+ "dnssec.inception": "0",
+ "ttls": {
+ "admin": "30",
+ "expire": "30",
+ "minimum": "30",
+ "refresh": "30",
+ "retry": "30"
+ },
+ "weight": "1",
+ "zonemanager.cache.maintenance.interval": "30",
+ "zonemanager.threadpool.scale": "1"
+ },
+ "contentServers": {
+ "server0": {
+ "cacheGroup": "cg0",
+ "profile": "Edge0",
+ "fqdn": "server0.monitor-integration.test",
+ "hashCount": 1,
+ "hashId": "server0",
+ "httpsPort" : null,
+ "ip": "testcaches",
+ "ip6": null,
+ "locationId": "",
+ "port" : 30000,
+ "status": "REPORTED",
+ "type": "EDGE",
+ "deliveryServices": {"ds0":["ds0.monitor-integration.test"]},
+ "routingDisabled": 0
+ },
+ "server1": {
+ "cacheGroup": "cg0",
+ "profile": "Edge0",
+ "fqdn": "server1.monitor-integration.test",
+ "hashCount": 1,
+ "hashId": "server1",
+ "httpsPort" : null,
+ "ip": "testcaches",
+ "ip6": null,
+ "locationId": "",
+ "port" : 30001,
+ "status": "REPORTED",
+ "type": "EDGE",
+ "deliveryServices": {"ds0":["ds0.monitor-integration.test"]},
+ "routingDisabled": 0
+ }
+ },
+ "deliveryServices": {
+ "ds0": {
+ "anonymousBlockingEnabled": false,
+ "consistentHashQueryParams": [],
+ "consistentHashRegex": "",
+ "coverageZoneOnly": false,
+ "dispersion": {
+ "limit": 1,
+ "shuffled": false
+ },
+ "domains": ["ds0.monitor-integration.test"],
+ "geolocationProvider": null,
+ "matchsets": [
+ {
+ "protocol": "HTTP",
+ "matchlist": [
+ {
+ "regex": "\\.*ds0\\.*",
+ "match-type": "regex"
+ }
+ ]
+ }
+ ],
+ "missLocation": {"lat": 0, "lon": 0},
+ "protocol": {
+ "acceptHttp": true,
+ "acceptHttps": false,
+ "redirectToHttps": false
+ },
+ "regionalGeoBlocking": "false",
+ "responseHeaders": {},
+ "requestHeaders": [],
+ "soa": {
+ "admin": "60",
+ "expire": "60",
+ "minimum": "60",
+ "refresh": "60",
+ "retry": "60"
+ },
+ "sslEnabled": false,
+ "ttl": 60,
+ "ttls": {
+ "A": "60",
+ "AAAA": "60",
+ "DNSKEY": "60",
+ "DS": "60",
+ "NS": "60",
+ "SOA": "60"
+ },
+ "maxDnsIpsForLocation": 3,
+ "ip6RoutingEnabled": false,
+ "routingName": "ccr",
+ "bypassDestination": null,
+ "deepCachingType": null,
+ "geoEnabled": false,
+ "geoLimitRedirectURL": null,
+ "staticDnsEntries": []
+ }
+ },
+ "edgeLocations": {
+ "cg0": {"latitude":0, "longitude":0}
+ },
+ "trafficRouterLocations": {
+ "tr0": {"latitude":0, "longitude":0}
+ },
+ "monitors": {
+ "trafficmonitor": {
+ "fqdn": "trafficmonitor.monitor-integration.test",
+ "httpsPort": null,
+ "ip": "trafficmonitor",
+ "ip6": null,
+ "location": "cg0",
+ "port": 80,
+ "profile": "Monitor0",
+ "status": "REPORTED"
+ }
+ },
+ "stats": {
+ "CDN_name": "fake",
+ "date": 1561000000,
+ "tm_host": "testto",
+ "tm_path": "/fake",
+ "tm_user": "fake",
+ "tm_version": "integrationtest/0.fake"
+ }
+}
+'
+
+ curl -Lvsk ${TESTTO_URI}/api/1.2/cdns/fake/configs/monitoring.json -X
POST -d '
+{
+ "trafficServers": [
+ {
+ "profile": "Edge0",
+ "ip": "testcaches",
+ "status": "REPORTED",
+ "cacheGroup": "cg0",
+ "ip6": null,
+ "port": 30000,
+ "httpsPort": null,
+ "hostName": "server0",
+ "fqdn": "server0.monitor-integration.test",
+ "interfaceName": "bond0",
+ "type": "EDGE",
+ "hashId": "server0",
+ "deliveryServices": {"ds0":["ds0.monitor-integration.test"]}
+ },
+ {
+ "profile": "Edge0",
+ "ip": "testcaches",
+ "status": "REPORTED",
+ "cacheGroup": "cg0",
+ "ip6": null,
+ "port": 30001,
+ "httpsPort": null,
+ "hostName": "server1",
+ "fqdn": "server1.monitor-integration.test",
+ "interfaceName": "bond0",
+ "type": "EDGE",
+ "hashId": "server1",
+ "deliveryServices": {"ds0":["ds0.monitor-integration.test"]}
+ }
+ ],
+ "cacheGroups": [
+ {
+ "cg0": {
+ "name": "cg0",
+ "coordinates": {"latitude": 0, "longitude": 0}
+ }
+ }
+ ],
+ "config": {
+ "peers.polling.interval": 30,
+ "health.polling.interval": 2000,
+ "heartbeat.polling.interval": 2000,
+ "tm.polling.interval": 30
+ },
+ "trafficMonitors": [
+ {
+ "port": 80,
+ "ip6": "",
+ "ip": "trafficmonitor",
+ "hostName": "trafficmonitor",
+ "fqdn": "trafficmonitor.traffic-monitor-integration.test",
+ "profile": "Monitor0",
+ "location": "cg0",
+ "status": "REPORTED"
+ }
+ ],
+ "deliveryServices": [
+ {
+ "xmlId": "ds0",
+ "TotalTpsThreshold": 1000000,
+ "status": "Available",
+ "TotalKbpsThreshold": 10000000
+ }
+ ],
+ "profiles": [
+ {
+ "parameters": {
+ "health.connection.timeout": 10,
+ "health.polling.url":
"http://${hostname}/_astats?application=plugin.remap",
+ "health.polling.format": "",
+ "health.polling.type": "",
+ "history.count": 0,
+ "MinFreeKbps": 20000,
+ "health_threshold": {}
+ },
+ "name": "Edge0",
+ "type": "EDGE"
+ },
+ {
+ "parameters": {
+ "health.connection.timeout": 10,
+ "health.polling.url": "",
+ "health.polling.format": "",
+ "health.polling.type": "",
+ "history.count": 5,
+ "MinFreeKbps": 20000,
+ "health_threshold": {}
+ },
+ "name": "Monitor0",
+ "type": "RASCAL"
+ }
+ ]
+}
+'
+
+ curl -Lvsk ${TESTTO_URI}/api/1.2/servers -X POST -d '
+[
+ {
+ "cachegroup": "foo",
+ "cachegroupId": 0,
+ "cdnId": 1,
+ "cdnName": "fake",
+ "deliveryServices": null,
+ "fqdn": "trafficmonitor.traffic-monitor-integration.test",
+ "guid": "foo",
+ "hostName": "trafficmonitor",
+ "httpsPort": null,
+ "id": 1,
+ "iloIpAddress": null,
+ "iloIpGateway": null,
+ "iloIpNetmask": null,
+ "iloPassword": null,
+ "iloUsername": null,
+ "interfaceMtu": null,
+ "interfaceName": "bond0",
+ "ip6Address": null,
+ "ip6Gateway": null,
+ "ipAddress": "trafficmonitor",
+ "ipGateway": "192.0.0.1",
+ "ipNetmask": "255.255.255.0",
Review comment:
After #4700, this part looks like
```json
"interfaces": [
{
"ipAddresses": [
{
"address": "172.16.239.6",
"gateway": "172.16.239.1",
"serviceAddress": true
},
{
"address": "fc01:9400:1000:8::6",
"gateway": "fc01:9400:1000:8::1",
"serviceAddress": true
}
],
"maxBandwidth": null,
"monitor": true,
"mtu": 1500,
"name": "eth0"
}
],
```
##########
File path: traffic_monitor/tools/testcaches/fakesrvr/cmd.go
##########
@@ -0,0 +1,220 @@
+package fakesrvr
+
+/*
+ * 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
+ *
+ * http://www.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.
+ */
+
+import (
+ "net/http"
+ "strconv"
+ "strings"
+ "sync/atomic"
+ "unsafe"
+
+
"github.com/apache/trafficcontrol/traffic_monitor/tools/testcaches/fakesrvrdata"
+)
+
+type CmdFunc = func(http.ResponseWriter, *http.Request, fakesrvrdata.Ths)
+
+var cmds = map[string]CmdFunc{
+ "setstat": cmdSetStat,
+ "setdelay": cmdSetDelay,
+ "setsystem": cmdSetSystem,
+}
+
+// cmdSetStat sets the rate of the given stat increase for the given remap.
+//
+// query parameters:
+// remap: string; required; the full name of the remap whose kbps to set.
+// stat: string; required; the stat to set (in_bytes, out_bytes, status_2xx,
status_3xx, status_4xx, status_5xx).
+// min: unsigned integer; required; new minimum of kbps increase of
InBytes stat for the given remap.
+// max: unsigned integer; required; new maximum of kbps increase of
InBytes stat for the given remap.
+//
+func cmdSetStat(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs
fakesrvrdata.Ths) {
+ urlQry := r.URL.Query()
+
+ newMinStr := urlQry.Get("min")
+ newMin, err := strconv.ParseUint(newMinStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'min': must be a
positive integer: " + err.Error() + "\n"))
+ return
+ }
+
+ newMaxStr := urlQry.Get("max")
+ newMax, err := strconv.ParseUint(newMaxStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'max': must be a
positive integer: " + err.Error() + "\n"))
+ return
+ }
+
+ remap := urlQry.Get("remap")
+ if remap == "" {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("missing query parameter 'remap': must specify a
remap to set\n"))
+ return
+ }
+
+ stat := urlQry.Get("stat")
+
+ validStats := map[string]struct{}{
+ "in_bytes": {},
+ "out_bytes": {},
+ "status_2xx": {},
+ "status_3xx": {},
+ "status_4xx": {},
+ "status_5xx": {},
+ }
+
+ if _, ok := validStats[stat]; !ok {
+ w.WriteHeader(http.StatusBadRequest)
+ statNames := []string{}
+ for statName, _ := range validStats {
+ statNames = append(statNames, statName)
+ }
+ w.Write([]byte("error with query parameter 'stat' '" + stat +
"': not found. Valid stats are: [" + strings.Join(statNames, ",") + "\n"))
+ return
+ }
+
+ srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get())
+ if _, ok := srvr.ATS.Remaps[remap]; !ok {
+ w.WriteHeader(http.StatusBadRequest)
+ remapNames := []string{}
+ for remapName, _ := range srvr.ATS.Remaps {
+ remapNames = append(remapNames, remapName)
+ }
+ w.Write([]byte("error with query parameter 'remap' '" + remap +
"': not found. Valid remaps are: [" + strings.Join(remapNames, ",") + "\n"))
+ return
+ }
+
+ incs := <-fakeSrvrDataThs.GetIncrementsChan
+ inc := incs[remap]
+
+ switch stat {
+ case "in_bytes":
+ inc.Min.InBytes = newMin
+ inc.Max.InBytes = newMax
+ case "out_bytes":
+ inc.Min.OutBytes = newMin
+ inc.Max.OutBytes = newMax
+ case "status_2xx":
+ inc.Min.Status2xx = newMin
+ inc.Max.Status2xx = newMax
+ case "status_3xx":
+ inc.Min.Status3xx = newMin
+ inc.Max.Status3xx = newMax
+ case "status_4xx":
+ inc.Min.Status4xx = newMin
+ inc.Max.Status4xx = newMax
+ case "status_5xx":
+ inc.Min.Status5xx = newMin
+ inc.Max.Status5xx = newMax
+ default:
+ panic("unknown stat; should never happen")
+ }
+
+ fakeSrvrDataThs.IncrementChan <- fakesrvrdata.IncrementChanT{RemapName:
remap, BytesPerSec: inc}
+
+ w.WriteHeader(http.StatusNoContent)
+}
+
+func cmdSetDelay(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs
fakesrvrdata.Ths) {
+ urlQry := r.URL.Query()
+
+ newMinStr := urlQry.Get("min")
+ newMin, err := strconv.ParseUint(newMinStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'min': must be a
non-negative integer: " + err.Error() + "\n"))
+ return
+ }
+
+ newMaxStr := urlQry.Get("max")
+ newMax, err := strconv.ParseUint(newMaxStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'max': must be a
non-negative integer: " + err.Error() + "\n"))
+ return
+ }
+
+ newMinMax := fakesrvrdata.MinMaxUint64{Min: newMin, Max: newMax}
+ newMinMaxPtr := &newMinMax
+
+ p := (unsafe.Pointer)(newMinMaxPtr)
+ atomic.StorePointer(fakeSrvrDataThs.DelayMS, p)
+ w.WriteHeader(http.StatusNoContent)
+}
+
+func cmdSetSystem(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs
fakesrvrdata.Ths) {
+ urlQry := r.URL.Query()
+
+ if newSpeedStr := urlQry.Get("speed"); newSpeedStr != "" {
+ newSpeed, err := strconv.ParseUint(newSpeedStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'speed':
must be a non-negative integer: " + err.Error() + "\n"))
+ return
+ }
+
+ srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get())
+ srvr.System.Speed = int(newSpeed)
+ fakeSrvrDataThs.Set(fakesrvrdata.ThsT(srvr))
Review comment:
This can be just
```go
fakeSrvrDataThs.Set(srvr)
```
##########
File path: traffic_monitor/tests/integration/config/config.go
##########
@@ -0,0 +1,157 @@
+/*
+ * 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
+ *
+ * http://www.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.
+ */
+
+package config
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "reflect"
+
+ "github.com/apache/trafficcontrol/lib/go-log"
+ "github.com/kelseyhightower/envconfig"
+)
+
+// Config reflects the structure of the test-to-api.conf file
+type Config struct {
+ TrafficMonitor TrafficMonitor `json:"trafficMonitor"`
+ Default Default `json:"default"`
+}
+
+// TrafficMonitor is the monitor config section.
+type TrafficMonitor struct {
+ // URL points to the Traffic Monitor instance being tested
+ URL string `json:"url" envconfig:"TM_URL"`
+}
+
+// Default - config section
+type Default struct {
+ Session Session `json:"session"`
+ Log Locations `json:"logLocations"`
+}
+
+// Session - config section
+type Session struct {
+ TimeoutInSecs int `json:"timeoutInSecs"
envconfig:"SESSION_TIMEOUT_IN_SECS"`
+}
+
+// Locations - reflects the structure of the database.conf file
+type Locations struct {
+ Debug string `json:"debug"`
+ Event string `json:"event"`
+ Error string `json:"error"`
+ Info string `json:"info"`
+ Warning string `json:"warning"`
+}
+
+// LoadConfig - reads the config file into the Config struct
+func LoadConfig(confPath string) (Config, error) {
+ var cfg Config
+
+ if _, err := os.Stat(confPath); !os.IsNotExist(err) {
+ confBytes, err := ioutil.ReadFile(confPath)
+ if err != nil {
+ return Config{}, fmt.Errorf("Reading CDN conf '%s':
%v", confPath, err)
+ }
+
+ err = json.Unmarshal(confBytes, &cfg)
+ if err != nil {
+ return Config{}, fmt.Errorf("unmarshalling '%s': %v",
confPath, err)
+ }
+ }
+ errs := validate(confPath, cfg)
+ if len(errs) > 0 {
+ fmt.Printf("configuration error:\n")
+ for _, e := range errs {
+ fmt.Printf("%v\n", e)
+ }
+ os.Exit(0)
+ }
+ err := envconfig.Process("traffic-ops-client-tests", &cfg)
+ if err != nil {
+ fmt.Errorf("cannot parse config: %v\n", err)
Review comment:
Is this error meant to be printed?
##########
File path: traffic_monitor/tools/testcaches/fakesrvr/server.go
##########
@@ -40,6 +43,20 @@ func reqIsApplicationSystem(r *http.Request) bool {
func astatsHandler(fakeSrvrDataThs fakesrvrdata.Ths) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get())
+
+ delayMSPtr :=
(*fakesrvrdata.MinMaxUint64)(atomic.LoadPointer(fakeSrvrDataThs.DelayMS))
+ minDelayMS := delayMSPtr.Min
+ maxDelayMS := delayMSPtr.Max
+
+ if maxDelayMS != 0 {
+ delayMS := minDelayMS
+ if minDelayMS != maxDelayMS {
+ delayMS += uint64(rand.Int63n(int64((maxDelayMS
- minDelayMS))))
Review comment:
Redundant parenthesis here
##########
File path: traffic_monitor/tests/integration/README.md
##########
@@ -0,0 +1,12 @@
+# Traffic Monitor Integration Test Framework
+
+## Running
+
+From the `trafficcontrol/traffic_monitor` directory:
+
+```
+(cd tools/testto && go build)
+(cd tools/testcaches && go build)
+(cd tests/integration && go test -c -o traffic_monitor_integration_test)
Review comment:
Instead of `tests/integration`, could you make the directory name
`tests/_integration` so that go testing `./...` does not test this directory?
##########
File path: traffic_monitor/tests/integration/config/config.go
##########
@@ -0,0 +1,157 @@
+/*
+ * 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
+ *
+ * http://www.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.
+ */
+
+package config
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "reflect"
+
+ "github.com/apache/trafficcontrol/lib/go-log"
+ "github.com/kelseyhightower/envconfig"
+)
+
+// Config reflects the structure of the test-to-api.conf file
+type Config struct {
+ TrafficMonitor TrafficMonitor `json:"trafficMonitor"`
+ Default Default `json:"default"`
+}
+
+// TrafficMonitor is the monitor config section.
+type TrafficMonitor struct {
+ // URL points to the Traffic Monitor instance being tested
+ URL string `json:"url" envconfig:"TM_URL"`
+}
+
+// Default - config section
+type Default struct {
+ Session Session `json:"session"`
+ Log Locations `json:"logLocations"`
+}
+
+// Session - config section
+type Session struct {
+ TimeoutInSecs int `json:"timeoutInSecs"
envconfig:"SESSION_TIMEOUT_IN_SECS"`
+}
+
+// Locations - reflects the structure of the database.conf file
+type Locations struct {
+ Debug string `json:"debug"`
+ Event string `json:"event"`
+ Error string `json:"error"`
+ Info string `json:"info"`
+ Warning string `json:"warning"`
+}
+
+// LoadConfig - reads the config file into the Config struct
+func LoadConfig(confPath string) (Config, error) {
+ var cfg Config
+
+ if _, err := os.Stat(confPath); !os.IsNotExist(err) {
+ confBytes, err := ioutil.ReadFile(confPath)
+ if err != nil {
+ return Config{}, fmt.Errorf("Reading CDN conf '%s':
%v", confPath, err)
Review comment:
*Reading* should not be capitalized
##########
File path: traffic_monitor/tests/integration/README.md
##########
@@ -0,0 +1,12 @@
+# Traffic Monitor Integration Test Framework
+
+## Running
+
+From the `trafficcontrol/traffic_monitor` directory:
+
+```
+(cd tools/testto && go build)
+(cd tools/testcaches && go build)
+(cd tests/integration && go test -c -o traffic_monitor_integration_test)
Review comment:
When I run the docker-compose command to start the test,
```shell
docker-compose -p tmi --project-directory . -f
tests/integration/docker-compose.yml run tmintegrationtest
```
I indefinitely get
```
Waiting for Traffic Ops to return a 200 OK
```
Looking at the `testto` container, it says
```logs
[user@computer traffic_monitor]$ 3 tmi --project-directory . -f
tests/integration/docker-compose.yml logs -f testto
Attaching to tmi_testto_1
testto_1 | testto: /lib64/libc.so.6: version `GLIBC_2.32' not
found (required by testto)
tmi_testto_1 exited with code 1
```
So the instructions should have you build with `CGO_ENABLED=0` (and
`GOOS=linux`).
##########
File path: traffic_monitor/tools/testcaches/fakesrvr/cmd.go
##########
@@ -0,0 +1,220 @@
+package fakesrvr
+
+/*
+ * 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
+ *
+ * http://www.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.
+ */
+
+import (
+ "net/http"
+ "strconv"
+ "strings"
+ "sync/atomic"
+ "unsafe"
+
+
"github.com/apache/trafficcontrol/traffic_monitor/tools/testcaches/fakesrvrdata"
+)
+
+type CmdFunc = func(http.ResponseWriter, *http.Request, fakesrvrdata.Ths)
+
+var cmds = map[string]CmdFunc{
+ "setstat": cmdSetStat,
+ "setdelay": cmdSetDelay,
+ "setsystem": cmdSetSystem,
+}
+
+// cmdSetStat sets the rate of the given stat increase for the given remap.
+//
+// query parameters:
+// remap: string; required; the full name of the remap whose kbps to set.
+// stat: string; required; the stat to set (in_bytes, out_bytes, status_2xx,
status_3xx, status_4xx, status_5xx).
+// min: unsigned integer; required; new minimum of kbps increase of
InBytes stat for the given remap.
+// max: unsigned integer; required; new maximum of kbps increase of
InBytes stat for the given remap.
+//
+func cmdSetStat(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs
fakesrvrdata.Ths) {
+ urlQry := r.URL.Query()
+
+ newMinStr := urlQry.Get("min")
+ newMin, err := strconv.ParseUint(newMinStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'min': must be a
positive integer: " + err.Error() + "\n"))
+ return
+ }
+
+ newMaxStr := urlQry.Get("max")
+ newMax, err := strconv.ParseUint(newMaxStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'max': must be a
positive integer: " + err.Error() + "\n"))
+ return
+ }
+
+ remap := urlQry.Get("remap")
+ if remap == "" {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("missing query parameter 'remap': must specify a
remap to set\n"))
+ return
+ }
+
+ stat := urlQry.Get("stat")
+
+ validStats := map[string]struct{}{
+ "in_bytes": {},
+ "out_bytes": {},
+ "status_2xx": {},
+ "status_3xx": {},
+ "status_4xx": {},
+ "status_5xx": {},
+ }
+
+ if _, ok := validStats[stat]; !ok {
+ w.WriteHeader(http.StatusBadRequest)
+ statNames := []string{}
+ for statName, _ := range validStats {
+ statNames = append(statNames, statName)
+ }
+ w.Write([]byte("error with query parameter 'stat' '" + stat +
"': not found. Valid stats are: [" + strings.Join(statNames, ",") + "\n"))
+ return
+ }
+
+ srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get())
+ if _, ok := srvr.ATS.Remaps[remap]; !ok {
+ w.WriteHeader(http.StatusBadRequest)
+ remapNames := []string{}
+ for remapName, _ := range srvr.ATS.Remaps {
+ remapNames = append(remapNames, remapName)
+ }
+ w.Write([]byte("error with query parameter 'remap' '" + remap +
"': not found. Valid remaps are: [" + strings.Join(remapNames, ",") + "\n"))
+ return
+ }
+
+ incs := <-fakeSrvrDataThs.GetIncrementsChan
+ inc := incs[remap]
+
+ switch stat {
+ case "in_bytes":
+ inc.Min.InBytes = newMin
+ inc.Max.InBytes = newMax
+ case "out_bytes":
+ inc.Min.OutBytes = newMin
+ inc.Max.OutBytes = newMax
+ case "status_2xx":
+ inc.Min.Status2xx = newMin
+ inc.Max.Status2xx = newMax
+ case "status_3xx":
+ inc.Min.Status3xx = newMin
+ inc.Max.Status3xx = newMax
+ case "status_4xx":
+ inc.Min.Status4xx = newMin
+ inc.Max.Status4xx = newMax
+ case "status_5xx":
+ inc.Min.Status5xx = newMin
+ inc.Max.Status5xx = newMax
+ default:
+ panic("unknown stat; should never happen")
+ }
+
+ fakeSrvrDataThs.IncrementChan <- fakesrvrdata.IncrementChanT{RemapName:
remap, BytesPerSec: inc}
+
+ w.WriteHeader(http.StatusNoContent)
+}
+
+func cmdSetDelay(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs
fakesrvrdata.Ths) {
+ urlQry := r.URL.Query()
+
+ newMinStr := urlQry.Get("min")
+ newMin, err := strconv.ParseUint(newMinStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'min': must be a
non-negative integer: " + err.Error() + "\n"))
+ return
+ }
+
+ newMaxStr := urlQry.Get("max")
+ newMax, err := strconv.ParseUint(newMaxStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'max': must be a
non-negative integer: " + err.Error() + "\n"))
+ return
+ }
+
+ newMinMax := fakesrvrdata.MinMaxUint64{Min: newMin, Max: newMax}
+ newMinMaxPtr := &newMinMax
+
+ p := (unsafe.Pointer)(newMinMaxPtr)
+ atomic.StorePointer(fakeSrvrDataThs.DelayMS, p)
+ w.WriteHeader(http.StatusNoContent)
+}
+
+func cmdSetSystem(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs
fakesrvrdata.Ths) {
+ urlQry := r.URL.Query()
+
+ if newSpeedStr := urlQry.Get("speed"); newSpeedStr != "" {
+ newSpeed, err := strconv.ParseUint(newSpeedStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'speed':
must be a non-negative integer: " + err.Error() + "\n"))
+ return
+ }
+
+ srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get())
+ srvr.System.Speed = int(newSpeed)
+ fakeSrvrDataThs.Set(fakesrvrdata.ThsT(srvr))
+ }
+
+ if newLoadAvg1MStr := urlQry.Get("loadavg1m"); newLoadAvg1MStr != "" {
+ newLoadAvg1M, err := strconv.ParseFloat(newLoadAvg1MStr, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter
'loadavg1m': must be a number: " + err.Error() + "\n"))
+ return
+ }
+
+ srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get())
+ srvr.System.ProcLoadAvg.CPU1m = newLoadAvg1M
+ fakeSrvrDataThs.Set(fakesrvrdata.ThsT(srvr))
+ }
+
+ if newLoadAvg5MStr := urlQry.Get("loadavg5m"); newLoadAvg5MStr != "" {
+ newLoadAvg5M, err := strconv.ParseFloat(newLoadAvg5MStr, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter
'loadavg5m': must be a number: " + err.Error() + "\n"))
+ return
+ }
+
+ srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get())
+ srvr.System.ProcLoadAvg.CPU5m = newLoadAvg5M
+ fakeSrvrDataThs.Set(fakesrvrdata.ThsT(srvr))
Review comment:
Another `fakeSrvrDataThs.Set(srvr)` candidate
##########
File path: traffic_monitor/tools/testcaches/fakesrvr/cmd.go
##########
@@ -0,0 +1,220 @@
+package fakesrvr
+
+/*
+ * 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
+ *
+ * http://www.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.
+ */
+
+import (
+ "net/http"
+ "strconv"
+ "strings"
+ "sync/atomic"
+ "unsafe"
+
+
"github.com/apache/trafficcontrol/traffic_monitor/tools/testcaches/fakesrvrdata"
+)
+
+type CmdFunc = func(http.ResponseWriter, *http.Request, fakesrvrdata.Ths)
+
+var cmds = map[string]CmdFunc{
+ "setstat": cmdSetStat,
+ "setdelay": cmdSetDelay,
+ "setsystem": cmdSetSystem,
+}
+
+// cmdSetStat sets the rate of the given stat increase for the given remap.
+//
+// query parameters:
+// remap: string; required; the full name of the remap whose kbps to set.
+// stat: string; required; the stat to set (in_bytes, out_bytes, status_2xx,
status_3xx, status_4xx, status_5xx).
+// min: unsigned integer; required; new minimum of kbps increase of
InBytes stat for the given remap.
+// max: unsigned integer; required; new maximum of kbps increase of
InBytes stat for the given remap.
+//
+func cmdSetStat(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs
fakesrvrdata.Ths) {
+ urlQry := r.URL.Query()
+
+ newMinStr := urlQry.Get("min")
+ newMin, err := strconv.ParseUint(newMinStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'min': must be a
positive integer: " + err.Error() + "\n"))
+ return
+ }
+
+ newMaxStr := urlQry.Get("max")
+ newMax, err := strconv.ParseUint(newMaxStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'max': must be a
positive integer: " + err.Error() + "\n"))
+ return
+ }
+
+ remap := urlQry.Get("remap")
+ if remap == "" {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("missing query parameter 'remap': must specify a
remap to set\n"))
+ return
+ }
+
+ stat := urlQry.Get("stat")
+
+ validStats := map[string]struct{}{
+ "in_bytes": {},
+ "out_bytes": {},
+ "status_2xx": {},
+ "status_3xx": {},
+ "status_4xx": {},
+ "status_5xx": {},
+ }
+
+ if _, ok := validStats[stat]; !ok {
+ w.WriteHeader(http.StatusBadRequest)
+ statNames := []string{}
+ for statName, _ := range validStats {
+ statNames = append(statNames, statName)
+ }
+ w.Write([]byte("error with query parameter 'stat' '" + stat +
"': not found. Valid stats are: [" + strings.Join(statNames, ",") + "\n"))
+ return
+ }
+
+ srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get())
+ if _, ok := srvr.ATS.Remaps[remap]; !ok {
+ w.WriteHeader(http.StatusBadRequest)
+ remapNames := []string{}
+ for remapName, _ := range srvr.ATS.Remaps {
+ remapNames = append(remapNames, remapName)
+ }
+ w.Write([]byte("error with query parameter 'remap' '" + remap +
"': not found. Valid remaps are: [" + strings.Join(remapNames, ",") + "\n"))
+ return
+ }
+
+ incs := <-fakeSrvrDataThs.GetIncrementsChan
+ inc := incs[remap]
+
+ switch stat {
+ case "in_bytes":
+ inc.Min.InBytes = newMin
+ inc.Max.InBytes = newMax
+ case "out_bytes":
+ inc.Min.OutBytes = newMin
+ inc.Max.OutBytes = newMax
+ case "status_2xx":
+ inc.Min.Status2xx = newMin
+ inc.Max.Status2xx = newMax
+ case "status_3xx":
+ inc.Min.Status3xx = newMin
+ inc.Max.Status3xx = newMax
+ case "status_4xx":
+ inc.Min.Status4xx = newMin
+ inc.Max.Status4xx = newMax
+ case "status_5xx":
+ inc.Min.Status5xx = newMin
+ inc.Max.Status5xx = newMax
+ default:
+ panic("unknown stat; should never happen")
+ }
+
+ fakeSrvrDataThs.IncrementChan <- fakesrvrdata.IncrementChanT{RemapName:
remap, BytesPerSec: inc}
+
+ w.WriteHeader(http.StatusNoContent)
+}
+
+func cmdSetDelay(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs
fakesrvrdata.Ths) {
+ urlQry := r.URL.Query()
+
+ newMinStr := urlQry.Get("min")
+ newMin, err := strconv.ParseUint(newMinStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'min': must be a
non-negative integer: " + err.Error() + "\n"))
+ return
+ }
+
+ newMaxStr := urlQry.Get("max")
+ newMax, err := strconv.ParseUint(newMaxStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'max': must be a
non-negative integer: " + err.Error() + "\n"))
+ return
+ }
+
+ newMinMax := fakesrvrdata.MinMaxUint64{Min: newMin, Max: newMax}
+ newMinMaxPtr := &newMinMax
+
+ p := (unsafe.Pointer)(newMinMaxPtr)
+ atomic.StorePointer(fakeSrvrDataThs.DelayMS, p)
+ w.WriteHeader(http.StatusNoContent)
+}
+
+func cmdSetSystem(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs
fakesrvrdata.Ths) {
+ urlQry := r.URL.Query()
+
+ if newSpeedStr := urlQry.Get("speed"); newSpeedStr != "" {
+ newSpeed, err := strconv.ParseUint(newSpeedStr, 10, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter 'speed':
must be a non-negative integer: " + err.Error() + "\n"))
+ return
+ }
+
+ srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get())
+ srvr.System.Speed = int(newSpeed)
+ fakeSrvrDataThs.Set(fakesrvrdata.ThsT(srvr))
+ }
+
+ if newLoadAvg1MStr := urlQry.Get("loadavg1m"); newLoadAvg1MStr != "" {
+ newLoadAvg1M, err := strconv.ParseFloat(newLoadAvg1MStr, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter
'loadavg1m': must be a number: " + err.Error() + "\n"))
+ return
+ }
+
+ srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get())
+ srvr.System.ProcLoadAvg.CPU1m = newLoadAvg1M
+ fakeSrvrDataThs.Set(fakesrvrdata.ThsT(srvr))
+ }
+
+ if newLoadAvg5MStr := urlQry.Get("loadavg5m"); newLoadAvg5MStr != "" {
+ newLoadAvg5M, err := strconv.ParseFloat(newLoadAvg5MStr, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter
'loadavg5m': must be a number: " + err.Error() + "\n"))
+ return
+ }
+
+ srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get())
+ srvr.System.ProcLoadAvg.CPU5m = newLoadAvg5M
+ fakeSrvrDataThs.Set(fakesrvrdata.ThsT(srvr))
+ }
+
+ if newLoadAvg10MStr := urlQry.Get("loadavg10m"); newLoadAvg10MStr != ""
{
+ newLoadAvg10M, err := strconv.ParseFloat(newLoadAvg10MStr, 64)
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("error parsing query parameter
'loadavg10m': must be a non-negative integer: " + err.Error() + "\n"))
+ return
+ }
+
+ srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get())
+ srvr.System.ProcLoadAvg.CPU10m = newLoadAvg10M
+ fakeSrvrDataThs.Set(fakesrvrdata.ThsT(srvr))
Review comment:
`fakeSrvrDataThs.Set(srvr)` can be here
##########
File path: traffic_monitor/tests/integration/README.md
##########
@@ -0,0 +1,12 @@
+# Traffic Monitor Integration Test Framework
+
+## Running
+
+From the `trafficcontrol/traffic_monitor` directory:
+
+```
+(cd tools/testto && go build)
+(cd tools/testcaches && go build)
+(cd tests/integration && go test -c -o traffic_monitor_integration_test)
+sudo docker-compose -p tmi --project-directory . -f
tests/integration/docker-compose.yml run tmintegrationtest
Review comment:
After resolving the above linking error, rebuilding the images, and
re-running, I get unmarshalling errors like
```json
{"error": "unmarshalling posted body: json: cannot unmarshal bool into Go
struct field CRConfigDeliveryService.deliveryServices.anonymousBlockingEnabled
of type string"}
```
It eventually says
```
Error communicating with Monitor 'http://trafficmonitor' - didn't return a
200 OK in 30s
```
<details><summary>Full <code>tmintegrationtest</code> logs are here (click
to expand)</summary>
```
< HTTP/1.1 200 OK
* About to connect() to testto port 80 (#0)
* Trying 172.23.0.3...
* Connected to testto (172.23.0.3) port 80 (#0)
> POST /api/1.2/cdns/fake/snapshot HTTP/1.1
> User-Agent: curl/7.29.0
> Host: testto
> Accept: */*
> Content-Length: 3756
> Content-Type: application/x-www-form-urlencoded
> Expect: 100-continue
>
< HTTP/1.1 100 Continue
< HTTP/1.1 400 Bad Request
< Date: Fri, 25 Sep 2020 21:32:19 GMT
< Content-Length: 169
< Content-Type: text/plain; charset=utf-8
* HTTP error before end of send, stop sending
<
* Closing connection 0
{"error": "unmarshalling posted body: json: cannot unmarshal bool into Go
struct field CRConfigDeliveryService.deliveryServices.anonymousBlockingEnabled
of type string"}* About to connect() to testto port 80 (#0)
* Trying 172.23.0.3...
* Connected to testto (172.23.0.3) port 80 (#0)
> POST /api/1.2/cdns/fake/configs/monitoring.json HTTP/1.1
> User-Agent: curl/7.29.0
> Host: testto
> Accept: */*
> Content-Length: 2308
> Content-Type: application/x-www-form-urlencoded
> Expect: 100-continue
>
< HTTP/1.1 100 Continue
< HTTP/1.1 400 Bad Request
< Date: Fri, 25 Sep 2020 21:32:19 GMT
< Content-Length: 167
< Content-Type: text/plain; charset=utf-8
* HTTP error before end of send, stop sending
<
* Closing connection 0
{"error": "unmarshalling posted body: json: cannot unmarshal object into Go
struct field TrafficServer.trafficServers.deliveryServices of type
[]tc.tsdeliveryService"}* About to connect() to testto port 80 (#0)
* Trying 172.23.0.3...
* Connected to testto (172.23.0.3) port 80 (#0)
> POST /api/1.2/servers HTTP/1.1
> User-Agent: curl/7.29.0
> Host: testto
> Accept: */*
> Content-Length: 1135
> Content-Type: application/x-www-form-urlencoded
> Expect: 100-continue
>
< HTTP/1.1 100 Continue
< HTTP/1.1 204 No Content
< Date: Fri, 25 Sep 2020 21:32:19 GMT
<
* Connection #0 to host testto left intact
testto:
% Total % Received % Xferd Average Speed Time Time Time
Current
Dload Upload Total Spent Left Speed
100 3877 0 3877 0 0 657k 0 --:--:-- --:--:-- --:--:--
757k
{"response":{
"config": {
"api.cache-control.max_age": "30",
"consistent.dns.routing": "true",
"coveragezone.polling.interval": "30",
testcaches:
% Total % Received % Xferd Average Speed Time Time Time
Current
Dload Upload Total Spent Left Speed
{ 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
1 "ats": {
0 "plugin.remap_stats.num7.example.net.in_bytes": 2,
0 "plugin.remap_stats.num7.example.net.out_bytes": 2,
"plugin.remap_stats.num7.example.net.status_2xx": 1,
34955 0 34955 0 0 4648k 0 --:--:-- --:--:-- --:--:-- 4876k
(23) Failed writing body
traffic_monitor:
% Total % Received % Xferd Average Speed Time Time Time
Current
Dload Upload Total Spent Left Speed
100 6522<!DOCTYPE html>0 0 0 0 --:--:-- --:--:-- --:--:-- 0
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
0 6522 0 0 1159k 0 --:--:-- --:--:-- --:--:-- 1273k
(23) Failed writing body
DEBUG traffic_monitor_integration starting
Error communicating with Monitor 'http://trafficmonitor' - didn't return a
200 OK in 30s
```
</details>
Looking at the logs of the `trafficmonitor` container, it doen't seem to get
past
```go
ERROR: datareq.go:152: 2020-09-25T21:32:38.143874758Z: Request Error:
/api/version: service still starting, some caches unpolled: map[]
```
although there are other errors in there too, like
```go
trafficmonitor_1 | ERROR: monitorconfig.go:346:
2020-09-25T21:32:33.530687712Z: Failed to parse polling strings for cache
server 'server0': no service addresses found
trafficmonitor_1 | ERROR: monitorconfig.go:346:
2020-09-25T21:32:33.530798803Z: Failed to parse polling strings for cache
server 'server1': no service addresses found
```
<details><summary>Full <code>trafficmonitor</code> container logs are here
(click to expand)</summary>
```go
Attaching to tmi_trafficmonitor_1
trafficmonitor_1 | Failed to get D-Bus connection: Operation not
permitted
trafficmonitor_1 | /etc/init.d/traffic_monitor: line 41:
/etc/sysconfig/network: No such file or directory
trafficmonitor_1 | Starting traffic_monitor:
trafficmonitor_1 | ERROR: opsconfig.go:77:
2020-09-25T21:32:18.530467624Z: OpsConfigManager: getting CDN name from Traffic
Ops, using config CDN 'nocdn': getting monitor CDN: no server 'trafficmonitor'
found in Traffic Ops
trafficmonitor_1 |
trafficmonitor_1 | ERROR: opsconfig.go:77:
2020-09-25T21:32:18.538113399Z: OpsConfigManager: Error getting Traffic Ops
data: Error getting CRconfig from Traffic Ops: invalid CRConfig:
CRConfig.Stats.CDN missing
trafficmonitor_1 |
trafficmonitor_1 | ERROR: opsconfig.go:205:
2020-09-25T21:32:18.538157142Z: retrying in 100ms
trafficmonitor_1 | ERROR: opsconfig.go:77:
2020-09-25T21:32:18.638750564Z: OpsConfigManager: Error getting Traffic Ops
data: Error getting CRconfig from Traffic Ops: invalid CRConfig:
CRConfig.Stats.CDN missing
trafficmonitor_1 |
trafficmonitor_1 | ERROR: opsconfig.go:205:
2020-09-25T21:32:18.638781915Z: retrying in 248.612746ms
trafficmonitor_1 | ERROR: opsconfig.go:77:
2020-09-25T21:32:18.888066827Z: OpsConfigManager: Error getting Traffic Ops
data: Error getting CRconfig from Traffic Ops: invalid CRConfig:
CRConfig.Stats.CDN missing
trafficmonitor_1 |
trafficmonitor_1 | ERROR: opsconfig.go:205:
2020-09-25T21:32:18.888119514Z: retrying in 642.263625ms
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:20.102734056Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152: 2020-09-25T21:32:21.1049107Z:
Request Error: /api/version: service still starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:22.107247084Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:23.109295834Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:24.111672602Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:25.113719758Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:26.115813436Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:27.118294393Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:28.120237324Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:29.122398443Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:30.123841491Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:31.125970238Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:32.128329372Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:33.130950123Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: monitorconfig.go:346:
2020-09-25T21:32:33.530687712Z: Failed to parse polling strings for cache
server 'server0': no service addresses found
trafficmonitor_1 | ERROR: monitorconfig.go:346:
2020-09-25T21:32:33.530798803Z: Failed to parse polling strings for cache
server 'server1': no service addresses found
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:34.135402114Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:35.136748588Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:36.139031306Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:37.141564084Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:38.143874758Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:39.146134866Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152: 2020-09-25T21:32:40.14928317Z:
Request Error: /api/version: service still starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:41.151503741Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:42.154148808Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:43.156421926Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:44.158672414Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:45.159938856Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:46.161990084Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:47.163412929Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:48.164121248Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
trafficmonitor_1 | ERROR: datareq.go:152:
2020-09-25T21:32:49.165983499Z: Request Error: /api/version: service still
starting, some caches unpolled: map[]
```
</details>
##########
File path: traffic_monitor/tests/integration/Dockerfile_run.sh
##########
@@ -0,0 +1,394 @@
+#!/usr/bin/env bash
+# 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
+#
+# http://www.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.
+
+# The following environment variables must be set (ordinarily by `docker run
-e` arguments):
+# TO_URI
+# TO_USER
+# TO_PASS
+# CDN
+
+# Check that env vars are set
+envvars=( TESTTO_URI TESTTO_PORT TESTCACHES_URI TESTCACHES_PORT_START TM_URI )
+for v in $envvars
+do
+ if [[ -z $$v ]]; then echo "$v is unset"; exit 1; fi
+done
+
+
+CFG_FILE=/traffic-monitor-integration-test.cfg
+
+start() {
+ printf "DEBUG traffic_monitor_integration starting\n"
+
+ exec /traffic_monitor_integration_test -test.v -cfg $CFG_FILE
+}
+
+init() {
+ wait_for_to
+
+ curl -Lvsk ${TESTTO_URI}/api/1.2/cdns/fake/snapshot -X POST -d '
+{
+ "config": {
+ "api.cache-control.max_age": "30",
+ "consistent.dns.routing": "true",
+ "coveragezone.polling.interval": "30",
+ "coveragezone.polling.url": "30",
+ "dnssec.dynamic.response.expiration": "60",
+ "dnssec.enabled": "false",
+ "domain_name": "monitor-integration.test",
+ "federationmapping.polling.interval": "60",
+ "federationmapping.polling.url": "foo",
+ "geolocation.polling.interval": "30",
+ "geolocation.polling.url": "foo",
+ "keystore.maintenance.interval": "30",
+ "neustar.polling.interval": "30",
+ "neustar.polling.url": "foo",
+ "soa": {
+
+ },
+ "dnssec.inception": "0",
+ "ttls": {
+ "admin": "30",
+ "expire": "30",
+ "minimum": "30",
+ "refresh": "30",
+ "retry": "30"
+ },
+ "weight": "1",
+ "zonemanager.cache.maintenance.interval": "30",
+ "zonemanager.threadpool.scale": "1"
+ },
+ "contentServers": {
+ "server0": {
+ "cacheGroup": "cg0",
+ "profile": "Edge0",
+ "fqdn": "server0.monitor-integration.test",
+ "hashCount": 1,
+ "hashId": "server0",
+ "httpsPort" : null,
+ "ip": "testcaches",
+ "ip6": null,
+ "locationId": "",
+ "port" : 30000,
+ "status": "REPORTED",
+ "type": "EDGE",
+ "deliveryServices": {"ds0":["ds0.monitor-integration.test"]},
+ "routingDisabled": 0
+ },
+ "server1": {
+ "cacheGroup": "cg0",
+ "profile": "Edge0",
+ "fqdn": "server1.monitor-integration.test",
+ "hashCount": 1,
+ "hashId": "server1",
+ "httpsPort" : null,
+ "ip": "testcaches",
+ "ip6": null,
+ "locationId": "",
+ "port" : 30001,
+ "status": "REPORTED",
+ "type": "EDGE",
+ "deliveryServices": {"ds0":["ds0.monitor-integration.test"]},
+ "routingDisabled": 0
+ }
+ },
+ "deliveryServices": {
+ "ds0": {
+ "anonymousBlockingEnabled": false,
+ "consistentHashQueryParams": [],
+ "consistentHashRegex": "",
+ "coverageZoneOnly": false,
+ "dispersion": {
+ "limit": 1,
+ "shuffled": false
+ },
+ "domains": ["ds0.monitor-integration.test"],
+ "geolocationProvider": null,
+ "matchsets": [
+ {
+ "protocol": "HTTP",
+ "matchlist": [
+ {
+ "regex": "\\.*ds0\\.*",
+ "match-type": "regex"
+ }
+ ]
+ }
+ ],
+ "missLocation": {"lat": 0, "lon": 0},
+ "protocol": {
+ "acceptHttp": true,
+ "acceptHttps": false,
+ "redirectToHttps": false
+ },
+ "regionalGeoBlocking": "false",
+ "responseHeaders": {},
+ "requestHeaders": [],
+ "soa": {
+ "admin": "60",
+ "expire": "60",
+ "minimum": "60",
+ "refresh": "60",
+ "retry": "60"
+ },
+ "sslEnabled": false,
+ "ttl": 60,
+ "ttls": {
+ "A": "60",
+ "AAAA": "60",
+ "DNSKEY": "60",
+ "DS": "60",
+ "NS": "60",
+ "SOA": "60"
+ },
+ "maxDnsIpsForLocation": 3,
+ "ip6RoutingEnabled": false,
+ "routingName": "ccr",
+ "bypassDestination": null,
+ "deepCachingType": null,
+ "geoEnabled": false,
+ "geoLimitRedirectURL": null,
+ "staticDnsEntries": []
+ }
+ },
+ "edgeLocations": {
+ "cg0": {"latitude":0, "longitude":0}
+ },
+ "trafficRouterLocations": {
+ "tr0": {"latitude":0, "longitude":0}
+ },
+ "monitors": {
+ "trafficmonitor": {
+ "fqdn": "trafficmonitor.monitor-integration.test",
+ "httpsPort": null,
+ "ip": "trafficmonitor",
+ "ip6": null,
+ "location": "cg0",
+ "port": 80,
+ "profile": "Monitor0",
+ "status": "REPORTED"
+ }
+ },
+ "stats": {
+ "CDN_name": "fake",
+ "date": 1561000000,
+ "tm_host": "testto",
+ "tm_path": "/fake",
+ "tm_user": "fake",
+ "tm_version": "integrationtest/0.fake"
+ }
+}
+'
+
+ curl -Lvsk ${TESTTO_URI}/api/1.2/cdns/fake/configs/monitoring.json -X
POST -d '
Review comment:
As with the rest of the API v2.0 routes, this has become
`cdns/fake/configs/monitoring`, `json` suffix no longer supported
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]