From 0394998592a31bc1321f755b44caf9327e346de7 Mon Sep 17 00:00:00 2001 From: Robert Butts Date: Tue, 25 Jun 2019 20:23:42 -0600 Subject: [PATCH 01/22] Add tm testcaches http commands to change caches This is with the intention of adding an integration test framework for Traffic Monitor, which can use this tool and send commands to change the fake caches to test various scenarios. --- .../tools/testcaches/fakesrvr/cmd.go | 220 ++++++++++++++++++ .../tools/testcaches/fakesrvr/server.go | 35 +++ .../tools/testcaches/fakesrvrdata/run.go | 96 +++++--- .../tools/testcaches/fakesrvrdata/ths.go | 27 ++- 4 files changed, 348 insertions(+), 30 deletions(-) create mode 100644 traffic_monitor/tools/testcaches/fakesrvr/cmd.go diff --git a/traffic_monitor/tools/testcaches/fakesrvr/cmd.go b/traffic_monitor/tools/testcaches/fakesrvr/cmd.go new file mode 100644 index 0000000000..c29c9281b1 --- /dev/null +++ b/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)) + } + + w.WriteHeader(http.StatusNoContent) +} diff --git a/traffic_monitor/tools/testcaches/fakesrvr/server.go b/traffic_monitor/tools/testcaches/fakesrvr/server.go index acfbbc7b8c..40fe4dd71d 100644 --- a/traffic_monitor/tools/testcaches/fakesrvr/server.go +++ b/traffic_monitor/tools/testcaches/fakesrvr/server.go @@ -22,8 +22,11 @@ package fakesrvr import ( "encoding/json" "fmt" + "math/rand" "net/http" "strconv" + "strings" + "sync/atomic" "time" "github.com/apache/trafficcontrol/traffic_monitor/tools/testcaches/fakesrvrdata" @@ -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)))) + } + delay := time.Duration(delayMS) * time.Millisecond + time.Sleep(delay) + } + // TODO cast to System, if query string `application=system` b := []byte{} err := error(nil) @@ -56,9 +73,27 @@ func astatsHandler(fakeSrvrDataThs fakesrvrdata.Ths) http.HandlerFunc { } } +func cmdHandler(fakeSrvrDataThs fakesrvrdata.Ths) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + path := r.URL.Path + path = strings.ToLower(path) + path = strings.TrimLeft(path, "/cmd") + for cmd, cmdF := range cmds { + if strings.HasPrefix(path, cmd) { + cmdF(w, r, fakeSrvrDataThs) + return + } + } + w.WriteHeader(http.StatusNotFound) + w.Write([]byte("command '" + path + "' not found\n")) + } +} + func Serve(port int, fakeSrvrData fakesrvrdata.Ths) *http.Server { mux := http.NewServeMux() mux.HandleFunc("/_astats", astatsHandler(fakeSrvrData)) + mux.HandleFunc("/cmd", cmdHandler(fakeSrvrData)) + mux.HandleFunc("/cmd/", cmdHandler(fakeSrvrData)) server := &http.Server{ Addr: ":" + strconv.Itoa(port), Handler: mux, diff --git a/traffic_monitor/tools/testcaches/fakesrvrdata/run.go b/traffic_monitor/tools/testcaches/fakesrvrdata/run.go index 4a1956c6c8..d2c1a6b782 100644 --- a/traffic_monitor/tools/testcaches/fakesrvrdata/run.go +++ b/traffic_monitor/tools/testcaches/fakesrvrdata/run.go @@ -72,44 +72,82 @@ func Run(s FakeServerData, remapIncrements map[string]BytesPerSec) (Ths, error) } ths := NewThs() ths.Set(&s) + go run(ths, remapIncrements) return ths, nil } +type IncrementChanT struct { + RemapName string + BytesPerSec BytesPerSec +} + // run starts a goroutine incrementing the FakeServerData's values according to the remapIncrements. Never returns. func run(srvrThs Ths, remapIncrements map[string]BytesPerSec) { tickSecs := uint64(1) // adjustable for performance (i.e. a higher number is less CPU work) + + ticker := time.NewTicker(time.Second * time.Duration(tickSecs)) + for { - time.Sleep(time.Second * time.Duration(tickSecs)) - srvr := srvrThs.Get() - newRemaps := copyRemaps(srvr.ATS.Remaps) - for remap, increments := range remapIncrements { - srvrRemap := newRemaps[remap] - if increments.Min.InBytes != increments.Min.InBytes { - i := uint64(rand.Int63n(int64((increments.Max.InBytes-increments.Min.InBytes)*tickSecs))) + (increments.Min.InBytes * tickSecs) - srvrRemap.InBytes += i - srvr.System.ProcNetDev.RcvBytes += i - } - if increments.Min.OutBytes != increments.Max.OutBytes { - i := uint64(rand.Int63n(int64((increments.Max.OutBytes-increments.Min.OutBytes)*tickSecs))) + (increments.Min.OutBytes * tickSecs) - srvrRemap.OutBytes += i - srvr.System.ProcNetDev.SndBytes += i - } - if increments.Min.Status2xx != increments.Max.Status2xx { - srvrRemap.Status2xx += uint64(rand.Int63n(int64((increments.Max.Status2xx-increments.Min.Status2xx)*tickSecs))) + (increments.Min.Status2xx * tickSecs) - } - if increments.Min.Status3xx != increments.Max.Status3xx { - srvrRemap.Status3xx += uint64(rand.Int63n(int64((increments.Max.Status3xx-increments.Min.Status3xx)*tickSecs))) + (increments.Min.Status3xx * tickSecs) - } - if increments.Min.Status4xx != increments.Max.Status4xx { - srvrRemap.Status4xx += uint64(rand.Int63n(int64((increments.Max.Status4xx-increments.Min.Status4xx)*tickSecs))) + (increments.Min.Status4xx * tickSecs) - } - if increments.Min.Status5xx != increments.Max.Status5xx { - srvrRemap.Status5xx += uint64(rand.Int63n(int64((increments.Max.Status5xx-increments.Min.Status5xx)*tickSecs))) + (increments.Min.Status5xx * tickSecs) + select { + case srvrThs.GetIncrementsChan <- remapIncrements: + case newIncrement := <-srvrThs.IncrementChan: + remapIncrements[newIncrement.RemapName] = newIncrement.BytesPerSec + case <-ticker.C: + srvr := srvrThs.Get() + newRemaps := copyRemaps(srvr.ATS.Remaps) + for remap, increments := range remapIncrements { + srvrRemap := newRemaps[remap] + + addInBytes := increments.Min.InBytes * tickSecs + if increments.Min.InBytes != increments.Max.InBytes { + addInBytes += uint64(rand.Int63n(int64((increments.Max.InBytes - increments.Min.InBytes) * tickSecs))) + } + srvrRemap.InBytes += addInBytes + srvr.System.ProcNetDev.RcvBytes += addInBytes + + addOutBytes := increments.Min.OutBytes * tickSecs + if increments.Min.OutBytes != increments.Max.OutBytes { + addOutBytes += uint64(rand.Int63n(int64((increments.Max.OutBytes - increments.Min.OutBytes) * tickSecs))) + } + srvrRemap.OutBytes += addOutBytes + srvr.System.ProcNetDev.SndBytes += addOutBytes + + srvrRemap.Status2xx += increments.Min.Status2xx * tickSecs + if increments.Min.Status2xx != increments.Max.Status2xx { + srvrRemap.Status2xx += uint64(rand.Int63n(int64((increments.Max.Status2xx - increments.Min.Status2xx) * tickSecs))) + } + + srvrRemap.Status3xx += increments.Min.Status3xx * tickSecs + if increments.Min.Status3xx != increments.Max.Status3xx { + srvrRemap.Status3xx += uint64(rand.Int63n(int64((increments.Max.Status3xx - increments.Min.Status3xx) * tickSecs))) + } + + srvrRemap.Status4xx += increments.Min.Status4xx * tickSecs + if increments.Min.Status4xx != increments.Max.Status4xx { + srvrRemap.Status4xx += uint64(rand.Int63n(int64((increments.Max.Status4xx - increments.Min.Status4xx) * tickSecs))) + } + + srvrRemap.Status5xx += increments.Min.Status5xx * tickSecs + if increments.Min.Status5xx != increments.Max.Status5xx { + srvrRemap.Status5xx += uint64(rand.Int63n(int64((increments.Max.Status5xx - increments.Min.Status5xx) * tickSecs))) + } + + newRemaps[remap] = srvrRemap } - newRemaps[remap] = srvrRemap + srvr.ATS.Remaps = newRemaps + srvrThs.Set(srvr) } - srvr.ATS.Remaps = newRemaps - srvrThs.Set(srvr) + } +} + +// tryReceiveIncrement asynchronously tries to recieve from incrementChan if it has a value. +// Returns the value if it was read, or returns false if no value was waiting. +func tryReceiveIncrement(incrementChan <-chan IncrementChanT) (IncrementChanT, bool) { + select { + case inc := <-incrementChan: + return inc, true + default: + return IncrementChanT{}, false } } diff --git a/traffic_monitor/tools/testcaches/fakesrvrdata/ths.go b/traffic_monitor/tools/testcaches/fakesrvrdata/ths.go index e0befa2436..a08566a1d5 100644 --- a/traffic_monitor/tools/testcaches/fakesrvrdata/ths.go +++ b/traffic_monitor/tools/testcaches/fakesrvrdata/ths.go @@ -21,17 +21,42 @@ package fakesrvrdata import ( "sync" + "unsafe" ) +type MinMaxUint64 struct { + Min uint64 + Max uint64 +} + // Ths provides threadsafe access to a ThsT pointer. Note the object itself is not safe for multiple access, and must not be mutated, either by the original owner after calling Set, or by future users who call Get. If you need to mutate, perform a deep copy. type Ths struct { v *ThsT m *sync.RWMutex + // IncrementChan may be used to set the increments for a particular remap. + // Note this is not synchronized with GetIncrementChan, so multiple writers calling GetIncrementChan and IncrmeentChan to get and set will race, unless they are externally synchronized. + IncrementChan chan IncrementChanT + // GetIncrementsChan may be used to get the current increments for all remaps. + // The returned map must not be modified. + // Note this is not synchronized with GetIncrementChan, so multiple writers calling GetIncrementChan and IncrmeentChan to get and set will race, unless they are externally synchronized. + GetIncrementsChan chan map[string]BytesPerSec + + // DelayMS is the minimum and maximum delay to serve requests, in milliseconds. + // Atomic - MUST be accessed with sync/atomic.LoadUintptr and sync/atomic.StoreUintptr. + DelayMS *unsafe.Pointer } func NewThs() Ths { v := ThsT(nil) - return Ths{m: &sync.RWMutex{}, v: &v} + delayMSPtr := &MinMaxUint64{} + delayMSUnsafePtr := unsafe.Pointer(delayMSPtr) + return Ths{ + m: &sync.RWMutex{}, + v: &v, + IncrementChan: make(chan IncrementChanT, 10), // arbitrarily allow 10 writes before blocking. TODO document? config? + GetIncrementsChan: make(chan map[string]BytesPerSec), + DelayMS: &delayMSUnsafePtr, + } } func (t Ths) Set(v ThsT) { From 4cfa8c775a551b8133559ca246ed350145c16d83 Mon Sep 17 00:00:00 2001 From: Robert Butts Date: Sun, 30 Jun 2019 23:33:15 -0600 Subject: [PATCH 02/22] Add TM client, integration test framework --- traffic_monitor/Dockerfile | 41 ++ traffic_monitor/Dockerfile_run.sh | 81 ++++ traffic_monitor/health/event.go | 11 + traffic_monitor/tests/integration/Dockerfile | 48 +++ .../tests/integration/Dockerfile_run.sh | 394 ++++++++++++++++++ traffic_monitor/tests/integration/README.md | 12 + .../tests/integration/client_test.go | 112 +++++ .../tests/integration/config/config.go | 157 +++++++ .../tests/integration/docker-compose.yml | 102 +++++ .../tests/integration/kbps_test.go | 75 ++++ .../integration/traffic-monitor-test.conf | 17 + .../tests/integration/traffic_monitor_test.go | 125 ++++++ traffic_monitor/tmclient/tmclient.go | 252 +++++++++++ traffic_monitor/tools/testcaches/Dockerfile | 29 ++ .../tools/testcaches/Dockerfile_run.sh | 47 +++ traffic_monitor/tools/testcaches/README.md | 65 +++ .../tools/testcaches/fakesrvr/server.go | 2 + traffic_monitor/tools/testto/Dockerfile | 29 ++ .../tools/testto/Dockerfile_run.sh | 41 ++ traffic_monitor/tools/testto/README.md | 36 ++ traffic_monitor/tools/testto/testto.go | 246 +++++++++++ traffic_monitor/variables.env | 22 + 22 files changed, 1944 insertions(+) create mode 100644 traffic_monitor/Dockerfile create mode 100755 traffic_monitor/Dockerfile_run.sh create mode 100644 traffic_monitor/tests/integration/Dockerfile create mode 100755 traffic_monitor/tests/integration/Dockerfile_run.sh create mode 100644 traffic_monitor/tests/integration/README.md create mode 100644 traffic_monitor/tests/integration/client_test.go create mode 100644 traffic_monitor/tests/integration/config/config.go create mode 100644 traffic_monitor/tests/integration/docker-compose.yml create mode 100644 traffic_monitor/tests/integration/kbps_test.go create mode 100644 traffic_monitor/tests/integration/traffic-monitor-test.conf create mode 100644 traffic_monitor/tests/integration/traffic_monitor_test.go create mode 100644 traffic_monitor/tmclient/tmclient.go create mode 100644 traffic_monitor/tools/testcaches/Dockerfile create mode 100755 traffic_monitor/tools/testcaches/Dockerfile_run.sh create mode 100644 traffic_monitor/tools/testto/Dockerfile create mode 100755 traffic_monitor/tools/testto/Dockerfile_run.sh create mode 100644 traffic_monitor/tools/testto/README.md create mode 100644 traffic_monitor/tools/testto/testto.go create mode 100644 traffic_monitor/variables.env diff --git a/traffic_monitor/Dockerfile b/traffic_monitor/Dockerfile new file mode 100644 index 0000000000..9c70c8a62f --- /dev/null +++ b/traffic_monitor/Dockerfile @@ -0,0 +1,41 @@ +# 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. + +# This is a very simple Dockerfile. +# All it does is install and start the Traffic Monitor, given a Traffic Ops to point it to. +# It doesn't do any of the complex things the Dockerfiles in infrastructure/docker or infrastructure/cdn-in-a-box do, like inserting itself into Traffic Ops. +# It is designed for a very simple use case, where the complex orchestration of other Traffic Control components is done elsewhere (or manually). + +# Example Build and Run: +# docker build --build-arg RPM=traffic_monitor.rpm --tag traffic_monitor:latest . +# +# docker run --detach --name tm --hostname tm --net=tmi --env TO_URI=http://to.invalid:3000 --env TO_USER=user --env TO_PASS=pass traffic_monitor:latest + +FROM centos/systemd +MAINTAINER dev@trafficcontrol.apache.org + +ARG RPM=traffic_monitor.rpm +ADD $RPM / + +RUN yum install -y initscripts +RUN yum install -y /$(basename $RPM) +RUN rm /$(basename $RPM) + +RUN curl -L jq https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64 > /usr/sbin/jq && chmod +x /usr/sbin/jq + +ADD Dockerfile_run.sh / +ENTRYPOINT /Dockerfile_run.sh diff --git a/traffic_monitor/Dockerfile_run.sh b/traffic_monitor/Dockerfile_run.sh new file mode 100755 index 0000000000..f6cffad73f --- /dev/null +++ b/traffic_monitor/Dockerfile_run.sh @@ -0,0 +1,81 @@ +#!/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=( TO_URI TO_USER TO_PASS CDN PORT ) +for v in $envvars +do + if [[ -z $$v ]]; then echo "$v is unset"; exit 1; fi +done + +start() { + service traffic_monitor start + touch /opt/traffic_monitor/var/log/traffic_monitor.log + exec tail -f /opt/traffic_monitor/var/log/traffic_monitor.log +} + +init() { + mkdir -p /opt/traffic_monitor/conf + cat > /opt/traffic_monitor/conf/traffic_monitor.cfg <<- EOF + { + "cache_health_polling_interval_ms": 6000, + "cache_stat_polling_interval_ms": 6000, + "monitor_config_polling_interval_ms": 15000, + "http_timeout_ms": 2000, + "peer_polling_interval_ms": 5000, + "peer_optimistic": true, + "max_events": 200, + "max_stat_history": 5, + "max_health_history": 5, + "health_flush_interval_ms": 20, + "stat_flush_interval_ms": 20, + "log_location_event": "/opt/traffic_monitor/var/log/event.log", + "log_location_error": "/opt/traffic_monitor/var/log/traffic_monitor.log", + "log_location_warning": "/opt/traffic_monitor/var/log/traffic_monitor.log", + "log_location_info": "null", + "log_location_debug": "null", + "serve_read_timeout_ms": 10000, + "serve_write_timeout_ms": 10000, + "http_poll_no_sleep": false, + "static_file_dir": "/opt/traffic_monitor/static/" + } +EOF + + cat > /opt/traffic_monitor/conf/traffic_ops.cfg <<- EOF + { + "username": "$TO_USER", + "password": "$TO_PASS", + "url": "$TO_URI", + "insecure": true, + "cdnName": "$CDN", + "httpListener": ":$PORT" + } + EOF + + echo "INITIALIZED=1" >> /etc/environment +} + +source /etc/environment +if [ -z "$INITIALIZED" ]; then init; fi +start diff --git a/traffic_monitor/health/event.go b/traffic_monitor/health/event.go index d913c35ccc..cec3c63f64 100644 --- a/traffic_monitor/health/event.go +++ b/traffic_monitor/health/event.go @@ -20,7 +20,9 @@ package health */ import ( + "errors" "fmt" + "strconv" "sync" "time" @@ -33,6 +35,15 @@ func (t Time) MarshalJSON() ([]byte, error) { return []byte(fmt.Sprintf("%d", time.Time(t).Unix())), nil } +func (t *Time) UnmarshalJSON(data []byte) error { + i, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return errors.New("health.Time (" + string(data) + ") must be a unix epoch integer: " + err.Error()) + } + *t = Time(time.Unix(i, 0)) + return nil +} + // Event represents an event change in aggregated data. For example, a cache being marked as unavailable. type Event struct { Time Time `json:"time"` diff --git a/traffic_monitor/tests/integration/Dockerfile b/traffic_monitor/tests/integration/Dockerfile new file mode 100644 index 0000000000..97e2d1820e --- /dev/null +++ b/traffic_monitor/tests/integration/Dockerfile @@ -0,0 +1,48 @@ +# 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. + +# This is a very simple Dockerfile. +# All it does is install and start the Traffic Monitor, given a Traffic Ops to point it to. +# It doesn't do any of the complex things the Dockerfiles in infrastructure/docker or infrastructure/cdn-in-a-box do, like inserting itself into Traffic Ops. +# It is designed for a very simple use case, where the complex orchestration of other Traffic Control components is done elsewhere (or manually). + +# Example Build and Run: +# docker build --build-arg RPM=traffic_monitor.rpm --tag traffic_monitor:latest . +# +# docker run --detach --name tm --hostname tm --net=tmi --env TO_URI=http://to.invalid:3000 --env TO_USER=user --env TO_PASS=pass traffic_monitor:latest + +FROM centos/systemd +MAINTAINER dev@trafficcontrol.apache.org + +# ARG RPM=traffic_monitor.rpm +# ADD $RPM / + +RUN yum install -y initscripts +RUN yum install -y epel-release +RUN yum install -y golang +# RUN yum install -y /$(basename $RPM) +# RUN rm /$(basename $RPM) + +COPY traffic_monitor_integration_test / + +# TODO build go test here, instead of requiring it manually be built beforehand +# See /infrastructure/cdn-in-a-box/traffic_ops_integration_test/Dockerfile + +RUN curl -L jq https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64 > /usr/sbin/jq && chmod +x /usr/sbin/jq + +COPY Dockerfile_run.sh / +ENTRYPOINT /Dockerfile_run.sh diff --git a/traffic_monitor/tests/integration/Dockerfile_run.sh b/traffic_monitor/tests/integration/Dockerfile_run.sh new file mode 100755 index 0000000000..0316a91097 --- /dev/null +++ b/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", + "lastUpdated": "2019", + "mgmtIpAddress": null, + "mgmtIpGateway": null, + "mgmtIpNetmask": null, + "offlineReason": "none", + "physLocation": "", + "physLocationId": 0, + "profile": "Monitor0", + "profileDesc": "nodesc", + "profileId": 0, + "rack": "", + "revalPending": false, + "routerHostName": "", + "routerPortName": "", + "status": "REPORTED", + "statusId": 0, + "tcpPort": 80, + "type": "RASCAL", + "typeId": 0, + "updPending": false, + "xmppId": "", + "xmppPasswd": "" + } +] +' + + # DEBUG + printf "\n\ntestto:\n" + curl -Lk ${TESTTO_URI}/api/1.2/cdns/foo/snapshot | head -5 + printf "\n\ntestcaches:\n" + curl -Lk ${TESTCACHES_URI}:${TESTCACHES_PORT_START}/_astats | head -5 + printf "\n\ntraffic_monitor:\n" + curl -Lk http://trafficmonitor | head -5 + + + cat > $CFG_FILE <<- EOF +{ + "trafficMonitor": { + "url": "$TM_URI" + }, + "default": { + "session": { + "timeoutInSecs": 30 + }, + "log": { + "debug": "stdout", + "event": "stdout", + "info": "stdout", + "error": "stdout", + "warning": "stdout" + } + } +} +EOF + + echo "INITIALIZED=1" >> /etc/environment +} + +wait_for_to() { + while true; do + curl -Lvsk ${TESTTO_URI}/api/1.2/servers 2>&1 1> /dev/null | grep -E '< HTTP/[0-9]\.?[0-9]* 200' + RC=$?; + if [[ $RC -eq 0 ]] ; then + break; + fi + printf "Waiting for Traffic Ops to return a 200 OK\n"; + sleep 2; + done +} + +source /etc/environment +if [ -z "$INITIALIZED" ]; then init; fi +start diff --git a/traffic_monitor/tests/integration/README.md b/traffic_monitor/tests/integration/README.md new file mode 100644 index 0000000000..2368cb6954 --- /dev/null +++ b/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 +``` diff --git a/traffic_monitor/tests/integration/client_test.go b/traffic_monitor/tests/integration/client_test.go new file mode 100644 index 0000000000..658275d987 --- /dev/null +++ b/traffic_monitor/tests/integration/client_test.go @@ -0,0 +1,112 @@ +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 ( + "testing" +) + +func TestClient(t *testing.T) { + if actual, err := TMClient.CacheCount(); err != nil { + t.Errorf("client CacheCount error expected nil, actual %v\n", err) + } else if actual <= 0 { + t.Errorf("client CacheCount expected > 0 actual %v\n", actual) + } + + if actual, err := TMClient.CacheAvailableCount(); err != nil { + t.Errorf("client CacheAvailableCount error expected nil, actual %v\n", err) + } else if actual <= 0 { + t.Errorf("client CacheAvailableCount expected > 0 actual %v\n", actual) + } + + if actual, err := TMClient.CacheDownCount(); err != nil { + t.Errorf("client CacheDownCount error expected nil, actual %v\n", err) + } else if actual < 0 { + t.Errorf("client CacheDownCount expected >= 0 actual %v\n", actual) + } + + if actual, err := TMClient.Version(); err != nil { + t.Errorf("client Version error expected nil, actual %v\n", err) + } else if actual == "" { + t.Errorf("client Version expected not empty, actual empty\n") + } + + if actual, err := TMClient.TrafficOpsURI(); err != nil { + t.Errorf("client TrafficOpsURI error expected nil, actual %v\n", err) + } else if actual == "" { + t.Errorf("client TrafficOpsURI expected not empty, actual empty\n") + } + + if actual, err := TMClient.BandwidthKBPS(); err != nil { + t.Errorf("client BandwidthKBPS error expected nil, actual %v\n", err) + } else if actual < 0 { + t.Errorf("client BandwidthKBPS expected >=0 , actual %v\n", actual) + } + + if actual, err := TMClient.BandwidthCapacityKBPS(); err != nil { + t.Errorf("client BandwidthCapacityKBPS error expected nil, actual %v\n", err) + } else if actual <= 0 { + t.Errorf("client BandwidthCapacityKBPS expected >0 , actual %v\n", actual) + } + + if actual, err := TMClient.CacheStatuses(); err != nil { + t.Errorf("client CacheStatuses error expected nil, actual %v\n", err) + } else if len(actual) == 0 { + t.Errorf("client len(CacheStatuses) expected >0 , actual %v\n", actual) + } + + if actual, err := TMClient.MonitorConfig(); err != nil { + t.Errorf("client MonitorConfig error expected nil, actual %v\n", err) + } else if len(actual.TrafficServer) == 0 { + t.Errorf("client len(TrafficMonitorConfig.TrafficServers) expected not empty, actual %v\n", actual) + } + + if actual, err := TMClient.CRConfigHistory(); err != nil { + t.Errorf("client CRConfigHistory error expected nil, actual %v\n", err) + } else if len(actual) == 0 { + t.Errorf("client len(CRConfigHistory) expected !=0, actual %v\n", actual) + } + + if actual, err := TMClient.EventLog(); err != nil { + t.Errorf("client EventLog error expected nil, actual %v\n", err) + } else if len(actual.Events) == 0 { + t.Errorf("client len(EventLog.Events) expected !=0, actual %v\n", actual) + } + + if actual, err := TMClient.CacheStats(); err != nil { + t.Errorf("client CacheStats error expected nil, actual %v\n", err) + } else if len(actual.Caches) == 0 { + t.Errorf("client len(CacheStats.Caches) expected !=0, actual %v\n", actual) + } + + if actual, err := TMClient.DSStats(); err != nil { + t.Errorf("client DSStats error expected nil, actual %v\n", err) + } else if len(actual.DeliveryService) == 0 { + t.Errorf("client len(DSStats.DeliveryService) expected !=0, actual %v\n", actual) + } + + if actual, err := TMClient.CRStates(false); err != nil { + t.Errorf("client CRStates error expected nil, actual %v\n", err) + } else if len(actual.Caches) == 0 { + t.Errorf("client len(CRStates.Caches) expected !=0, actual %v\n", actual) + } + + if actual, err := TMClient.CRConfig(); err != nil { + t.Errorf("client CRConfig error expected nil, actual %v\n", err) + } else if len(actual.ContentServers) == 0 { + t.Errorf("client len(CRConfig.ContentServers) expected !=0, actual %v\n", actual) + } +} diff --git a/traffic_monitor/tests/integration/config/config.go b/traffic_monitor/tests/integration/config/config.go new file mode 100644 index 0000000000..dc4917faab --- /dev/null +++ b/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) + os.Exit(0) + } + + return cfg, err +} + +// validate all required fields in the config. +func validate(confPath string, config Config) []error { + + errs := []error{} + + var f string + f = "TrafficMonitor" + toTag, ok := getStructTag(config, f) + if !ok { + errs = append(errs, fmt.Errorf("'%s' must be configured in %s", toTag, confPath)) + } + + if config.TrafficMonitor.URL == "" { + f = "URL" + tag, ok := getStructTag(config.TrafficMonitor, f) + if !ok { + errs = append(errs, fmt.Errorf("cannot lookup structTag: %s", f)) + } + errs = append(errs, fmt.Errorf("'%s.%s' must be configured in %s", toTag, tag, confPath)) + } + + return errs +} + +func getStructTag(thing interface{}, fieldName string) (string, bool) { + var tag string + var ok bool + t := reflect.TypeOf(thing) + if t != nil { + if f, ok := t.FieldByName(fieldName); ok { + tag = f.Tag.Get("json") + return tag, ok + } + } + return tag, ok +} + +// ErrorLog - critical messages +func (c Config) ErrorLog() log.LogLocation { + return log.LogLocation(c.Default.Log.Error) +} + +// WarningLog - warning messages +func (c Config) WarningLog() log.LogLocation { + return log.LogLocation(c.Default.Log.Warning) +} + +// InfoLog - information messages +func (c Config) InfoLog() log.LogLocation { + return log.LogLocation(c.Default.Log.Info) +} + +// DebugLog - troubleshooting messages +func (c Config) DebugLog() log.LogLocation { + return log.LogLocation(c.Default.Log.Debug) +} + +// EventLog - access.log high level transactions +func (c Config) EventLog() log.LogLocation { + return log.LogLocation(c.Default.Log.Event) +} diff --git a/traffic_monitor/tests/integration/docker-compose.yml b/traffic_monitor/tests/integration/docker-compose.yml new file mode 100644 index 0000000000..ae36e1d020 --- /dev/null +++ b/traffic_monitor/tests/integration/docker-compose.yml @@ -0,0 +1,102 @@ +# 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. +# +# To run the integration test: +# 1. build Traffic Monitor and then copy the RPM to this directory as traffic_monitor.rpm. +# 2. build traffic_monitor/tools/testcaches and place the binary in this directory. +# 3. build traffic_monitor/tools/testto and place the binary in this directory. +# 4. Run this compose +# +# Commands are, from the root trafficcontrol directory: +# ./pkg -v traffic_monitor_build +# cp ./dist/traffic_monitor*.rpm ./traffic_monitor/ +# (cd ./traffic_monitor/tools/testto && go build) +# (cd ./traffic_monitor/tools/testcaches && go build) +# cd ./traffic_monitor +# docker-compose up +# +# To run the integration tests, your current working directory must be trafficcontrol/traffic_monitor. +# This is because Docker doesn't allow accessing files outside the current "context," and the integration tests need +# +# docker-compose up -d +# + +--- +version: '2.1' + +services: + testto: + build: + context: ./tools/testto + dockerfile: ./Dockerfile + domainname: traffic-monitor-integration.test + env_file: + - variables.env + hostname: testto + image: testto + volumes: + - shared:/shared + + testcaches: + build: + context: ./tools/testcaches + dockerfile: ./Dockerfile + domainname: traffic-monitor-integration.test + env_file: + - variables.env + hostname: testcaches + image: testcaches + volumes: + - shared:/shared + + trafficmonitor: + build: + context: . + dockerfile: ./Dockerfile + args: + RPM: ./traffic_monitor.rpm + depends_on: + - testto + domainname: traffic-monitor-integration.test + env_file: + - variables.env + hostname: trafficmonitor + image: trafficmonitor + ports: + - "80:80" + volumes: + - shared:/shared + + tmintegrationtest: + build: + context: ./tests/integration + dockerfile: ./Dockerfile + depends_on: + - testto + - testcaches + - trafficmonitor + domainname: traffic-monitor-integration.test + env_file: + - variables.env + hostname: tmintegrationtest + image: tmintegrationtest + volumes: + - shared:/shared + +volumes: + shared: + external: false diff --git a/traffic_monitor/tests/integration/kbps_test.go b/traffic_monitor/tests/integration/kbps_test.go new file mode 100644 index 0000000000..3d1e6cda43 --- /dev/null +++ b/traffic_monitor/tests/integration/kbps_test.go @@ -0,0 +1,75 @@ +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 ( + "fmt" + "net/http" + "testing" + "time" + + "github.com/apache/trafficcontrol/lib/go-tc" +) + +func TestKBPS(t *testing.T) { + crc, err := TMClient.CRConfig() + if err != nil { + t.Fatalf("client CRConfig error expected nil, actual %v\n", err) + } + + if len(crc.ContentServers) == 0 { + t.Fatalf("Monitor CRConfig has no servers, cannot test KBPS") + } + + serverName := "" + server := tc.CRConfigTrafficOpsServer{} + for crcServerName, crcServer := range crc.ContentServers { + server = crcServer + serverName = crcServerName + break + } + if server.Ip == nil { + t.Fatalf("Monitor CRConfig server '" + serverName + "' has no Ip, cannot test KBPS") + } + if server.Port == nil { + t.Fatalf("Monitor CRConfig server '" + serverName + "' has no Port, cannot test KBPS") + } + + const bytesPerKilobit = 125 + + expectedKbps := 10000 + + httpClient := http.Client{Timeout: time.Duration(Config.Default.Session.TimeoutInSecs) * time.Second} + + kbps10 := bytesPerKilobit * expectedKbps + uri := fmt.Sprintf(`http://%v:%v/cmd/setstat?remap=num1.example.net&stat=out_bytes&min=%v&max=%v`, *server.Ip, *server.Port, kbps10, kbps10) + resp, err := httpClient.Get(uri) + if err != nil { + t.Fatalf("Error posting fake cache command '" + uri + "': " + err.Error()) + } + resp.Body.Close() + + time.Sleep(time.Second * 5) // TODO determine if there's a faster or more precise way to wait for polled data? + + kbps, err := TMClient.BandwidthKBPS() + if err != nil { + t.Fatalf("getting monitor bandwidth kbps: %v\n", err) + } + + if kbps < float64(expectedKbps)*0.9 || kbps > float64(expectedKbps)*1.1 { + t.Errorf("monitor bandwidth kbps expected %v actual %v\n", expectedKbps, kbps) + } +} diff --git a/traffic_monitor/tests/integration/traffic-monitor-test.conf b/traffic_monitor/tests/integration/traffic-monitor-test.conf new file mode 100644 index 0000000000..24956daef1 --- /dev/null +++ b/traffic_monitor/tests/integration/traffic-monitor-test.conf @@ -0,0 +1,17 @@ +{ + "default": { + "logLocations": { + "debug": "stdout", + "error": "stdout", + "event": "stdout", + "info": "stdout", + "warning": "stdout" + }, + "session": { + "timeoutInSecs": 30 + } + }, + "trafficMonitor": { + "URL": "http://localhost:80" + } +} diff --git a/traffic_monitor/tests/integration/traffic_monitor_test.go b/traffic_monitor/tests/integration/traffic_monitor_test.go new file mode 100644 index 0000000000..c4dbee1385 --- /dev/null +++ b/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) + // } + + tmReqTimeout := time.Second * time.Duration(Config.Default.Session.TimeoutInSecs) + + // err = SetupSession(toReqTimeout, Config.TrafficOps.URL, Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword) + // if err != nil { + // fmt.Printf("\nError creating session to %s - %s, %v\n", Config.TrafficOps.URL, Config.TrafficOpsDB.User, err) + // os.Exit(1) + // } + + monitorWaitSpan := 30 * time.Second // TODO make configurable? + + if !WaitForMonitor(Config.TrafficMonitor.URL, monitorWaitSpan) { + fmt.Printf("\nError communicating with Monitor '%v' - didn't return a 200 OK in %v\n", Config.TrafficMonitor.URL, monitorWaitSpan) + os.Exit(1) + } + + TMClient = tmclient.New(Config.TrafficMonitor.URL, tmReqTimeout) + + // Now run the test case + rc := m.Run() + os.Exit(rc) +} + +// WaitForMonitor waits for the monitor to fully start, and stop serving 5xx codes. +// If the monitor does not return a 200 from an API endpoint by timeout, returns false. +func WaitForMonitor(url string, timeout time.Duration) bool { + httpClient := http.Client{Timeout: timeout} + + tryInterval := time.Second // TODO make configurable? + + start := time.Now() + for { + if time.Now().After(start.Add(timeout)) { + return false + } + time.Sleep(tryInterval) + resp, err := httpClient.Get(strings.TrimSuffix(url, "/") + "/api/version") + if err != nil { + continue + } + resp.Body.Close() + if resp.StatusCode != 200 { + continue + } + return true + } +} diff --git a/traffic_monitor/tmclient/tmclient.go b/traffic_monitor/tmclient/tmclient.go new file mode 100644 index 0000000000..6ce0b0871f --- /dev/null +++ b/traffic_monitor/tmclient/tmclient.go @@ -0,0 +1,252 @@ +package tmclient + +/* + * 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 ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "net/http" + "strconv" + "strings" + "time" + + "github.com/apache/trafficcontrol/lib/go-tc" + "github.com/apache/trafficcontrol/traffic_monitor/cache" + "github.com/apache/trafficcontrol/traffic_monitor/datareq" + "github.com/apache/trafficcontrol/traffic_monitor/dsdata" + "github.com/apache/trafficcontrol/traffic_monitor/handler" + "github.com/apache/trafficcontrol/traffic_monitor/towrap" +) + +type TMClient struct { + url string + timeout time.Duration +} + +func New(url string, timeout time.Duration) *TMClient { + return &TMClient{url: strings.TrimSuffix(url, "/"), timeout: timeout} +} + +func (c *TMClient) CacheCount() (int, error) { return c.getInt("/api/cache-count") } + +func (c *TMClient) CacheAvailableCount() (int, error) { return c.getInt("/api/cache-available-count") } + +func (c *TMClient) CacheDownCount() (int, error) { return c.getInt("/api/cache-down-count") } + +func (c *TMClient) Version() (string, error) { return c.getStr("/api/version") } + +func (c *TMClient) TrafficOpsURI() (string, error) { return c.getStr("/api/traffic-ops-uri") } + +func (c *TMClient) BandwidthKBPS() (float64, error) { return c.getFloat("/api/bandwidth-kbps") } + +func (c *TMClient) BandwidthCapacityKBPS() (float64, error) { + return c.getFloat("/api/bandwidth-capacity-kbps") +} + +func (c *TMClient) CacheStatuses() (map[tc.CacheName]datareq.CacheStatus, error) { + path := "/api/cache-statuses" + obj := map[tc.CacheName]datareq.CacheStatus{} + if err := c.GetJSON(path, &obj); err != nil { + return nil, err // GetJSON adds context + } + return obj, nil +} + +func (c *TMClient) MonitorConfig() (tc.TrafficMonitorConfigMap, error) { + path := "/api/monitor-config" + obj := tc.TrafficMonitorConfigMap{} + if err := c.GetJSON(path, &obj); err != nil { + return tc.TrafficMonitorConfigMap{}, err // GetJSON adds context + } + return obj, nil +} + +func (c *TMClient) CRConfigHistory() ([]towrap.CRConfigStat, error) { + path := "/api/crconfig-history" + obj := []towrap.CRConfigStat{} + if err := c.GetJSON(path, &obj); err != nil { + return nil, err // GetJSON adds context + } + return obj, nil +} + +func (c *TMClient) EventLog() (datareq.JSONEvents, error) { + path := "/publish/EventLog" + obj := datareq.JSONEvents{} + if err := c.GetJSON(path, &obj); err != nil { + return datareq.JSONEvents{}, err // GetJSON adds context + } + return obj, nil +} + +func (c *TMClient) CacheStats() (cache.Stats, error) { + path := "/publish/CacheStats" + obj := cache.Stats{} + if err := c.GetJSON(path, &obj); err != nil { + return cache.Stats{}, err // GetJSON adds context + } + return obj, nil +} + +func (c *TMClient) DSStats() (dsdata.Stats, error) { + path := "/publish/DsStats" + obj := dsdata.Stats{} + if err := c.GetJSON(path, &obj); err != nil { + return dsdata.Stats{}, err // GetJSON adds context + } + return obj, nil +} + +func (c *TMClient) CRStates(raw bool) (tc.CRStates, error) { + path := "/publish/CrStates" + if raw { + path += "?raw" + } + obj := tc.CRStates{} + if err := c.GetJSON(path, &obj); err != nil { + return tc.CRStates{}, err // GetJSON adds context + } + return obj, nil +} + +func (c *TMClient) CRConfig() (tc.CRConfig, error) { + path := "/publish/CrConfig" + obj := tc.CRConfig{} + if err := c.GetJSON(path, &obj); err != nil { + return tc.CRConfig{}, err // GetJSON adds context + } + return obj, nil +} + +// CRConfigBytes returns the raw bytes of the Monitor's CRConfig. +// +// If you need a deserialized object, use TMClient.CRConfig() instead. +// +// This function exists because the Monitor very intentionally serves the CRConfig bytes as +// published by Traffic Ops, without deserializing or reserializing it. +// +// This can be useful to check for serialization or versioning issues, in case the Go object +// is missing values sent by Traffic Ops, or has other serialization issues. +// +// +func (c *TMClient) CRConfigBytes() ([]byte, error) { return c.getBytes("publish/CrConfig") } + +func (c *TMClient) PeerStates() (datareq.APIPeerStates, error) { + path := "/publish/PeerStates" + obj := datareq.APIPeerStates{} + if err := c.GetJSON(path, &obj); err != nil { + return datareq.APIPeerStates{}, err // GetJSON adds context + } + return obj, nil +} + +func (c *TMClient) Stats() (datareq.Stats, error) { + path := "/publish/Stats" + obj := datareq.Stats{} + if err := c.GetJSON(path, &obj); err != nil { + return datareq.Stats{}, err // GetJSON adds context + } + return obj, nil +} + +func (c *TMClient) StatSummary() (datareq.StatSummary, error) { + path := "/publish/StatSummary" + obj := datareq.StatSummary{} + if err := c.GetJSON(path, &obj); err != nil { + return datareq.StatSummary{}, err // GetJSON adds context + } + return obj, nil +} + +func (c *TMClient) ConfigDoc() (handler.OpsConfig, error) { + path := "/publish/ConfigDoc" + obj := handler.OpsConfig{} + if err := c.GetJSON(path, &obj); err != nil { + return handler.OpsConfig{}, err // GetJSON adds context + } + return obj, nil +} + +func (c *TMClient) getBytes(path string) ([]byte, error) { + url := c.url + path + httpClient := http.Client{Timeout: c.timeout} + resp, err := httpClient.Get(url) + if err != nil { + return nil, errors.New("getting from '" + url + "': " + err.Error()) + } + defer resp.Body.Close() + + if resp.StatusCode < 200 || resp.StatusCode > 299 { + return nil, fmt.Errorf("Monitor '"+url+"' returned bad status %v", resp.StatusCode) + } + + respBts, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, errors.New("reading body from '" + url + "': " + err.Error()) + } + return respBts, nil +} + +func (c *TMClient) GetJSON(path string, obj interface{}) error { + bts, err := c.getBytes(path) + if err != nil { + return err // getBytes already adds context + } + if err := json.Unmarshal(bts, obj); err != nil { + return errors.New("unmarshalling response '" + string(bts) + "' json: " + err.Error()) + } + return nil +} + +func (c *TMClient) getStr(path string) (string, error) { + respBts, err := c.getBytes(path) + if err != nil { + return "", err // getBytes already adds context + } + return string(respBts), nil +} + +func (c *TMClient) getInt(path string) (int, error) { + respStr, err := c.getStr(path) + if err != nil { + return 0, err // getStr already adds context + } + + respInt, err := strconv.Atoi(respStr) + if err != nil { + return 0, errors.New("parsing response '" + respStr + "': " + err.Error()) + } + return respInt, nil +} + +func (c *TMClient) getFloat(path string) (float64, error) { + respStr, err := c.getStr(path) + if err != nil { + return 0, err // getStr already adds context + } + + respFloat, err := strconv.ParseFloat(respStr, 64) + if err != nil { + return 0, errors.New("parsing response '" + respStr + "': " + err.Error()) + } + return respFloat, nil +} diff --git a/traffic_monitor/tools/testcaches/Dockerfile b/traffic_monitor/tools/testcaches/Dockerfile new file mode 100644 index 0000000000..e83c1f2c04 --- /dev/null +++ b/traffic_monitor/tools/testcaches/Dockerfile @@ -0,0 +1,29 @@ +# 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. + +FROM centos/systemd +MAINTAINER dev@trafficcontrol.apache.org + +# RUN rpm --import https://mirror.go-repo.io/centos/RPM-GPG-KEY-GO-REPO +# RUN curl -s https://mirror.go-repo.io/centos/go-repo.repo | tee /etc/yum.repos.d/go-repo.repo +# RUN yum install -y golang + +ARG APP_BIN=testcaches +ADD $APP_BIN /usr/sbin/ + +ADD Dockerfile_run.sh / +CMD /Dockerfile_run.sh diff --git a/traffic_monitor/tools/testcaches/Dockerfile_run.sh b/traffic_monitor/tools/testcaches/Dockerfile_run.sh new file mode 100755 index 0000000000..298068eb0b --- /dev/null +++ b/traffic_monitor/tools/testcaches/Dockerfile_run.sh @@ -0,0 +1,47 @@ +#!/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. + + +# envvars=( PORT ) +# for v in $envvars +# do +# if [[ -z $$v ]]; then echo "$v is unset"; exit 1; fi +# done + +start() { + ARGS= + if [[ -n "${NUM_PORTS}" ]]; then + ARGS="$ARGS -numPorts ${NUM_PORTS}" + fi + if [[ -n "${NUM_REMAPS}" ]]; then + ARGS="$ARGS -numRemaps ${NUM_REMAPS}" + fi + if [[ -n "${PORT_START}" ]]; then + ARGS="$ARGS -portStart ${PORT_START}" + fi + testcaches ${ARGS} +} + +init() { + + echo "INITIALIZED=1" >> /etc/environment +} + +source /etc/environment +if [ -z "$INITIALIZED" ]; then init; fi +start diff --git a/traffic_monitor/tools/testcaches/README.md b/traffic_monitor/tools/testcaches/README.md index 2617a93cda..77dc65a8cc 100644 --- a/traffic_monitor/tools/testcaches/README.md +++ b/traffic_monitor/tools/testcaches/README.md @@ -30,3 +30,68 @@ Each port is a unique fake server, with distinct incrementing stats. When run with no parameters, it defaults to ports 40000-40999 and 1000 remaps. Stats are served at the regular ATS `stats_over_http` endpoint, `_astats`. For example, if it's serving on port 40000, it can be reached via `curl http://localhost:40000/_astats`. It also respects the `?application=system` query parameter, and will serve only system stats (the Monitor "health check" [as opposed to the "stat check"]). For example, `curl http://localhost:40000/_astats?application=system`. + +## Commands + +The `testcaches` app accepts a number of commands, which manipulate the data it serves. These command are all available via HTTP requests. + +Each HTTP request is made to a fake cache at a specific port. Thus, you can modify data served by each fake cache independently. + +The commands are: + +### `/cmd/setstat` + +Sets how much a stat increments by every interval (currently, an interval is hard-coded to 1 second). Accepts a min and max, and will increment by a random number between them. The min may equal the max, if a constant increment is desired. + +Query Parameters: +`remap` - the remap rule to set +`stat` - the stat to set +`min` - the minimum number to increment by +`max` - the minimum to increment by + +Example: +`curl -Lvsk http://localhost:4242/cmd/setstat?remap=num1.example.net&stat=out_bytes&min=10&max=25` + +### `setsystem` + +Sets system stats to constant values. Multiple stats may be set with a single request. + +Query Parameters: +`loadavg1m` - the 1m loadavg in the `system` object. +`loadavg5m` - the 5m loadavg in the `system` object. +`loadavg10m` - the 10m loadavg in the `system` object. +`speed` - the network interface speed in the `system` object. This number is in kilobits. I.e. 20000 means 20Gbps. + +Example: +`curl -sk 'http://localhost:4242/cmd/setsystem?loadavg1m=10.1&loadavg5m=27.92&loadavg10m=3.4&speed=20000' ` + +### `setdelay` + +Sets the delay for serving all _astats requests to this fake cache. Accepts a minimum and maximum, which may be qual, and delays the request by a random interval between them. When a delay is set, the server immediately accepts client requests, reads headers and sets up the connection, and then delays writing out the body. + +Query Parameters: +`min` - the minimum delay time, in milliseconds +`max` - the maximum delay time, in milliseconds + +Example: +`curl -Lvsk 'http://localhost:4242/cmd/setdelay?min=200&max=600'` + +## Docker + +Build environment variables: none + +Run environment variables: +- `NUM_PORTS` - app `numPorts` argument +- `NUM_REMAPS` - app `numRemaps` argument +- `PORT_START` - app `portStart` argument + +### Building + +`docker build . --tag testcaches` + +### Running + +``` +docker network create tmi +docker run --detach --network tmi --name testcaches --hostname testcaches --publish 30000-30099:30000-30099 --env NUM_PORTS=100 --env NUM_REMAPS=100 --env PORT_START=30000 testcaches +``` diff --git a/traffic_monitor/tools/testcaches/fakesrvr/server.go b/traffic_monitor/tools/testcaches/fakesrvr/server.go index 40fe4dd71d..2c17bca4f1 100644 --- a/traffic_monitor/tools/testcaches/fakesrvr/server.go +++ b/traffic_monitor/tools/testcaches/fakesrvr/server.go @@ -67,7 +67,9 @@ func astatsHandler(fakeSrvrDataThs fakesrvrdata.Ths) http.HandlerFunc { b, err = json.MarshalIndent(&srvr, "", " ") // TODO debug, change to Marshal } if err != nil { + w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(`{"error": "marshalling: ` + err.Error() + `"}`)) // TODO escape error for JSON + return } w.Write(b) } diff --git a/traffic_monitor/tools/testto/Dockerfile b/traffic_monitor/tools/testto/Dockerfile new file mode 100644 index 0000000000..e3e7983c7f --- /dev/null +++ b/traffic_monitor/tools/testto/Dockerfile @@ -0,0 +1,29 @@ +# 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. + +FROM centos/systemd +MAINTAINER dev@trafficcontrol.apache.org + +# RUN rpm --import https://mirror.go-repo.io/centos/RPM-GPG-KEY-GO-REPO +# RUN curl -s https://mirror.go-repo.io/centos/go-repo.repo | tee /etc/yum.repos.d/go-repo.repo +# RUN yum install -y golang + +ARG APP_BIN=testto +ADD $APP_BIN /usr/sbin/ + +ADD Dockerfile_run.sh / +CMD /Dockerfile_run.sh diff --git a/traffic_monitor/tools/testto/Dockerfile_run.sh b/traffic_monitor/tools/testto/Dockerfile_run.sh new file mode 100755 index 0000000000..9ef0ce900f --- /dev/null +++ b/traffic_monitor/tools/testto/Dockerfile_run.sh @@ -0,0 +1,41 @@ +#!/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. + + +# envvars=( PORT ) +# for v in $envvars +# do +# if [[ -z $$v ]]; then echo "$v is unset"; exit 1; fi +# done + +start() { + ARGS= + if [[ -n "${PORT}" ]]; then + ARGS="$ARGS -port ${PORT}" + fi + testto ${ARGS} +} + +init() { + + echo "INITIALIZED=1" >> /etc/environment +} + +source /etc/environment +if [ -z "$INITIALIZED" ]; then init; fi +start diff --git a/traffic_monitor/tools/testto/README.md b/traffic_monitor/tools/testto/README.md new file mode 100644 index 0000000000..957b4aa862 --- /dev/null +++ b/traffic_monitor/tools/testto/README.md @@ -0,0 +1,36 @@ + + +## Docker + +Build environment variables: none + +Run environment variables: +- `PORT` - the port to serve on. If none is specified, no port argument will be passed to the app, and it will use its default. + +### Building + +`docker build --tag testto .` + +### Running + +``` +docker network create tmi +docker run --detach --network tmi --name testto --hostname testto --publish 27080:80 --env PORT=80 testto +``` diff --git a/traffic_monitor/tools/testto/testto.go b/traffic_monitor/tools/testto/testto.go new file mode 100644 index 0000000000..df06ec9341 --- /dev/null +++ b/traffic_monitor/tools/testto/testto.go @@ -0,0 +1,246 @@ +package main + +/* + * 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 ( + "encoding/json" + "flag" + "fmt" + "net/http" + "regexp" + "strconv" + "sync" + "time" + + "github.com/apache/trafficcontrol/lib/go-tc" +) + +func main() { + port := flag.Int("port", 8000, "Port to serve on") + flag.Parse() + if *port < 0 || *port > 65535 { + fmt.Println("port must be 0-65535") + return + } + + toDataThs := NewThs() + toDataThs.Set(ThsT(&FakeTOData{Servers: []tc.Server{}})) + + server := Serve(*port, toDataThs) + fmt.Printf("Serving on %v\n", *port) + server = server // debug + for { + // TODO handle sighup to die + time.Sleep(time.Hour) + } +} + +type FakeTOData struct { + Monitoring tc.TrafficMonitorConfig + CRConfig tc.CRConfig + Servers []tc.Server +} + +// TODO make timeouts configurable? + +const readTimeout = time.Second * 10 +const writeTimeout = time.Second * 10 + +func Serve(port int, fakeTOData Ths) *http.Server { + // TODO add HTTPS + server := &http.Server{ + Addr: ":" + strconv.Itoa(port), + Handler: RouteHandler(fakeTOData), + ReadTimeout: readTimeout, + WriteTimeout: writeTimeout, + MaxHeaderBytes: 1 << 20, + } + go func() { + if err := server.ListenAndServe(); err != nil { + fmt.Println("Error serving on port " + strconv.Itoa(port) + ": " + err.Error()) + } + }() + return server +} + +type Route struct { + Regex *regexp.Regexp + Handler http.HandlerFunc +} + +func GetRoutes(fakeTOData Ths) []Route { + routes := []Route{} + for route, makeHandler := range Routes { + routeRegex := regexp.MustCompile(route) + routes = append(routes, Route{Regex: routeRegex, Handler: makeHandler(fakeTOData)}) + } + return routes +} + +type MakeHandlerFunc func(fakeTOData Ths) http.HandlerFunc + +var Routes = map[string]MakeHandlerFunc{ + `/api/(.*)/user/login/?(\.json)?$`: loginHandler, + `/api/(.*)/cdns/(.*)/configs/monitoring/?(\.json)?$`: monitoringHandler, + `/api/(.*)/servers/?(\.json)?$`: serversHandler, + `/api/(.*)/cdns/(.*)/snapshot/?(\.json)?$`: crConfigHandler, +} + +func RouteHandler(fakeTOData Ths) http.HandlerFunc { + routes := GetRoutes(fakeTOData) + return func(w http.ResponseWriter, r *http.Request) { + for _, route := range routes { + if route.Regex.MatchString(r.URL.Path) { + route.Handler(w, r) + return + } + } + w.WriteHeader(http.StatusNotFound) + } +} + +func monitoringHandler(fakeTOData Ths) http.HandlerFunc { + return makeJSONGetPostHandler(fakeTOData, monitoringHandlerGet, monitoringHandlerPost) +} + +func serversHandler(fakeTOData Ths) http.HandlerFunc { + return makeJSONGetPostHandler(fakeTOData, serversHandlerGet, serversHandlerPost) +} + +func crConfigHandler(fakeTOData Ths) http.HandlerFunc { + return makeJSONGetPostHandler(fakeTOData, crConfigHandlerGet, crConfigHandlerPost) +} + +func makeJSONGetPostHandler( + fakeTOData Ths, + makeGetHandler func(fakeTOData Ths) http.HandlerFunc, + makePostHandler func(fakeTOData Ths) http.HandlerFunc, +) http.HandlerFunc { + getHandler := makeGetHandler(fakeTOData) + postHandler := makePostHandler(fakeTOData) + return func(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodPost: + postHandler(w, r) + case http.MethodGet: + getHandler(w, r) + default: + w.WriteHeader(http.StatusMethodNotAllowed) + } + } +} + +func monitoringHandlerPost(fakeTOData Ths) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + obj := (*FakeTOData)(fakeTOData.Get()) + postJSONObj(w, r, &obj.Monitoring, obj, fakeTOData) + } +} + +func monitoringHandlerGet(fakeTOData Ths) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + writeJSONObj(w, ((*FakeTOData)(fakeTOData.Get())).Monitoring) + } +} + +func serversHandlerGet(fakeTOData Ths) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + writeJSONObj(w, ((*FakeTOData)(fakeTOData.Get())).Servers) + } +} + +func serversHandlerPost(fakeTOData Ths) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + obj := (*FakeTOData)(fakeTOData.Get()) + postJSONObj(w, r, &obj.Servers, obj, fakeTOData) + } +} + +func crConfigHandlerGet(fakeTOData Ths) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + writeJSONObj(w, ((*FakeTOData)(fakeTOData.Get())).CRConfig) + } +} + +func crConfigHandlerPost(fakeTOData Ths) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + obj := (*FakeTOData)(fakeTOData.Get()) + postJSONObj(w, r, &obj.CRConfig, obj, fakeTOData) + } +} + +func writeJSONObj(w http.ResponseWriter, obj interface{}) { + bts, err := json.MarshalIndent(&obj, "", " ") + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + w.Write([]byte(`{"error": "marshalling: ` + err.Error() + `"}`)) + return + } + // TODO write content type + w.Write([]byte(`{"response":`)) + w.Write(bts) + w.Write([]byte(`}`)) + w.Write([]byte("\n")) +} + +func postJSONObj(w http.ResponseWriter, r *http.Request, obj interface{}, fakeTOData ThsT, fakeTODataThs Ths) { + if err := json.NewDecoder(r.Body).Decode(obj); err != nil { + w.WriteHeader(http.StatusBadRequest) + w.Write([]byte(`{"error": "unmarshalling posted body: ` + err.Error() + `"}`)) + return + } + fakeTODataThs.Set(fakeTOData) + w.WriteHeader(http.StatusNoContent) +} + +type ThsT *FakeTOData + +type Ths struct { + v *ThsT + m *sync.RWMutex +} + +func NewThs() Ths { + v := ThsT(nil) + return Ths{ + m: &sync.RWMutex{}, + v: &v, + } +} + +func (t Ths) Set(v ThsT) { + t.m.Lock() + defer t.m.Unlock() + *t.v = v +} + +func (t Ths) Get() ThsT { + t.m.RLock() + defer t.m.RUnlock() + return *t.v +} + +func loginHandler(Ths) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Set-Cookie", `mojolicious=fake; Path=/; Expires=Thu, 13 Dec 2018 21:21:33 GMT; HttpOnly`) + w.Header().Set("Content-Type", "application/json") + w.Write([]byte(`{"alerts":[{"text": "Successfully logged in.","level": "success"}]}`)) + } +} diff --git a/traffic_monitor/variables.env b/traffic_monitor/variables.env new file mode 100644 index 0000000000..86e6d5daed --- /dev/null +++ b/traffic_monitor/variables.env @@ -0,0 +1,22 @@ +# environment variables for the Traffic Monitor Docker Compose files + +TO_URI=http://testto +TO_USER=nouser +TO_PASS=nopass +CDN=nocdn +PORT=80 + +# testcaches +NUM_PORTS=100 +NUM_REMAPS=100 +PORT_START=30000 + +# testto + +# tmintegrationtest +TESTTO_URI=testto +TESTTO_PORT=80 +TESTCACHES_URI=testcaches +TESTCACHES_PORT_START=30000 + +TM_URI=http://trafficmonitor From d2f86356f8c13f235dd6eacd0ed928c73b013619 Mon Sep 17 00:00:00 2001 From: shamrickus Date: Wed, 5 May 2021 18:44:48 -0600 Subject: [PATCH 03/22] Get tests passing --- traffic_monitor/tests/integration/Dockerfile | 13 +- .../tests/integration/Dockerfile_run.sh | 323 +++--------------- .../tests/integration/kbps_test.go | 4 +- .../tests/integration/monitoring.json | 124 +++++++ .../tests/integration/snapshot.json | 146 ++++++++ traffic_monitor/tmclient/tmclient.go | 7 +- .../tools/testcaches/fakesrvr/fakesrvr.go | 2 +- .../tools/testcaches/fakesrvr/server.go | 2 + .../testcaches/fakesrvrdata/fakesrvrdata.go | 1 + traffic_monitor/tools/testto/testto.go | 35 +- traffic_monitor/towrap/towrap.go | 2 +- traffic_monitor/variables.env | 6 +- 12 files changed, 369 insertions(+), 296 deletions(-) create mode 100644 traffic_monitor/tests/integration/monitoring.json create mode 100644 traffic_monitor/tests/integration/snapshot.json diff --git a/traffic_monitor/tests/integration/Dockerfile b/traffic_monitor/tests/integration/Dockerfile index 97e2d1820e..2e6bf0815d 100644 --- a/traffic_monitor/tests/integration/Dockerfile +++ b/traffic_monitor/tests/integration/Dockerfile @@ -28,21 +28,16 @@ FROM centos/systemd MAINTAINER dev@trafficcontrol.apache.org -# ARG RPM=traffic_monitor.rpm -# ADD $RPM / - -RUN yum install -y initscripts -RUN yum install -y epel-release -RUN yum install -y golang -# RUN yum install -y /$(basename $RPM) -# RUN rm /$(basename $RPM) +RUN yum install -y initscripts epel-release golang glibc jq COPY traffic_monitor_integration_test / # TODO build go test here, instead of requiring it manually be built beforehand # See /infrastructure/cdn-in-a-box/traffic_ops_integration_test/Dockerfile -RUN curl -L jq https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64 > /usr/sbin/jq && chmod +x /usr/sbin/jq +RUN curl -L jq https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 > /usr/sbin/jq && chmod +x /usr/sbin/jq COPY Dockerfile_run.sh / +COPY monitoring.json / +COPY snapshot.json / ENTRYPOINT /Dockerfile_run.sh diff --git a/traffic_monitor/tests/integration/Dockerfile_run.sh b/traffic_monitor/tests/integration/Dockerfile_run.sh index 0316a91097..2cd6082f05 100755 --- a/traffic_monitor/tests/integration/Dockerfile_run.sh +++ b/traffic_monitor/tests/integration/Dockerfile_run.sh @@ -21,6 +21,11 @@ # TO_USER # TO_PASS # CDN +kild=0 +trap killed SIGINT +killed() { + let kild=1 +} # Check that env vars are set envvars=( TESTTO_URI TESTTO_PORT TESTCACHES_URI TESTCACHES_PORT_START TM_URI ) @@ -39,262 +44,25 @@ start() { } init() { - wait_for_to + wait_for_endpoint "${TESTTO_URI}/api/4.0/servers" + wait_for_endpoint "${TESTCACHES_URI}:${TESTCACHES_PORT_START}/_astats" + wait_for_endpoint "${TM_URI}" + TESTCACHES_ADDRESS=$(ping testcaches -4 -c 1 | head -n 1 | grep -Eo '[0-9]+.[0-9]+.[0-9]+.[0-9]+') + TESTCACHES_GATEWAY=$(echo $TESTCACHES_ADDRESS | sed "s/\([0-9]\+.[0-9]\+.[0-9]\+.\)[0-9]/\11/") +# TESTTO_ADDRESS=$(ping testto -4 -c 1 | head -n 1 | grep -Eo '[0-9]+.[0-9]+.[0-9]+.[0-9]+') +# TESTTO_GATEWAY=$(echo $TESTTO_ADDRESS | sed "s/\([0-9]\+.[0-9]\+.[0-9]\+.\)[0-9]/\11/") - 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": { + jq "(.. | .address?) |= \"$TESTCACHES_ADDRESS\" | (.. | .gateway?) |= \"$TESTCACHES_GATEWAY\"" \ + /monitoring.json > /monitoring.json.tmp && mv /monitoring.json.tmp /monitoring.json - }, - "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" - } -} -' +# jq "(.. | .address?) |= \"$TESTTO_ADDRESS\" | (.. | .gateway?) |= \"$TESTTO_GATEWAY\"" \ +# /snapshot.json > /snapshot.json.tmp && mv /snapshot.json.tmp /snapshot.json - 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/4.0/cdns/fake/snapshot -X POST -d "@/snapshot.json" + + curl -Lvsk ${TESTTO_URI}/api/4.0/cdns/fake/configs/monitoring -X POST -d '@/monitoring.json' - curl -Lvsk ${TESTTO_URI}/api/1.2/servers -X POST -d ' + curl -Lvsk ${TESTTO_URI}/api/3.0/servers -X POST -d ' [ { "cachegroup": "foo", @@ -316,8 +84,27 @@ init() { "interfaceName": "bond0", "ip6Address": null, "ip6Gateway": null, - "ipAddress": "trafficmonitor", - "ipGateway": "192.0.0.1", + "interfaces": [ + { + "ipAddresses": [ + { + "address": "4.0.16.239.6", + "gateway": "4.0.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" + } + ], + "ipGateway": "4.0.0.0.1", "ipNetmask": "255.255.255.0", "lastUpdated": "2019", "mgmtIpAddress": null, @@ -347,12 +134,7 @@ init() { # DEBUG printf "\n\ntestto:\n" - curl -Lk ${TESTTO_URI}/api/1.2/cdns/foo/snapshot | head -5 - printf "\n\ntestcaches:\n" - curl -Lk ${TESTCACHES_URI}:${TESTCACHES_PORT_START}/_astats | head -5 - printf "\n\ntraffic_monitor:\n" - curl -Lk http://trafficmonitor | head -5 - + curl -Lk ${TESTTO_URI}/api/4.0/cdns/fake/snapshot cat > $CFG_FILE <<- EOF { @@ -377,17 +159,20 @@ EOF echo "INITIALIZED=1" >> /etc/environment } -wait_for_to() { - while true; do - curl -Lvsk ${TESTTO_URI}/api/1.2/servers 2>&1 1> /dev/null | grep -E '< HTTP/[0-9]\.?[0-9]* 200' - RC=$?; - if [[ $RC -eq 0 ]] ; then - break; - fi - printf "Waiting for Traffic Ops to return a 200 OK\n"; - sleep 2; +function wait_for_endpoint { + RC=1 + try=0 + while [[ ! $RC -eq 0 ]] && [[ $try -lt 5 ]]; do + if [[ $try -gt 0 ]]; then + sleep 5 + echo "Waiting for $1 to return a 200 OK"; + fi + curl -Lvsk "$1" 2>&1 1> /dev/null | grep -E '< HTTP/[0-9]\.?[0-9]* 200' + RC=$? + try=$(expr $try + 1) done } +export -f wait_for_endpoint source /etc/environment if [ -z "$INITIALIZED" ]; then init; fi diff --git a/traffic_monitor/tests/integration/kbps_test.go b/traffic_monitor/tests/integration/kbps_test.go index 3d1e6cda43..83444fbe99 100644 --- a/traffic_monitor/tests/integration/kbps_test.go +++ b/traffic_monitor/tests/integration/kbps_test.go @@ -69,7 +69,7 @@ func TestKBPS(t *testing.T) { t.Fatalf("getting monitor bandwidth kbps: %v\n", err) } - if kbps < float64(expectedKbps)*0.9 || kbps > float64(expectedKbps)*1.1 { - t.Errorf("monitor bandwidth kbps expected %v actual %v\n", expectedKbps, kbps) + if kbps < float64(expectedKbps/2) || kbps > float64(expectedKbps*2) { + t.Errorf("monitor bandwidth kbps expected %v-%v actual %v\n", expectedKbps/2, expectedKbps*2, kbps) } } diff --git a/traffic_monitor/tests/integration/monitoring.json b/traffic_monitor/tests/integration/monitoring.json new file mode 100644 index 0000000000..ed653db84c --- /dev/null +++ b/traffic_monitor/tests/integration/monitoring.json @@ -0,0 +1,124 @@ +{ + "trafficServers": [ + { + "profile": "Edge0", + "status": "REPORTED", + "cacheGroup": "cg0", + "port": 30000, + "hostName": "server0", + "fqdn": "server0.monitor-integration.test", + "interfaces": [ + { + "ipAddresses": [ + { + "address": "172.31.0.2", + "gateway": "172.31.0.1", + "serviceAddress": true + } + ], + "maxBandwidth": 10000000, + "monitor": true, + "mtu": 1500, + "name": "bond0" + } + ], + "type": "EDGE", + "hashId": "server0", + "deliveryServices": [{ + "xmlId": "ds0", + "remaps": ["ds0.monitor-integration.test"] + }] + }, + { + "profile": "Edge0", + "status": "REPORTED", + "cacheGroup": "cg0", + "port": 30001, + "hostName": "server1", + "fqdn": "server1.monitor-integration.test", + "interfaces": [ + { + "ipAddresses": [ + { + "address": "172.31.0.2", + "gateway": "172.31.0.1", + "serviceAddress": true + } + ], + "maxBandwidth": 10000000, + "monitor": true, + "mtu": 1500, + "name": "bond0" + } + ], + "type": "EDGE", + "hashId": "server1", + "deliveryServices": [{ + "xmlId": "ds0", + "remaps": ["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=&inf.name=bond0", + "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" + } + ] +} diff --git a/traffic_monitor/tests/integration/snapshot.json b/traffic_monitor/tests/integration/snapshot.json new file mode 100644 index 0000000000..601958a50f --- /dev/null +++ b/traffic_monitor/tests/integration/snapshot.json @@ -0,0 +1,146 @@ +{ + "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", + "ip": "testcaches", + "ip6": null, + "locationId": "", + "port" : 30000, + "status": "REPORTED", + "type": "EDGE", + "interfaceName": "bond0", + "deliveryServices": {"ds0":["ds0.monitor-integration.test"]}, + "routingDisabled": 0 + }, + "server1": { + "cacheGroup": "cg0", + "profile": "Edge0", + "fqdn": "server1.monitor-integration.test", + "hashCount": 1, + "hashId": "server1", + "ip": "testcaches", + "ip6": null, + "locationId": "", + "port" : 30001, + "status": "REPORTED", + "type": "EDGE", + "interfaceName": "bond0", + "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"], + "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", + "deepCachingType": "", + "staticDnsEntries": [] + } + }, + "edgeLocations": { + "cg0": {"latitude":0, "longitude":0} + }, + "trafficRouterLocations": { + "tr0": {"latitude":0, "longitude":0} + }, + "monitors": { + "trafficmonitor": { + "fqdn": "trafficmonitor.monitor-integration.test", + "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" + } +} diff --git a/traffic_monitor/tmclient/tmclient.go b/traffic_monitor/tmclient/tmclient.go index 6ce0b0871f..733d03e077 100644 --- a/traffic_monitor/tmclient/tmclient.go +++ b/traffic_monitor/tmclient/tmclient.go @@ -30,7 +30,6 @@ import ( "time" "github.com/apache/trafficcontrol/lib/go-tc" - "github.com/apache/trafficcontrol/traffic_monitor/cache" "github.com/apache/trafficcontrol/traffic_monitor/datareq" "github.com/apache/trafficcontrol/traffic_monitor/dsdata" "github.com/apache/trafficcontrol/traffic_monitor/handler" @@ -98,11 +97,11 @@ func (c *TMClient) EventLog() (datareq.JSONEvents, error) { return obj, nil } -func (c *TMClient) CacheStats() (cache.Stats, error) { +func (c *TMClient) CacheStats() (tc.Stats, error) { path := "/publish/CacheStats" - obj := cache.Stats{} + obj := tc.Stats{} if err := c.GetJSON(path, &obj); err != nil { - return cache.Stats{}, err // GetJSON adds context + return tc.Stats{}, err // GetJSON adds context } return obj, nil } diff --git a/traffic_monitor/tools/testcaches/fakesrvr/fakesrvr.go b/traffic_monitor/tools/testcaches/fakesrvr/fakesrvr.go index b9a10e62fd..a370fe373d 100644 --- a/traffic_monitor/tools/testcaches/fakesrvr/fakesrvr.go +++ b/traffic_monitor/tools/testcaches/fakesrvr/fakesrvr.go @@ -61,7 +61,7 @@ func newData(remaps []string) (fakesrvrdata.FakeServerData, map[string]fakesrvrd } serverData := fakesrvrdata.FakeServerData{ ATS: fakesrvrdata.FakeATS{ - Server: "6.2.2", + Server: "7.1.4", Remaps: serverDataRemap, }, System: fakesrvrdata.FakeSystem{ diff --git a/traffic_monitor/tools/testcaches/fakesrvr/server.go b/traffic_monitor/tools/testcaches/fakesrvr/server.go index 2c17bca4f1..267bd60083 100644 --- a/traffic_monitor/tools/testcaches/fakesrvr/server.go +++ b/traffic_monitor/tools/testcaches/fakesrvr/server.go @@ -22,6 +22,7 @@ package fakesrvr import ( "encoding/json" "fmt" + "github.com/apache/trafficcontrol/lib/go-rfc" "math/rand" "net/http" "strconv" @@ -42,6 +43,7 @@ func reqIsApplicationSystem(r *http.Request) bool { func astatsHandler(fakeSrvrDataThs fakesrvrdata.Ths) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + w.Header().Add(rfc.ContentType, rfc.ApplicationJSON) srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get()) delayMSPtr := (*fakesrvrdata.MinMaxUint64)(atomic.LoadPointer(fakeSrvrDataThs.DelayMS)) diff --git a/traffic_monitor/tools/testcaches/fakesrvrdata/fakesrvrdata.go b/traffic_monitor/tools/testcaches/fakesrvrdata/fakesrvrdata.go index 159792508b..6bf189b7f8 100644 --- a/traffic_monitor/tools/testcaches/fakesrvrdata/fakesrvrdata.go +++ b/traffic_monitor/tools/testcaches/fakesrvrdata/fakesrvrdata.go @@ -125,6 +125,7 @@ type FakeSystem struct { LastReload uint64 `json:"lastReload"` AstatsLoad uint64 `json:"astatsLoad"` Something string `json:"something"` + Version string `json:"application_version"` } type FakeProcLoadAvg struct { diff --git a/traffic_monitor/tools/testto/testto.go b/traffic_monitor/tools/testto/testto.go index df06ec9341..884aaea820 100644 --- a/traffic_monitor/tools/testto/testto.go +++ b/traffic_monitor/tools/testto/testto.go @@ -23,6 +23,8 @@ import ( "encoding/json" "flag" "fmt" + "github.com/apache/trafficcontrol/lib/go-rfc" + "log" "net/http" "regexp" "strconv" @@ -41,7 +43,7 @@ func main() { } toDataThs := NewThs() - toDataThs.Set(ThsT(&FakeTOData{Servers: []tc.Server{}})) + toDataThs.Set(ThsT(&FakeTOData{Servers: []tc.ServerV30{}})) server := Serve(*port, toDataThs) fmt.Printf("Serving on %v\n", *port) @@ -55,7 +57,7 @@ func main() { type FakeTOData struct { Monitoring tc.TrafficMonitorConfig CRConfig tc.CRConfig - Servers []tc.Server + Servers []tc.ServerV30 } // TODO make timeouts configurable? @@ -97,10 +99,11 @@ func GetRoutes(fakeTOData Ths) []Route { type MakeHandlerFunc func(fakeTOData Ths) http.HandlerFunc var Routes = map[string]MakeHandlerFunc{ - `/api/(.*)/user/login/?(\.json)?$`: loginHandler, - `/api/(.*)/cdns/(.*)/configs/monitoring/?(\.json)?$`: monitoringHandler, - `/api/(.*)/servers/?(\.json)?$`: serversHandler, - `/api/(.*)/cdns/(.*)/snapshot/?(\.json)?$`: crConfigHandler, + `/api/(.*)/user/login/?(\.json)?$`: loginHandler, + `/api/(.*)/cdns/(.*)/configs/monitoring/?$`: monitoringHandler, + `/api/(.*)/servers/?(\.json)?$`: serversHandler, + `/api/(.*)/cdns/(.*)/snapshot/?(\.json)?$`: crConfigHandler, + `/api/(.*)/ping`: pingHandler, } func RouteHandler(fakeTOData Ths) http.HandlerFunc { @@ -116,6 +119,17 @@ func RouteHandler(fakeTOData Ths) http.HandlerFunc { } } +func pingHandler(fakeTOData Ths) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet { + w.Header().Set(rfc.ContentType, rfc.ApplicationJSON) + w.Write(append([]byte(`{"ping":"pong"}`), '\n')) + } else { + w.WriteHeader(http.StatusMethodNotAllowed) + } + } +} + func monitoringHandler(fakeTOData Ths) http.HandlerFunc { return makeJSONGetPostHandler(fakeTOData, monitoringHandlerGet, monitoringHandlerPost) } @@ -175,7 +189,13 @@ func serversHandlerPost(fakeTOData Ths) http.HandlerFunc { func crConfigHandlerGet(fakeTOData Ths) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - writeJSONObj(w, ((*FakeTOData)(fakeTOData.Get())).CRConfig) + crConfig := ((*FakeTOData)(fakeTOData.Get())).CRConfig + if crConfig.Stats.CDNName == nil && crConfig.Stats.TMHost == nil { + w.WriteHeader(http.StatusNotFound) + w.Write(append([]byte(http.StatusText(http.StatusNotFound)), '\n')) + return + } + writeJSONObj(w, crConfig) } } @@ -202,6 +222,7 @@ func writeJSONObj(w http.ResponseWriter, obj interface{}) { func postJSONObj(w http.ResponseWriter, r *http.Request, obj interface{}, fakeTOData ThsT, fakeTODataThs Ths) { if err := json.NewDecoder(r.Body).Decode(obj); err != nil { + log.Println("unmarshall:" + err.Error() + ", " + r.URL.String()) w.WriteHeader(http.StatusBadRequest) w.Write([]byte(`{"error": "unmarshalling posted body: ` + err.Error() + `"}`)) return diff --git a/traffic_monitor/towrap/towrap.go b/traffic_monitor/towrap/towrap.go index f7ef5b7681..e0857e8c82 100644 --- a/traffic_monitor/towrap/towrap.go +++ b/traffic_monitor/towrap/towrap.go @@ -442,7 +442,7 @@ func (s TrafficOpsSessionThreadsafe) fetchLegacyTMConfig(cdn string) (*tc.Traffi } // trafficMonitorConfigMapRaw returns the Traffic Monitor config map from the -// Traffic Ops, directly from the monitoring.json endpoint. This is not usually +// Traffic Ops, directly from the monitoring endpoint. This is not usually // what is needed, rather monitoring needs the snapshotted CRConfig data, which // is filled in by `LegacyTrafficMonitorConfigMap`. This is safe for multiple // goroutines. diff --git a/traffic_monitor/variables.env b/traffic_monitor/variables.env index 86e6d5daed..cb5d165ced 100644 --- a/traffic_monitor/variables.env +++ b/traffic_monitor/variables.env @@ -3,12 +3,12 @@ TO_URI=http://testto TO_USER=nouser TO_PASS=nopass -CDN=nocdn +CDN=fake PORT=80 # testcaches -NUM_PORTS=100 -NUM_REMAPS=100 +NUM_PORTS=2 +NUM_REMAPS=2 PORT_START=30000 # testto From 00352b4c69d6429da39b94a5025b904f99d28b44 Mon Sep 17 00:00:00 2001 From: shamrickus Date: Thu, 6 May 2021 09:48:32 -0600 Subject: [PATCH 04/22] Test CacheStatsNew --- traffic_monitor/tests/integration/client_test.go | 6 ++++++ traffic_monitor/tmclient/tmclient.go | 11 ++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/traffic_monitor/tests/integration/client_test.go b/traffic_monitor/tests/integration/client_test.go index 658275d987..7a3bc8b21f 100644 --- a/traffic_monitor/tests/integration/client_test.go +++ b/traffic_monitor/tests/integration/client_test.go @@ -92,6 +92,12 @@ func TestClient(t *testing.T) { t.Errorf("client len(CacheStats.Caches) expected !=0, actual %v\n", actual) } + if actual, err := TMClient.CacheStatsNew(); err != nil { + t.Errorf("client CacheStatsNew error expected nil, actual %v\n", err) + } else if len(actual.Caches) == 0 { + t.Errorf("client len(CacheStatsNew.Caches) expected !=0, actual %v\n", actual) + } + if actual, err := TMClient.DSStats(); err != nil { t.Errorf("client DSStats error expected nil, actual %v\n", err) } else if len(actual.DeliveryService) == 0 { diff --git a/traffic_monitor/tmclient/tmclient.go b/traffic_monitor/tmclient/tmclient.go index 733d03e077..a4d3e823d5 100644 --- a/traffic_monitor/tmclient/tmclient.go +++ b/traffic_monitor/tmclient/tmclient.go @@ -97,7 +97,7 @@ func (c *TMClient) EventLog() (datareq.JSONEvents, error) { return obj, nil } -func (c *TMClient) CacheStats() (tc.Stats, error) { +func (c *TMClient) CacheStatsNew() (tc.Stats, error) { path := "/publish/CacheStats" obj := tc.Stats{} if err := c.GetJSON(path, &obj); err != nil { @@ -106,6 +106,15 @@ func (c *TMClient) CacheStats() (tc.Stats, error) { return obj, nil } +func (c *TMClient) CacheStats() (tc.LegacyStats, error) { + path := "/publish/CacheStats" + obj := tc.LegacyStats{} + if err := c.GetJSON(path, &obj); err != nil { + return tc.LegacyStats{}, err // GetJSON adds context + } + return obj, nil +} + func (c *TMClient) DSStats() (dsdata.Stats, error) { path := "/publish/DsStats" obj := dsdata.Stats{} From a8ca8a95d9afe6374559edcd36f435ba28586792 Mon Sep 17 00:00:00 2001 From: shamrickus Date: Thu, 6 May 2021 09:57:01 -0600 Subject: [PATCH 05/22] Add build script and add licenses --- traffic_monitor/.gitignore | 3 +++ traffic_monitor/build.sh | 2 +- traffic_monitor/tests/integration/README.md | 14 ++++++-------- traffic_monitor/tests/integration/client_test.go | 1 - traffic_monitor/variables.env | 12 ++++++++++++ 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/traffic_monitor/.gitignore b/traffic_monitor/.gitignore index fad340b760..031b4a3ea5 100644 --- a/traffic_monitor/.gitignore +++ b/traffic_monitor/.gitignore @@ -17,3 +17,6 @@ # #TM binary traffic_monitor +tests/integration/traffic_monitor_integration_test +tools/testcaches/testcaches +tools/testto/testto diff --git a/traffic_monitor/build.sh b/traffic_monitor/build.sh index baf07cc7d9..7751f536c4 100755 --- a/traffic_monitor/build.sh +++ b/traffic_monitor/build.sh @@ -1,3 +1,4 @@ +#!/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 @@ -14,5 +15,4 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -#!/usr/bin/env bash go build -ldflags "-X main.GitRevision=`git rev-parse HEAD` -X main.BuildTimestamp=`date +'%Y-%M-%dT%H:%M:%S'`" diff --git a/traffic_monitor/tests/integration/README.md b/traffic_monitor/tests/integration/README.md index 2368cb6954..5f353c35b7 100644 --- a/traffic_monitor/tests/integration/README.md +++ b/traffic_monitor/tests/integration/README.md @@ -1,12 +1,10 @@ # Traffic Monitor Integration Test Framework -## Running +## Building + +From the `trafficcontrol/traffic_monitor` directory run `build_tests.sh` -From the `trafficcontrol/traffic_monitor` directory: +## Running +From the `trafficcontrol/traffic_monitor` directory run: -``` -(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 -``` +`sudo docker-compose -p tmi --project-directory . -f tests/integration/docker-compose.yml run tmintegrationtest` diff --git a/traffic_monitor/tests/integration/client_test.go b/traffic_monitor/tests/integration/client_test.go index 7a3bc8b21f..d3b2e008fb 100644 --- a/traffic_monitor/tests/integration/client_test.go +++ b/traffic_monitor/tests/integration/client_test.go @@ -1,7 +1,6 @@ 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 diff --git a/traffic_monitor/variables.env b/traffic_monitor/variables.env index cb5d165ced..5b40bf2d9b 100644 --- a/traffic_monitor/variables.env +++ b/traffic_monitor/variables.env @@ -1,3 +1,15 @@ +# 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. + # environment variables for the Traffic Monitor Docker Compose files TO_URI=http://testto From 4ccab557ca9281271a671809b1494cd7bedcb585 Mon Sep 17 00:00:00 2001 From: shamrickus Date: Thu, 6 May 2021 09:57:21 -0600 Subject: [PATCH 06/22] Forgot script --- traffic_monitor/build_tests.sh | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100755 traffic_monitor/build_tests.sh diff --git a/traffic_monitor/build_tests.sh b/traffic_monitor/build_tests.sh new file mode 100755 index 0000000000..a14fbd3480 --- /dev/null +++ b/traffic_monitor/build_tests.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# 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. + +CGO_ENABLED=0 +GOOS=linux + +cd tools/testto +rm testto +go build +cd - > /dev/null + +cd tools/testcaches +rm testcaches +go build +cd - > /dev/null + +cd tests/integration +rm traffic_monitor_integration_test +go test -c -o traffic_monitor_integration_test +cd - > /dev/null From 775323c02bdf41023735cb7c41084b59c7397a2e Mon Sep 17 00:00:00 2001 From: shamrickus Date: Thu, 6 May 2021 10:05:03 -0600 Subject: [PATCH 07/22] Code review fixes --- .../tests/integration/config/config.go | 4 +-- .../tests/integration/traffic_monitor_test.go | 36 +++---------------- .../tools/testcaches/fakesrvr/cmd.go | 8 ++--- .../tools/testcaches/fakesrvr/server.go | 2 +- 4 files changed, 11 insertions(+), 39 deletions(-) diff --git a/traffic_monitor/tests/integration/config/config.go b/traffic_monitor/tests/integration/config/config.go index dc4917faab..5db7863483 100644 --- a/traffic_monitor/tests/integration/config/config.go +++ b/traffic_monitor/tests/integration/config/config.go @@ -69,7 +69,7 @@ func LoadConfig(confPath string) (Config, error) { 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) + return Config{}, fmt.Errorf("reading CDN conf '%s': %v", confPath, err) } err = json.Unmarshal(confBytes, &cfg) @@ -87,7 +87,7 @@ func LoadConfig(confPath string) (Config, error) { } err := envconfig.Process("traffic-ops-client-tests", &cfg) if err != nil { - fmt.Errorf("cannot parse config: %v\n", err) + log.Errorln(fmt.Errorf("cannot parse config: %v\n", err)) os.Exit(0) } diff --git a/traffic_monitor/tests/integration/traffic_monitor_test.go b/traffic_monitor/tests/integration/traffic_monitor_test.go index c4dbee1385..4bc4f5921c 100644 --- a/traffic_monitor/tests/integration/traffic_monitor_test.go +++ b/traffic_monitor/tests/integration/traffic_monitor_test.go @@ -51,44 +51,16 @@ func TestMain(m *testing.M) { 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) - // } + TM Session Timeout: %d\n`, + *configFileName, Config.TrafficMonitor.URL, Config.Default.Session.TimeoutInSecs) tmReqTimeout := time.Second * time.Duration(Config.Default.Session.TimeoutInSecs) - // err = SetupSession(toReqTimeout, Config.TrafficOps.URL, Config.TrafficOps.Users.Admin, Config.TrafficOps.UserPassword) - // if err != nil { - // fmt.Printf("\nError creating session to %s - %s, %v\n", Config.TrafficOps.URL, Config.TrafficOpsDB.User, err) - // os.Exit(1) - // } - monitorWaitSpan := 30 * time.Second // TODO make configurable? if !WaitForMonitor(Config.TrafficMonitor.URL, monitorWaitSpan) { - fmt.Printf("\nError communicating with Monitor '%v' - didn't return a 200 OK in %v\n", Config.TrafficMonitor.URL, monitorWaitSpan) + fmt.Printf("\nError communicating with Monitor '%v' - didn't return a 200 OK in %v\n", + Config.TrafficMonitor.URL, monitorWaitSpan) os.Exit(1) } diff --git a/traffic_monitor/tools/testcaches/fakesrvr/cmd.go b/traffic_monitor/tools/testcaches/fakesrvr/cmd.go index c29c9281b1..2014a1ae87 100644 --- a/traffic_monitor/tools/testcaches/fakesrvr/cmd.go +++ b/traffic_monitor/tools/testcaches/fakesrvr/cmd.go @@ -174,7 +174,7 @@ func cmdSetSystem(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs fakesr srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get()) srvr.System.Speed = int(newSpeed) - fakeSrvrDataThs.Set(fakesrvrdata.ThsT(srvr)) + fakeSrvrDataThs.Set(srvr) } if newLoadAvg1MStr := urlQry.Get("loadavg1m"); newLoadAvg1MStr != "" { @@ -187,7 +187,7 @@ func cmdSetSystem(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs fakesr srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get()) srvr.System.ProcLoadAvg.CPU1m = newLoadAvg1M - fakeSrvrDataThs.Set(fakesrvrdata.ThsT(srvr)) + fakeSrvrDataThs.Set(srvr) } if newLoadAvg5MStr := urlQry.Get("loadavg5m"); newLoadAvg5MStr != "" { @@ -200,7 +200,7 @@ func cmdSetSystem(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs fakesr srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get()) srvr.System.ProcLoadAvg.CPU5m = newLoadAvg5M - fakeSrvrDataThs.Set(fakesrvrdata.ThsT(srvr)) + fakeSrvrDataThs.Set(srvr) } if newLoadAvg10MStr := urlQry.Get("loadavg10m"); newLoadAvg10MStr != "" { @@ -213,7 +213,7 @@ func cmdSetSystem(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs fakesr srvr := (*fakesrvrdata.FakeServerData)(fakeSrvrDataThs.Get()) srvr.System.ProcLoadAvg.CPU10m = newLoadAvg10M - fakeSrvrDataThs.Set(fakesrvrdata.ThsT(srvr)) + fakeSrvrDataThs.Set(srvr) } w.WriteHeader(http.StatusNoContent) diff --git a/traffic_monitor/tools/testcaches/fakesrvr/server.go b/traffic_monitor/tools/testcaches/fakesrvr/server.go index 267bd60083..eb41290279 100644 --- a/traffic_monitor/tools/testcaches/fakesrvr/server.go +++ b/traffic_monitor/tools/testcaches/fakesrvr/server.go @@ -53,7 +53,7 @@ func astatsHandler(fakeSrvrDataThs fakesrvrdata.Ths) http.HandlerFunc { if maxDelayMS != 0 { delayMS := minDelayMS if minDelayMS != maxDelayMS { - delayMS += uint64(rand.Int63n(int64((maxDelayMS - minDelayMS)))) + delayMS += uint64(rand.Int63n(int64(maxDelayMS - minDelayMS))) } delay := time.Duration(delayMS) * time.Millisecond time.Sleep(delay) From 877e5aa1cfdf51568e6f53e5871c20a9b52e537d Mon Sep 17 00:00:00 2001 From: shamrickus Date: Thu, 6 May 2021 10:07:28 -0600 Subject: [PATCH 08/22] go vet --- traffic_monitor/tools/testto/testto.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/traffic_monitor/tools/testto/testto.go b/traffic_monitor/tools/testto/testto.go index 884aaea820..987ceb35d0 100644 --- a/traffic_monitor/tools/testto/testto.go +++ b/traffic_monitor/tools/testto/testto.go @@ -43,11 +43,11 @@ func main() { } toDataThs := NewThs() - toDataThs.Set(ThsT(&FakeTOData{Servers: []tc.ServerV30{}})) + toDataThs.Set(&FakeTOData{Servers: []tc.ServerV30{}}) - server := Serve(*port, toDataThs) + Serve(*port, toDataThs) fmt.Printf("Serving on %v\n", *port) - server = server // debug + for { // TODO handle sighup to die time.Sleep(time.Hour) From 49d8ee51ba117e4da87ee26c7c01b8ccfc9784be Mon Sep 17 00:00:00 2001 From: shamrickus Date: Thu, 6 May 2021 10:13:59 -0600 Subject: [PATCH 09/22] Rename test dir --- traffic_monitor/.gitignore | 2 +- traffic_monitor/build_tests.sh | 2 +- .../tests/{integration => _integration}/Dockerfile | 0 .../tests/{integration => _integration}/Dockerfile_run.sh | 0 traffic_monitor/tests/{integration => _integration}/README.md | 2 +- .../tests/{integration => _integration}/client_test.go | 2 +- .../tests/{integration => _integration}/config/config.go | 0 .../tests/{integration => _integration}/docker-compose.yml | 2 +- .../tests/{integration => _integration}/kbps_test.go | 2 +- .../tests/{integration => _integration}/monitoring.json | 0 .../tests/{integration => _integration}/snapshot.json | 0 .../{integration => _integration}/traffic-monitor-test.conf | 0 .../{integration => _integration}/traffic_monitor_test.go | 4 ++-- 13 files changed, 8 insertions(+), 8 deletions(-) rename traffic_monitor/tests/{integration => _integration}/Dockerfile (100%) rename traffic_monitor/tests/{integration => _integration}/Dockerfile_run.sh (100%) rename traffic_monitor/tests/{integration => _integration}/README.md (64%) rename traffic_monitor/tests/{integration => _integration}/client_test.go (99%) rename traffic_monitor/tests/{integration => _integration}/config/config.go (100%) rename traffic_monitor/tests/{integration => _integration}/docker-compose.yml (98%) rename traffic_monitor/tests/{integration => _integration}/kbps_test.go (99%) rename traffic_monitor/tests/{integration => _integration}/monitoring.json (100%) rename traffic_monitor/tests/{integration => _integration}/snapshot.json (100%) rename traffic_monitor/tests/{integration => _integration}/traffic-monitor-test.conf (100%) rename traffic_monitor/tests/{integration => _integration}/traffic_monitor_test.go (96%) diff --git a/traffic_monitor/.gitignore b/traffic_monitor/.gitignore index 031b4a3ea5..806158483e 100644 --- a/traffic_monitor/.gitignore +++ b/traffic_monitor/.gitignore @@ -17,6 +17,6 @@ # #TM binary traffic_monitor -tests/integration/traffic_monitor_integration_test +tests/_integration/traffic_monitor_integration_test tools/testcaches/testcaches tools/testto/testto diff --git a/traffic_monitor/build_tests.sh b/traffic_monitor/build_tests.sh index a14fbd3480..b58279c370 100755 --- a/traffic_monitor/build_tests.sh +++ b/traffic_monitor/build_tests.sh @@ -25,7 +25,7 @@ rm testcaches go build cd - > /dev/null -cd tests/integration +cd tests/_integration rm traffic_monitor_integration_test go test -c -o traffic_monitor_integration_test cd - > /dev/null diff --git a/traffic_monitor/tests/integration/Dockerfile b/traffic_monitor/tests/_integration/Dockerfile similarity index 100% rename from traffic_monitor/tests/integration/Dockerfile rename to traffic_monitor/tests/_integration/Dockerfile diff --git a/traffic_monitor/tests/integration/Dockerfile_run.sh b/traffic_monitor/tests/_integration/Dockerfile_run.sh similarity index 100% rename from traffic_monitor/tests/integration/Dockerfile_run.sh rename to traffic_monitor/tests/_integration/Dockerfile_run.sh diff --git a/traffic_monitor/tests/integration/README.md b/traffic_monitor/tests/_integration/README.md similarity index 64% rename from traffic_monitor/tests/integration/README.md rename to traffic_monitor/tests/_integration/README.md index 5f353c35b7..0242759a6d 100644 --- a/traffic_monitor/tests/integration/README.md +++ b/traffic_monitor/tests/_integration/README.md @@ -7,4 +7,4 @@ From the `trafficcontrol/traffic_monitor` directory run `build_tests.sh` ## Running From the `trafficcontrol/traffic_monitor` directory run: -`sudo docker-compose -p tmi --project-directory . -f tests/integration/docker-compose.yml run tmintegrationtest` +`sudo docker-compose -p tmi --project-directory . -f tests/_integration/docker-compose.yml run tmintegrationtest` diff --git a/traffic_monitor/tests/integration/client_test.go b/traffic_monitor/tests/_integration/client_test.go similarity index 99% rename from traffic_monitor/tests/integration/client_test.go rename to traffic_monitor/tests/_integration/client_test.go index d3b2e008fb..5839983a26 100644 --- a/traffic_monitor/tests/integration/client_test.go +++ b/traffic_monitor/tests/_integration/client_test.go @@ -1,4 +1,4 @@ -package integration +package _integration /* Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/traffic_monitor/tests/integration/config/config.go b/traffic_monitor/tests/_integration/config/config.go similarity index 100% rename from traffic_monitor/tests/integration/config/config.go rename to traffic_monitor/tests/_integration/config/config.go diff --git a/traffic_monitor/tests/integration/docker-compose.yml b/traffic_monitor/tests/_integration/docker-compose.yml similarity index 98% rename from traffic_monitor/tests/integration/docker-compose.yml rename to traffic_monitor/tests/_integration/docker-compose.yml index ae36e1d020..5ebda9f27a 100644 --- a/traffic_monitor/tests/integration/docker-compose.yml +++ b/traffic_monitor/tests/_integration/docker-compose.yml @@ -83,7 +83,7 @@ services: tmintegrationtest: build: - context: ./tests/integration + context: ./tests/_integration dockerfile: ./Dockerfile depends_on: - testto diff --git a/traffic_monitor/tests/integration/kbps_test.go b/traffic_monitor/tests/_integration/kbps_test.go similarity index 99% rename from traffic_monitor/tests/integration/kbps_test.go rename to traffic_monitor/tests/_integration/kbps_test.go index 83444fbe99..dae40eb14e 100644 --- a/traffic_monitor/tests/integration/kbps_test.go +++ b/traffic_monitor/tests/_integration/kbps_test.go @@ -1,4 +1,4 @@ -package integration +package _integration /* diff --git a/traffic_monitor/tests/integration/monitoring.json b/traffic_monitor/tests/_integration/monitoring.json similarity index 100% rename from traffic_monitor/tests/integration/monitoring.json rename to traffic_monitor/tests/_integration/monitoring.json diff --git a/traffic_monitor/tests/integration/snapshot.json b/traffic_monitor/tests/_integration/snapshot.json similarity index 100% rename from traffic_monitor/tests/integration/snapshot.json rename to traffic_monitor/tests/_integration/snapshot.json diff --git a/traffic_monitor/tests/integration/traffic-monitor-test.conf b/traffic_monitor/tests/_integration/traffic-monitor-test.conf similarity index 100% rename from traffic_monitor/tests/integration/traffic-monitor-test.conf rename to traffic_monitor/tests/_integration/traffic-monitor-test.conf diff --git a/traffic_monitor/tests/integration/traffic_monitor_test.go b/traffic_monitor/tests/_integration/traffic_monitor_test.go similarity index 96% rename from traffic_monitor/tests/integration/traffic_monitor_test.go rename to traffic_monitor/tests/_integration/traffic_monitor_test.go index 4bc4f5921c..28394342d7 100644 --- a/traffic_monitor/tests/integration/traffic_monitor_test.go +++ b/traffic_monitor/tests/_integration/traffic_monitor_test.go @@ -1,4 +1,4 @@ -package integration +package _integration /* @@ -26,7 +26,7 @@ import ( "time" "github.com/apache/trafficcontrol/lib/go-log" - "github.com/apache/trafficcontrol/traffic_monitor/tests/integration/config" + "github.com/apache/trafficcontrol/traffic_monitor/tests/_integration/config" "github.com/apache/trafficcontrol/traffic_monitor/tmclient" ) From 4780affd1f640bbd8f13e84038a903b9514a9d12 Mon Sep 17 00:00:00 2001 From: shamrickus Date: Thu, 6 May 2021 10:21:54 -0600 Subject: [PATCH 10/22] Cleanup and use latest api/structs --- traffic_monitor/tests/_integration/Dockerfile_run.sh | 11 +---------- traffic_monitor/tools/testto/testto.go | 4 ++-- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/traffic_monitor/tests/_integration/Dockerfile_run.sh b/traffic_monitor/tests/_integration/Dockerfile_run.sh index 2cd6082f05..91ba51550c 100755 --- a/traffic_monitor/tests/_integration/Dockerfile_run.sh +++ b/traffic_monitor/tests/_integration/Dockerfile_run.sh @@ -49,20 +49,15 @@ init() { wait_for_endpoint "${TM_URI}" TESTCACHES_ADDRESS=$(ping testcaches -4 -c 1 | head -n 1 | grep -Eo '[0-9]+.[0-9]+.[0-9]+.[0-9]+') TESTCACHES_GATEWAY=$(echo $TESTCACHES_ADDRESS | sed "s/\([0-9]\+.[0-9]\+.[0-9]\+.\)[0-9]/\11/") -# TESTTO_ADDRESS=$(ping testto -4 -c 1 | head -n 1 | grep -Eo '[0-9]+.[0-9]+.[0-9]+.[0-9]+') -# TESTTO_GATEWAY=$(echo $TESTTO_ADDRESS | sed "s/\([0-9]\+.[0-9]\+.[0-9]\+.\)[0-9]/\11/") jq "(.. | .address?) |= \"$TESTCACHES_ADDRESS\" | (.. | .gateway?) |= \"$TESTCACHES_GATEWAY\"" \ /monitoring.json > /monitoring.json.tmp && mv /monitoring.json.tmp /monitoring.json -# jq "(.. | .address?) |= \"$TESTTO_ADDRESS\" | (.. | .gateway?) |= \"$TESTTO_GATEWAY\"" \ -# /snapshot.json > /snapshot.json.tmp && mv /snapshot.json.tmp /snapshot.json - curl -Lvsk ${TESTTO_URI}/api/4.0/cdns/fake/snapshot -X POST -d "@/snapshot.json" curl -Lvsk ${TESTTO_URI}/api/4.0/cdns/fake/configs/monitoring -X POST -d '@/monitoring.json' - curl -Lvsk ${TESTTO_URI}/api/3.0/servers -X POST -d ' + curl -Lvsk ${TESTTO_URI}/api/4.0/servers -X POST -d ' [ { "cachegroup": "foo", @@ -132,10 +127,6 @@ init() { ] ' - # DEBUG - printf "\n\ntestto:\n" - curl -Lk ${TESTTO_URI}/api/4.0/cdns/fake/snapshot - cat > $CFG_FILE <<- EOF { "trafficMonitor": { diff --git a/traffic_monitor/tools/testto/testto.go b/traffic_monitor/tools/testto/testto.go index 987ceb35d0..5f18237325 100644 --- a/traffic_monitor/tools/testto/testto.go +++ b/traffic_monitor/tools/testto/testto.go @@ -43,7 +43,7 @@ func main() { } toDataThs := NewThs() - toDataThs.Set(&FakeTOData{Servers: []tc.ServerV30{}}) + toDataThs.Set(&FakeTOData{Servers: []tc.ServerV40{}}) Serve(*port, toDataThs) fmt.Printf("Serving on %v\n", *port) @@ -57,7 +57,7 @@ func main() { type FakeTOData struct { Monitoring tc.TrafficMonitorConfig CRConfig tc.CRConfig - Servers []tc.ServerV30 + Servers []tc.ServerV40 } // TODO make timeouts configurable? From 00fdb0bbeccadd88302c5173d30929373fad5bd0 Mon Sep 17 00:00:00 2001 From: shamrickus Date: Thu, 6 May 2021 10:30:28 -0600 Subject: [PATCH 11/22] Changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 486cfdbb01..cdb7ec3aeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - t3c: bug fix to consider plugin config files for reloading remap.config - t3c: Change syncds so that it only warns on package version mismatch. - atstccfg: add ##REFETCH## support to regex_revalidate.config processing. +- Added a Traffic Monitor integration test framework. ### Fixed - [#5690](https://github.com/apache/trafficcontrol/issues/5690) - Fixed github action for added/modified db migration file. From 27950271436dc751b7adef1cde57c715b209f764 Mon Sep 17 00:00:00 2001 From: shamrickus Date: Thu, 6 May 2021 14:24:50 -0600 Subject: [PATCH 12/22] Fix CodeQL issues --- traffic_monitor/tools/testcaches/fakesrvr/cmd.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/traffic_monitor/tools/testcaches/fakesrvr/cmd.go b/traffic_monitor/tools/testcaches/fakesrvr/cmd.go index 2014a1ae87..b698d050f0 100644 --- a/traffic_monitor/tools/testcaches/fakesrvr/cmd.go +++ b/traffic_monitor/tools/testcaches/fakesrvr/cmd.go @@ -20,6 +20,7 @@ package fakesrvr */ import ( + "html" "net/http" "strconv" "strings" @@ -48,7 +49,7 @@ var cmds = map[string]CmdFunc{ func cmdSetStat(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs fakesrvrdata.Ths) { urlQry := r.URL.Query() - newMinStr := urlQry.Get("min") + newMinStr := html.EscapeString(urlQry.Get("min")) newMin, err := strconv.ParseUint(newMinStr, 10, 64) if err != nil { w.WriteHeader(http.StatusBadRequest) @@ -56,7 +57,7 @@ func cmdSetStat(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs fakesrvr return } - newMaxStr := urlQry.Get("max") + newMaxStr := html.EscapeString(urlQry.Get("max")) newMax, err := strconv.ParseUint(newMaxStr, 10, 64) if err != nil { w.WriteHeader(http.StatusBadRequest) @@ -64,14 +65,14 @@ func cmdSetStat(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs fakesrvr return } - remap := urlQry.Get("remap") + remap := html.EscapeString(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") + stat := html.EscapeString(urlQry.Get("stat")) validStats := map[string]struct{}{ "in_bytes": {}, @@ -165,7 +166,7 @@ func cmdSetSystem(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs fakesr urlQry := r.URL.Query() if newSpeedStr := urlQry.Get("speed"); newSpeedStr != "" { - newSpeed, err := strconv.ParseUint(newSpeedStr, 10, 64) + newSpeed, err := strconv.ParseInt(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")) From bae510e480345bb531fdfa117ceacb024c033dc1 Mon Sep 17 00:00:00 2001 From: shamrickus Date: Thu, 6 May 2021 15:05:43 -0600 Subject: [PATCH 13/22] More CodeQL --- traffic_monitor/tools/testcaches/fakesrvr/cmd.go | 2 +- traffic_monitor/tools/testcaches/fakesrvr/server.go | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/traffic_monitor/tools/testcaches/fakesrvr/cmd.go b/traffic_monitor/tools/testcaches/fakesrvr/cmd.go index b698d050f0..f25b3b97ed 100644 --- a/traffic_monitor/tools/testcaches/fakesrvr/cmd.go +++ b/traffic_monitor/tools/testcaches/fakesrvr/cmd.go @@ -166,7 +166,7 @@ func cmdSetSystem(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs fakesr urlQry := r.URL.Query() if newSpeedStr := urlQry.Get("speed"); newSpeedStr != "" { - newSpeed, err := strconv.ParseInt(newSpeedStr, 10, 64) + newSpeed, err := strconv.ParseInt(newSpeedStr, 10, 32) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("error parsing query parameter 'speed': must be a non-negative integer: " + err.Error() + "\n")) diff --git a/traffic_monitor/tools/testcaches/fakesrvr/server.go b/traffic_monitor/tools/testcaches/fakesrvr/server.go index eb41290279..5fd5e0ae8e 100644 --- a/traffic_monitor/tools/testcaches/fakesrvr/server.go +++ b/traffic_monitor/tools/testcaches/fakesrvr/server.go @@ -23,6 +23,7 @@ import ( "encoding/json" "fmt" "github.com/apache/trafficcontrol/lib/go-rfc" + "html" "math/rand" "net/http" "strconv" @@ -79,7 +80,7 @@ func astatsHandler(fakeSrvrDataThs fakesrvrdata.Ths) http.HandlerFunc { func cmdHandler(fakeSrvrDataThs fakesrvrdata.Ths) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - path := r.URL.Path + path := html.EscapeString(r.URL.Path) path = strings.ToLower(path) path = strings.TrimLeft(path, "/cmd") for cmd, cmdF := range cmds { From 178667c897e07afdba105ae9bb7f4b46cabea80c Mon Sep 17 00:00:00 2001 From: shamrickus Date: Wed, 12 May 2021 10:03:02 -0600 Subject: [PATCH 14/22] Code review fixes & code clean up --- traffic_monitor/Dockerfile | 8 +-- traffic_monitor/Dockerfile_run.sh | 2 +- traffic_monitor/build_tests.sh | 4 +- traffic_monitor/tests/_integration/Dockerfile | 7 +-- .../tests/_integration/Dockerfile_run.sh | 54 +++++++++---------- traffic_monitor/tests/_integration/README.md | 3 +- .../tests/_integration/config/config.go | 4 +- .../tests/_integration/kbps_test.go | 1 - .../tests/_integration/monitoring.json | 29 ++++++---- .../tests/_integration/snapshot.json | 42 ++++++++++----- traffic_monitor/tools/testcaches/Dockerfile | 6 +-- .../tools/testcaches/Dockerfile_run.sh | 32 +++++------ traffic_monitor/tools/testcaches/README.md | 22 +++++--- .../tools/testcaches/fakesrvr/cmd.go | 4 +- .../tools/testcaches/fakesrvrdata/run.go | 2 +- traffic_monitor/tools/testto/Dockerfile | 6 +-- .../tools/testto/Dockerfile_run.sh | 13 +++-- traffic_monitor/tools/testto/README.md | 4 +- 18 files changed, 127 insertions(+), 116 deletions(-) diff --git a/traffic_monitor/Dockerfile b/traffic_monitor/Dockerfile index 9c70c8a62f..6fad46cacd 100644 --- a/traffic_monitor/Dockerfile +++ b/traffic_monitor/Dockerfile @@ -25,17 +25,13 @@ # # docker run --detach --name tm --hostname tm --net=tmi --env TO_URI=http://to.invalid:3000 --env TO_USER=user --env TO_PASS=pass traffic_monitor:latest -FROM centos/systemd +FROM centos:8 MAINTAINER dev@trafficcontrol.apache.org ARG RPM=traffic_monitor.rpm ADD $RPM / -RUN yum install -y initscripts -RUN yum install -y /$(basename $RPM) -RUN rm /$(basename $RPM) - -RUN curl -L jq https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64 > /usr/sbin/jq && chmod +x /usr/sbin/jq +RUN yum install -y initscripts jq /$(basename $RPM) && rm /$(basename $RPM) ADD Dockerfile_run.sh / ENTRYPOINT /Dockerfile_run.sh diff --git a/traffic_monitor/Dockerfile_run.sh b/traffic_monitor/Dockerfile_run.sh index f6cffad73f..2e1682dc5f 100755 --- a/traffic_monitor/Dockerfile_run.sh +++ b/traffic_monitor/Dockerfile_run.sh @@ -26,7 +26,7 @@ envvars=( TO_URI TO_USER TO_PASS CDN PORT ) for v in $envvars do - if [[ -z $$v ]]; then echo "$v is unset"; exit 1; fi + if [[ -z ${!v} ]]; then echo "$v is unset"; exit 1; fi done start() { diff --git a/traffic_monitor/build_tests.sh b/traffic_monitor/build_tests.sh index b58279c370..7dc9ae0a46 100755 --- a/traffic_monitor/build_tests.sh +++ b/traffic_monitor/build_tests.sh @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -CGO_ENABLED=0 -GOOS=linux +export CGO_ENABLED=0 +export GOOS=linux cd tools/testto rm testto diff --git a/traffic_monitor/tests/_integration/Dockerfile b/traffic_monitor/tests/_integration/Dockerfile index 2e6bf0815d..b10c09cd51 100644 --- a/traffic_monitor/tests/_integration/Dockerfile +++ b/traffic_monitor/tests/_integration/Dockerfile @@ -25,18 +25,13 @@ # # docker run --detach --name tm --hostname tm --net=tmi --env TO_URI=http://to.invalid:3000 --env TO_USER=user --env TO_PASS=pass traffic_monitor:latest -FROM centos/systemd +FROM centos:8 MAINTAINER dev@trafficcontrol.apache.org RUN yum install -y initscripts epel-release golang glibc jq COPY traffic_monitor_integration_test / -# TODO build go test here, instead of requiring it manually be built beforehand -# See /infrastructure/cdn-in-a-box/traffic_ops_integration_test/Dockerfile - -RUN curl -L jq https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 > /usr/sbin/jq && chmod +x /usr/sbin/jq - COPY Dockerfile_run.sh / COPY monitoring.json / COPY snapshot.json / diff --git a/traffic_monitor/tests/_integration/Dockerfile_run.sh b/traffic_monitor/tests/_integration/Dockerfile_run.sh index 91ba51550c..19428f5b8d 100755 --- a/traffic_monitor/tests/_integration/Dockerfile_run.sh +++ b/traffic_monitor/tests/_integration/Dockerfile_run.sh @@ -21,43 +21,39 @@ # TO_USER # TO_PASS # CDN -kild=0 -trap killed SIGINT -killed() { - let kild=1 -} - # 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 +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 - +TO_API_VERSION=4.0 CFG_FILE=/traffic-monitor-integration-test.cfg start() { - printf "DEBUG traffic_monitor_integration starting\n" + printf "DEBUG traffic_monitor_integration starting\n" - exec /traffic_monitor_integration_test -test.v -cfg $CFG_FILE + exec /traffic_monitor_integration_test -test.v -cfg $CFG_FILE } init() { - wait_for_endpoint "${TESTTO_URI}/api/4.0/servers" + wait_for_endpoint "${TESTTO_URI}/api/${TO_API_VERSION}/servers" wait_for_endpoint "${TESTCACHES_URI}:${TESTCACHES_PORT_START}/_astats" wait_for_endpoint "${TM_URI}" - TESTCACHES_ADDRESS=$(ping testcaches -4 -c 1 | head -n 1 | grep -Eo '[0-9]+.[0-9]+.[0-9]+.[0-9]+') - TESTCACHES_GATEWAY=$(echo $TESTCACHES_ADDRESS | sed "s/\([0-9]\+.[0-9]\+.[0-9]\+.\)[0-9]/\11/") + TESTCACHES_ADDRESS=$(ping testcaches -4 -c 1 | head -n 1 | grep -Eo '[0-9]+.[0-9]+.[0-9]+.[0-9]+') + TESTCACHES_GATEWAY=$(echo $TESTCACHES_ADDRESS | sed "s/\([0-9]\+.[0-9]\+.[0-9]\+.\)[0-9]/\11/") jq "(.. | .address?) |= \"$TESTCACHES_ADDRESS\" | (.. | .gateway?) |= \"$TESTCACHES_GATEWAY\"" \ - /monitoring.json > /monitoring.json.tmp && mv /monitoring.json.tmp /monitoring.json + /monitoring.json >/monitoring.json.tmp && mv /monitoring.json.tmp /monitoring.json - curl -Lvsk ${TESTTO_URI}/api/4.0/cdns/fake/snapshot -X POST -d "@/snapshot.json" + curl -Lvsk ${TESTTO_URI}/api/${TO_API_VERSION}/cdns/fake/snapshot -X POST -d "@/snapshot.json" - curl -Lvsk ${TESTTO_URI}/api/4.0/cdns/fake/configs/monitoring -X POST -d '@/monitoring.json' + curl -Lvsk ${TESTTO_URI}/api/${TO_API_VERSION}/cdns/fake/configs/monitoring -X POST -d '@/monitoring.json' - curl -Lvsk ${TESTTO_URI}/api/4.0/servers -X POST -d ' + curl -Lvsk ${TESTTO_URI}/api/${TO_API_VERSION}/servers -X POST -d ' [ { "cachegroup": "foo", @@ -127,7 +123,7 @@ init() { ] ' - cat > $CFG_FILE <<- EOF + cat >$CFG_FILE <<-EOF { "trafficMonitor": { "url": "$TM_URI" @@ -147,21 +143,21 @@ init() { } EOF - echo "INITIALIZED=1" >> /etc/environment + echo "INITIALIZED=1" >>/etc/environment } -function wait_for_endpoint { - RC=1 +function wait_for_endpoint() { try=0 - while [[ ! $RC -eq 0 ]] && [[ $try -lt 5 ]]; do + while curl -Lvsk "$1" 2>&1 1> /dev/null | grep -E '< HTTP/[0-9]\.?[0-9]* 200' >/dev/null && [[ $try -lt 5 ]]; do if [[ $try -gt 0 ]]; then + echo "Waiting for $1 to return a 200 OK" sleep 5 - echo "Waiting for $1 to return a 200 OK"; + else + echo "Unable to get $1" + exit 1 fi - curl -Lvsk "$1" 2>&1 1> /dev/null | grep -E '< HTTP/[0-9]\.?[0-9]* 200' - RC=$? try=$(expr $try + 1) - done + done } export -f wait_for_endpoint diff --git a/traffic_monitor/tests/_integration/README.md b/traffic_monitor/tests/_integration/README.md index 0242759a6d..ba7e107e0c 100644 --- a/traffic_monitor/tests/_integration/README.md +++ b/traffic_monitor/tests/_integration/README.md @@ -1,10 +1,11 @@ # Traffic Monitor Integration Test Framework -## Building +## Building From the `trafficcontrol/traffic_monitor` directory run `build_tests.sh` ## Running + From the `trafficcontrol/traffic_monitor` directory run: `sudo docker-compose -p tmi --project-directory . -f tests/_integration/docker-compose.yml run tmintegrationtest` diff --git a/traffic_monitor/tests/_integration/config/config.go b/traffic_monitor/tests/_integration/config/config.go index 5db7863483..3a1022012a 100644 --- a/traffic_monitor/tests/_integration/config/config.go +++ b/traffic_monitor/tests/_integration/config/config.go @@ -1,3 +1,5 @@ +package config + /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -17,8 +19,6 @@ * under the License. */ -package config - import ( "encoding/json" "fmt" diff --git a/traffic_monitor/tests/_integration/kbps_test.go b/traffic_monitor/tests/_integration/kbps_test.go index dae40eb14e..986fba0967 100644 --- a/traffic_monitor/tests/_integration/kbps_test.go +++ b/traffic_monitor/tests/_integration/kbps_test.go @@ -1,7 +1,6 @@ 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 diff --git a/traffic_monitor/tests/_integration/monitoring.json b/traffic_monitor/tests/_integration/monitoring.json index ed653db84c..1acdae8fb1 100644 --- a/traffic_monitor/tests/_integration/monitoring.json +++ b/traffic_monitor/tests/_integration/monitoring.json @@ -24,10 +24,14 @@ ], "type": "EDGE", "hashId": "server0", - "deliveryServices": [{ - "xmlId": "ds0", - "remaps": ["ds0.monitor-integration.test"] - }] + "deliveryServices": [ + { + "xmlId": "ds0", + "remaps": [ + "ds0.monitor-integration.test" + ] + } + ] }, { "profile": "Edge0", @@ -53,17 +57,24 @@ ], "type": "EDGE", "hashId": "server1", - "deliveryServices": [{ - "xmlId": "ds0", - "remaps": ["ds0.monitor-integration.test"] - }] + "deliveryServices": [ + { + "xmlId": "ds0", + "remaps": [ + "ds0.monitor-integration.test" + ] + } + ] } ], "cacheGroups": [ { "cg0": { "name": "cg0", - "coordinates": {"latitude": 0, "longitude": 0} + "coordinates": { + "latitude": 0, + "longitude": 0 + } } } ], diff --git a/traffic_monitor/tests/_integration/snapshot.json b/traffic_monitor/tests/_integration/snapshot.json index 601958a50f..e720abf389 100644 --- a/traffic_monitor/tests/_integration/snapshot.json +++ b/traffic_monitor/tests/_integration/snapshot.json @@ -15,15 +15,14 @@ "neustar.polling.interval": "30", "neustar.polling.url": "foo", "soa": { - }, "dnssec.inception": "0", "ttls": { - "admin": "30", - "expire": "30", + "admin": "30", + "expire": "30", "minimum": "30", "refresh": "30", - "retry": "30" + "retry": "30" }, "weight": "1", "zonemanager.cache.maintenance.interval": "30", @@ -39,11 +38,15 @@ "ip": "testcaches", "ip6": null, "locationId": "", - "port" : 30000, + "port": 30000, "status": "REPORTED", "type": "EDGE", "interfaceName": "bond0", - "deliveryServices": {"ds0":["ds0.monitor-integration.test"]}, + "deliveryServices": { + "ds0": [ + "ds0.monitor-integration.test" + ] + }, "routingDisabled": 0 }, "server1": { @@ -55,11 +58,15 @@ "ip": "testcaches", "ip6": null, "locationId": "", - "port" : 30001, + "port": 30001, "status": "REPORTED", "type": "EDGE", "interfaceName": "bond0", - "deliveryServices": {"ds0":["ds0.monitor-integration.test"]}, + "deliveryServices": { + "ds0": [ + "ds0.monitor-integration.test" + ] + }, "routingDisabled": 0 } }, @@ -73,7 +80,9 @@ "limit": 1, "shuffled": "false" }, - "domains": ["ds0.monitor-integration.test"], + "domains": [ + "ds0.monitor-integration.test" + ], "matchsets": [ { "protocol": "HTTP", @@ -85,7 +94,10 @@ ] } ], - "missLocation": {"lat": 0, "lon": 0}, + "missLocation": { + "lat": 0, + "lon": 0 + }, "protocol": { "acceptHttp": "true", "acceptHttps": "false", @@ -119,10 +131,16 @@ } }, "edgeLocations": { - "cg0": {"latitude":0, "longitude":0} + "cg0": { + "latitude": 0, + "longitude": 0 + } }, "trafficRouterLocations": { - "tr0": {"latitude":0, "longitude":0} + "tr0": { + "latitude": 0, + "longitude": 0 + } }, "monitors": { "trafficmonitor": { diff --git a/traffic_monitor/tools/testcaches/Dockerfile b/traffic_monitor/tools/testcaches/Dockerfile index e83c1f2c04..4aa9f63f58 100644 --- a/traffic_monitor/tools/testcaches/Dockerfile +++ b/traffic_monitor/tools/testcaches/Dockerfile @@ -15,13 +15,9 @@ # specific language governing permissions and limitations # under the License. -FROM centos/systemd +FROM centos:8 MAINTAINER dev@trafficcontrol.apache.org -# RUN rpm --import https://mirror.go-repo.io/centos/RPM-GPG-KEY-GO-REPO -# RUN curl -s https://mirror.go-repo.io/centos/go-repo.repo | tee /etc/yum.repos.d/go-repo.repo -# RUN yum install -y golang - ARG APP_BIN=testcaches ADD $APP_BIN /usr/sbin/ diff --git a/traffic_monitor/tools/testcaches/Dockerfile_run.sh b/traffic_monitor/tools/testcaches/Dockerfile_run.sh index 298068eb0b..95abc17da5 100755 --- a/traffic_monitor/tools/testcaches/Dockerfile_run.sh +++ b/traffic_monitor/tools/testcaches/Dockerfile_run.sh @@ -16,30 +16,22 @@ # specific language governing permissions and limitations # under the License. - -# envvars=( PORT ) -# for v in $envvars -# do -# if [[ -z $$v ]]; then echo "$v is unset"; exit 1; fi -# done - start() { - ARGS= - if [[ -n "${NUM_PORTS}" ]]; then - ARGS="$ARGS -numPorts ${NUM_PORTS}" - fi - if [[ -n "${NUM_REMAPS}" ]]; then - ARGS="$ARGS -numRemaps ${NUM_REMAPS}" - fi - if [[ -n "${PORT_START}" ]]; then - ARGS="$ARGS -portStart ${PORT_START}" - fi - testcaches ${ARGS} + ARGS= + if [[ -n "${NUM_PORTS}" ]]; then + ARGS="$ARGS -numPorts ${NUM_PORTS}" + fi + if [[ -n "${NUM_REMAPS}" ]]; then + ARGS="$ARGS -numRemaps ${NUM_REMAPS}" + fi + if [[ -n "${PORT_START}" ]]; then + ARGS="$ARGS -portStart ${PORT_START}" + fi + testcaches ${ARGS} } init() { - - echo "INITIALIZED=1" >> /etc/environment + echo "INITIALIZED=1" >>/etc/environment } source /etc/environment diff --git a/traffic_monitor/tools/testcaches/README.md b/traffic_monitor/tools/testcaches/README.md index 77dc65a8cc..3d27aa3ea8 100644 --- a/traffic_monitor/tools/testcaches/README.md +++ b/traffic_monitor/tools/testcaches/README.md @@ -23,25 +23,32 @@ The `testcaches` tool simulates multiple ATS caches' `_astats` endpoints. Its primary goal is for testing the Monitor under load, but it may be useful for testing other components. -A list of parameters can be seen by running `./testcaches -h`. There are only three: the first port to use, the number of ports to use, and the number of remaps (delivery services) to serve in each fake server. +A list of parameters can be seen by running `./testcaches -h`. There are only three: the first port to use, the number +of ports to use, and the number of remaps (delivery services) to serve in each fake server. Each port is a unique fake server, with distinct incrementing stats. When run with no parameters, it defaults to ports 40000-40999 and 1000 remaps. -Stats are served at the regular ATS `stats_over_http` endpoint, `_astats`. For example, if it's serving on port 40000, it can be reached via `curl http://localhost:40000/_astats`. It also respects the `?application=system` query parameter, and will serve only system stats (the Monitor "health check" [as opposed to the "stat check"]). For example, `curl http://localhost:40000/_astats?application=system`. +Stats are served at the regular ATS `stats_over_http` endpoint, `_astats`. For example, if it's serving on port 40000, +it can be reached via `curl http://localhost:40000/_astats`. It also respects the `?application=system` query parameter, +and will serve only system stats (the Monitor "health check" [as opposed to the "stat check"]). For +example, `curl http://localhost:40000/_astats?application=system`. ## Commands -The `testcaches` app accepts a number of commands, which manipulate the data it serves. These command are all available via HTTP requests. +The `testcaches` app accepts a number of commands, which manipulate the data it serves. These command are all available +via HTTP requests. -Each HTTP request is made to a fake cache at a specific port. Thus, you can modify data served by each fake cache independently. +Each HTTP request is made to a fake cache at a specific port. Thus, you can modify data served by each fake cache +independently. The commands are: ### `/cmd/setstat` -Sets how much a stat increments by every interval (currently, an interval is hard-coded to 1 second). Accepts a min and max, and will increment by a random number between them. The min may equal the max, if a constant increment is desired. +Sets how much a stat increments by every interval (currently, an interval is hard-coded to 1 second). Accepts a min and +max, and will increment by a random number between them. The min may equal the max, if a constant increment is desired. Query Parameters: `remap` - the remap rule to set @@ -67,7 +74,9 @@ Example: ### `setdelay` -Sets the delay for serving all _astats requests to this fake cache. Accepts a minimum and maximum, which may be qual, and delays the request by a random interval between them. When a delay is set, the server immediately accepts client requests, reads headers and sets up the connection, and then delays writing out the body. +Sets the delay for serving all _astats requests to this fake cache. Accepts a minimum and maximum, which may be qual, +and delays the request by a random interval between them. When a delay is set, the server immediately accepts client +requests, reads headers and sets up the connection, and then delays writing out the body. Query Parameters: `min` - the minimum delay time, in milliseconds @@ -81,6 +90,7 @@ Example: Build environment variables: none Run environment variables: + - `NUM_PORTS` - app `numPorts` argument - `NUM_REMAPS` - app `numRemaps` argument - `PORT_START` - app `portStart` argument diff --git a/traffic_monitor/tools/testcaches/fakesrvr/cmd.go b/traffic_monitor/tools/testcaches/fakesrvr/cmd.go index f25b3b97ed..c2f71990e4 100644 --- a/traffic_monitor/tools/testcaches/fakesrvr/cmd.go +++ b/traffic_monitor/tools/testcaches/fakesrvr/cmd.go @@ -86,7 +86,7 @@ func cmdSetStat(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs fakesrvr if _, ok := validStats[stat]; !ok { w.WriteHeader(http.StatusBadRequest) statNames := []string{} - for statName, _ := range validStats { + 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")) @@ -97,7 +97,7 @@ func cmdSetStat(w http.ResponseWriter, r *http.Request, fakeSrvrDataThs fakesrvr if _, ok := srvr.ATS.Remaps[remap]; !ok { w.WriteHeader(http.StatusBadRequest) remapNames := []string{} - for remapName, _ := range srvr.ATS.Remaps { + 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")) diff --git a/traffic_monitor/tools/testcaches/fakesrvrdata/run.go b/traffic_monitor/tools/testcaches/fakesrvrdata/run.go index d2c1a6b782..3f6ec22490 100644 --- a/traffic_monitor/tools/testcaches/fakesrvrdata/run.go +++ b/traffic_monitor/tools/testcaches/fakesrvrdata/run.go @@ -32,7 +32,7 @@ type BytesPerSec struct { // TODO change to PerMin? PerHour? (to allow, e.g. one // runValidate verifies the FakeServerData Remaps match the RemapIncrements func runValidate(s *FakeServerData, remapIncrements map[string]BytesPerSec) error { - for r, _ := range s.ATS.Remaps { + for r := range s.ATS.Remaps { if _, ok := remapIncrements[r]; !ok { return errors.New("remap increments missing server remap '" + r + "'") } diff --git a/traffic_monitor/tools/testto/Dockerfile b/traffic_monitor/tools/testto/Dockerfile index e3e7983c7f..7e1f935c6b 100644 --- a/traffic_monitor/tools/testto/Dockerfile +++ b/traffic_monitor/tools/testto/Dockerfile @@ -15,13 +15,9 @@ # specific language governing permissions and limitations # under the License. -FROM centos/systemd +FROM centos:8 MAINTAINER dev@trafficcontrol.apache.org -# RUN rpm --import https://mirror.go-repo.io/centos/RPM-GPG-KEY-GO-REPO -# RUN curl -s https://mirror.go-repo.io/centos/go-repo.repo | tee /etc/yum.repos.d/go-repo.repo -# RUN yum install -y golang - ARG APP_BIN=testto ADD $APP_BIN /usr/sbin/ diff --git a/traffic_monitor/tools/testto/Dockerfile_run.sh b/traffic_monitor/tools/testto/Dockerfile_run.sh index 9ef0ce900f..6c6ef93509 100755 --- a/traffic_monitor/tools/testto/Dockerfile_run.sh +++ b/traffic_monitor/tools/testto/Dockerfile_run.sh @@ -16,7 +16,6 @@ # specific language governing permissions and limitations # under the License. - # envvars=( PORT ) # for v in $envvars # do @@ -24,16 +23,16 @@ # done start() { - ARGS= - if [[ -n "${PORT}" ]]; then - ARGS="$ARGS -port ${PORT}" - fi - testto ${ARGS} + ARGS= + if [[ -n "${PORT}" ]]; then + ARGS="$ARGS -port ${PORT}" + fi + testto ${ARGS} } init() { - echo "INITIALIZED=1" >> /etc/environment + echo "INITIALIZED=1" >>/etc/environment } source /etc/environment diff --git a/traffic_monitor/tools/testto/README.md b/traffic_monitor/tools/testto/README.md index 957b4aa862..ef031c61aa 100644 --- a/traffic_monitor/tools/testto/README.md +++ b/traffic_monitor/tools/testto/README.md @@ -22,7 +22,9 @@ Build environment variables: none Run environment variables: -- `PORT` - the port to serve on. If none is specified, no port argument will be passed to the app, and it will use its default. + +- `PORT` - the port to serve on. If none is specified, no port argument will be passed to the app, and it will use its + default. ### Building From 0da78c49dc5ec3205f96cd4146480590dc228ed9 Mon Sep 17 00:00:00 2001 From: shamrickus Date: Wed, 12 May 2021 10:25:47 -0600 Subject: [PATCH 15/22] English and better build --- traffic_monitor/build_tests.sh | 6 ++++++ traffic_monitor/tests/_integration/docker-compose.yml | 7 ++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/traffic_monitor/build_tests.sh b/traffic_monitor/build_tests.sh index 7dc9ae0a46..999adf0412 100755 --- a/traffic_monitor/build_tests.sh +++ b/traffic_monitor/build_tests.sh @@ -15,6 +15,12 @@ export CGO_ENABLED=0 export GOOS=linux +if [ ! -f "traffic_monitor.rpm" ]; then + echo "Unable to find traffic_monitor.rpm." + echo "Run './pkg traffic_monitor_build' and copy TM rpm to this directory." + exit 1 +fi + cd tools/testto rm testto go build diff --git a/traffic_monitor/tests/_integration/docker-compose.yml b/traffic_monitor/tests/_integration/docker-compose.yml index 5ebda9f27a..7aa1cb9ae8 100644 --- a/traffic_monitor/tests/_integration/docker-compose.yml +++ b/traffic_monitor/tests/_integration/docker-compose.yml @@ -24,13 +24,10 @@ # Commands are, from the root trafficcontrol directory: # ./pkg -v traffic_monitor_build # cp ./dist/traffic_monitor*.rpm ./traffic_monitor/ -# (cd ./traffic_monitor/tools/testto && go build) -# (cd ./traffic_monitor/tools/testcaches && go build) -# cd ./traffic_monitor -# docker-compose up +# ./build_tests.sh # # To run the integration tests, your current working directory must be trafficcontrol/traffic_monitor. -# This is because Docker doesn't allow accessing files outside the current "context," and the integration tests need +# This is because Docker doesn't allow accessing files outside the current "context". # # docker-compose up -d # From 926daec6b45978358dd627f79bd800eab1e63ebd Mon Sep 17 00:00:00 2001 From: shamrickus Date: Wed, 12 May 2021 10:32:10 -0600 Subject: [PATCH 16/22] Better build instructions --- traffic_monitor/tests/_integration/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/traffic_monitor/tests/_integration/README.md b/traffic_monitor/tests/_integration/README.md index ba7e107e0c..6c6e9c27db 100644 --- a/traffic_monitor/tests/_integration/README.md +++ b/traffic_monitor/tests/_integration/README.md @@ -2,6 +2,8 @@ ## Building +From the `trafficcontrol` run `./pkg -v traffic_monitor_build` and copy the traffic_monitor rpm to the `traffic_monitor` directory. + From the `trafficcontrol/traffic_monitor` directory run `build_tests.sh` ## Running From ffbaa934964bed3fb66d9e167fd5cb11a51a00a3 Mon Sep 17 00:00:00 2001 From: shamrickus Date: Mon, 17 May 2021 13:29:42 -0600 Subject: [PATCH 17/22] Clean up readmes --- traffic_monitor/tools/testcaches/README.md | 11 ----------- traffic_monitor/tools/testto/README.md | 11 ----------- 2 files changed, 22 deletions(-) diff --git a/traffic_monitor/tools/testcaches/README.md b/traffic_monitor/tools/testcaches/README.md index 3d27aa3ea8..5448a30a2a 100644 --- a/traffic_monitor/tools/testcaches/README.md +++ b/traffic_monitor/tools/testcaches/README.md @@ -94,14 +94,3 @@ Run environment variables: - `NUM_PORTS` - app `numPorts` argument - `NUM_REMAPS` - app `numRemaps` argument - `PORT_START` - app `portStart` argument - -### Building - -`docker build . --tag testcaches` - -### Running - -``` -docker network create tmi -docker run --detach --network tmi --name testcaches --hostname testcaches --publish 30000-30099:30000-30099 --env NUM_PORTS=100 --env NUM_REMAPS=100 --env PORT_START=30000 testcaches -``` diff --git a/traffic_monitor/tools/testto/README.md b/traffic_monitor/tools/testto/README.md index ef031c61aa..223f4a7f9d 100644 --- a/traffic_monitor/tools/testto/README.md +++ b/traffic_monitor/tools/testto/README.md @@ -25,14 +25,3 @@ Run environment variables: - `PORT` - the port to serve on. If none is specified, no port argument will be passed to the app, and it will use its default. - -### Building - -`docker build --tag testto .` - -### Running - -``` -docker network create tmi -docker run --detach --network tmi --name testto --hostname testto --publish 27080:80 --env PORT=80 testto -``` From a69b983ffa478f32fd1b2b52cd49b3aeca96e4cf Mon Sep 17 00:00:00 2001 From: shamrickus Date: Tue, 18 May 2021 06:50:12 -0600 Subject: [PATCH 18/22] Code review fixes --- traffic_monitor/Dockerfile | 5 ----- traffic_monitor/health/event.go | 4 ++-- traffic_monitor/tests/_integration/Dockerfile | 5 ----- traffic_monitor/tests/_integration/Dockerfile_run.sh | 2 +- traffic_monitor/tests/_integration/kbps_test.go | 3 ++- .../tests/_integration/traffic_monitor_test.go | 1 - traffic_monitor/tmclient/tmclient.go | 3 ++- traffic_monitor/tools/testcaches/fakesrvrdata/run.go | 11 ----------- traffic_monitor/tools/testto/Dockerfile_run.sh | 7 ------- 9 files changed, 7 insertions(+), 34 deletions(-) diff --git a/traffic_monitor/Dockerfile b/traffic_monitor/Dockerfile index 6fad46cacd..cdad32c9fd 100644 --- a/traffic_monitor/Dockerfile +++ b/traffic_monitor/Dockerfile @@ -20,11 +20,6 @@ # It doesn't do any of the complex things the Dockerfiles in infrastructure/docker or infrastructure/cdn-in-a-box do, like inserting itself into Traffic Ops. # It is designed for a very simple use case, where the complex orchestration of other Traffic Control components is done elsewhere (or manually). -# Example Build and Run: -# docker build --build-arg RPM=traffic_monitor.rpm --tag traffic_monitor:latest . -# -# docker run --detach --name tm --hostname tm --net=tmi --env TO_URI=http://to.invalid:3000 --env TO_USER=user --env TO_PASS=pass traffic_monitor:latest - FROM centos:8 MAINTAINER dev@trafficcontrol.apache.org diff --git a/traffic_monitor/health/event.go b/traffic_monitor/health/event.go index cec3c63f64..4afec867b6 100644 --- a/traffic_monitor/health/event.go +++ b/traffic_monitor/health/event.go @@ -36,11 +36,11 @@ func (t Time) MarshalJSON() ([]byte, error) { } func (t *Time) UnmarshalJSON(data []byte) error { - i, err := strconv.ParseInt(string(data), 10, 64) + unixTime, err := strconv.ParseInt(string(data), 10, 64) if err != nil { return errors.New("health.Time (" + string(data) + ") must be a unix epoch integer: " + err.Error()) } - *t = Time(time.Unix(i, 0)) + *t = Time(time.Unix(unixTime, 0)) return nil } diff --git a/traffic_monitor/tests/_integration/Dockerfile b/traffic_monitor/tests/_integration/Dockerfile index b10c09cd51..16f6c48432 100644 --- a/traffic_monitor/tests/_integration/Dockerfile +++ b/traffic_monitor/tests/_integration/Dockerfile @@ -20,11 +20,6 @@ # It doesn't do any of the complex things the Dockerfiles in infrastructure/docker or infrastructure/cdn-in-a-box do, like inserting itself into Traffic Ops. # It is designed for a very simple use case, where the complex orchestration of other Traffic Control components is done elsewhere (or manually). -# Example Build and Run: -# docker build --build-arg RPM=traffic_monitor.rpm --tag traffic_monitor:latest . -# -# docker run --detach --name tm --hostname tm --net=tmi --env TO_URI=http://to.invalid:3000 --env TO_USER=user --env TO_PASS=pass traffic_monitor:latest - FROM centos:8 MAINTAINER dev@trafficcontrol.apache.org diff --git a/traffic_monitor/tests/_integration/Dockerfile_run.sh b/traffic_monitor/tests/_integration/Dockerfile_run.sh index 19428f5b8d..480147d5f6 100755 --- a/traffic_monitor/tests/_integration/Dockerfile_run.sh +++ b/traffic_monitor/tests/_integration/Dockerfile_run.sh @@ -148,7 +148,7 @@ EOF function wait_for_endpoint() { try=0 - while curl -Lvsk "$1" 2>&1 1> /dev/null | grep -E '< HTTP/[0-9]\.?[0-9]* 200' >/dev/null && [[ $try -lt 5 ]]; do + while curl -Lvsk "$1" 2>&1 1>/dev/null | grep -Eq '< HTTP/[0-9]\.?[0-9]* 200' && [[ $try -lt 5 ]]; do if [[ $try -gt 0 ]]; then echo "Waiting for $1 to return a 200 OK" sleep 5 diff --git a/traffic_monitor/tests/_integration/kbps_test.go b/traffic_monitor/tests/_integration/kbps_test.go index 986fba0967..142edbfdbd 100644 --- a/traffic_monitor/tests/_integration/kbps_test.go +++ b/traffic_monitor/tests/_integration/kbps_test.go @@ -16,6 +16,7 @@ package _integration import ( "fmt" + "github.com/apache/trafficcontrol/lib/go-log" "net/http" "testing" "time" @@ -59,7 +60,7 @@ func TestKBPS(t *testing.T) { if err != nil { t.Fatalf("Error posting fake cache command '" + uri + "': " + err.Error()) } - resp.Body.Close() + log.Close(resp.Body, "Unable to close http client "+uri) time.Sleep(time.Second * 5) // TODO determine if there's a faster or more precise way to wait for polled data? diff --git a/traffic_monitor/tests/_integration/traffic_monitor_test.go b/traffic_monitor/tests/_integration/traffic_monitor_test.go index 28394342d7..0e11f2133d 100644 --- a/traffic_monitor/tests/_integration/traffic_monitor_test.go +++ b/traffic_monitor/tests/_integration/traffic_monitor_test.go @@ -16,7 +16,6 @@ package _integration */ import ( - // "database/sql" "flag" "fmt" "net/http" diff --git a/traffic_monitor/tmclient/tmclient.go b/traffic_monitor/tmclient/tmclient.go index a4d3e823d5..e620971d9c 100644 --- a/traffic_monitor/tmclient/tmclient.go +++ b/traffic_monitor/tmclient/tmclient.go @@ -23,6 +23,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/apache/trafficcontrol/lib/go-log" "io/ioutil" "net/http" "strconv" @@ -201,7 +202,7 @@ func (c *TMClient) getBytes(path string) ([]byte, error) { if err != nil { return nil, errors.New("getting from '" + url + "': " + err.Error()) } - defer resp.Body.Close() + log.Close(resp.Body, "Unable to close http client "+url) if resp.StatusCode < 200 || resp.StatusCode > 299 { return nil, fmt.Errorf("Monitor '"+url+"' returned bad status %v", resp.StatusCode) diff --git a/traffic_monitor/tools/testcaches/fakesrvrdata/run.go b/traffic_monitor/tools/testcaches/fakesrvrdata/run.go index 3f6ec22490..520679bb47 100644 --- a/traffic_monitor/tools/testcaches/fakesrvrdata/run.go +++ b/traffic_monitor/tools/testcaches/fakesrvrdata/run.go @@ -140,14 +140,3 @@ func run(srvrThs Ths, remapIncrements map[string]BytesPerSec) { } } } - -// tryReceiveIncrement asynchronously tries to recieve from incrementChan if it has a value. -// Returns the value if it was read, or returns false if no value was waiting. -func tryReceiveIncrement(incrementChan <-chan IncrementChanT) (IncrementChanT, bool) { - select { - case inc := <-incrementChan: - return inc, true - default: - return IncrementChanT{}, false - } -} diff --git a/traffic_monitor/tools/testto/Dockerfile_run.sh b/traffic_monitor/tools/testto/Dockerfile_run.sh index 6c6ef93509..b985900f82 100755 --- a/traffic_monitor/tools/testto/Dockerfile_run.sh +++ b/traffic_monitor/tools/testto/Dockerfile_run.sh @@ -16,12 +16,6 @@ # specific language governing permissions and limitations # under the License. -# envvars=( PORT ) -# for v in $envvars -# do -# if [[ -z $$v ]]; then echo "$v is unset"; exit 1; fi -# done - start() { ARGS= if [[ -n "${PORT}" ]]; then @@ -31,7 +25,6 @@ start() { } init() { - echo "INITIALIZED=1" >>/etc/environment } From 31e88e7a3fc0e0fc713c76b6affd6bf16c8637fa Mon Sep 17 00:00:00 2001 From: shamrickus Date: Tue, 18 May 2021 06:55:34 -0600 Subject: [PATCH 19/22] Woops --- traffic_monitor/tests/_integration/kbps_test.go | 2 +- traffic_monitor/tmclient/tmclient.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/traffic_monitor/tests/_integration/kbps_test.go b/traffic_monitor/tests/_integration/kbps_test.go index 142edbfdbd..d72cd108c3 100644 --- a/traffic_monitor/tests/_integration/kbps_test.go +++ b/traffic_monitor/tests/_integration/kbps_test.go @@ -16,11 +16,11 @@ package _integration import ( "fmt" - "github.com/apache/trafficcontrol/lib/go-log" "net/http" "testing" "time" + "github.com/apache/trafficcontrol/lib/go-log" "github.com/apache/trafficcontrol/lib/go-tc" ) diff --git a/traffic_monitor/tmclient/tmclient.go b/traffic_monitor/tmclient/tmclient.go index e620971d9c..e610084d81 100644 --- a/traffic_monitor/tmclient/tmclient.go +++ b/traffic_monitor/tmclient/tmclient.go @@ -23,13 +23,13 @@ import ( "encoding/json" "errors" "fmt" - "github.com/apache/trafficcontrol/lib/go-log" "io/ioutil" "net/http" "strconv" "strings" "time" + "github.com/apache/trafficcontrol/lib/go-log" "github.com/apache/trafficcontrol/lib/go-tc" "github.com/apache/trafficcontrol/traffic_monitor/datareq" "github.com/apache/trafficcontrol/traffic_monitor/dsdata" From d049ef63a5e325547a94caaa8684ea1546f5d362 Mon Sep 17 00:00:00 2001 From: shamrickus Date: Mon, 24 May 2021 12:42:26 -0600 Subject: [PATCH 20/22] Code review fixes --- traffic_monitor/tests/_integration/Dockerfile | 16 ++++++---- .../tests/_integration/Dockerfile_run.sh | 19 ++++++------ traffic_monitor/tests/_integration/README.md | 8 ++--- .../{ => tests/_integration}/build_tests.sh | 30 ++++++------------- .../tests/_integration/docker-compose.yml | 10 +++---- .../tests/_integration/kbps_test.go | 2 +- .../{ => tests/_integration/tm}/Dockerfile | 0 .../_integration/tm}/Dockerfile_run.sh | 0 .../{ => tests/_integration}/variables.env | 0 traffic_monitor/tmclient/tmclient.go | 2 +- traffic_monitor/tools/testcaches/Dockerfile | 12 +++++--- .../tools/testcaches/fakesrvr/server.go | 2 +- traffic_monitor/tools/testto/Dockerfile | 12 +++++--- traffic_monitor/tools/testto/testto.go | 3 +- 14 files changed, 56 insertions(+), 60 deletions(-) rename traffic_monitor/{ => tests/_integration}/build_tests.sh (56%) rename traffic_monitor/{ => tests/_integration/tm}/Dockerfile (100%) rename traffic_monitor/{ => tests/_integration/tm}/Dockerfile_run.sh (100%) rename traffic_monitor/{ => tests/_integration}/variables.env (100%) diff --git a/traffic_monitor/tests/_integration/Dockerfile b/traffic_monitor/tests/_integration/Dockerfile index 16f6c48432..4f8e835cfe 100644 --- a/traffic_monitor/tests/_integration/Dockerfile +++ b/traffic_monitor/tests/_integration/Dockerfile @@ -23,11 +23,15 @@ FROM centos:8 MAINTAINER dev@trafficcontrol.apache.org -RUN yum install -y initscripts epel-release golang glibc jq +RUN dnf install -y initscripts epel-release golang glibc jq git +ENV GOPATH=/go -COPY traffic_monitor_integration_test / +COPY ./tests/_integration/ /tm +WORKDIR /tm -COPY Dockerfile_run.sh / -COPY monitoring.json / -COPY snapshot.json / -ENTRYPOINT /Dockerfile_run.sh +RUN go get -u github.com/apache/trafficcontrol/lib/go-log github.com/apache/trafficcontrol/lib/go-tc github.com/apache/trafficcontrol/traffic_monitor +COPY . ${GOPATH}/src/github.com/apache/trafficcontrol/traffic_monitor/ + +RUN go test -c -o /traffic_monitor_integration_test + +CMD ./Dockerfile_run.sh diff --git a/traffic_monitor/tests/_integration/Dockerfile_run.sh b/traffic_monitor/tests/_integration/Dockerfile_run.sh index 480147d5f6..48f5932ee8 100755 --- a/traffic_monitor/tests/_integration/Dockerfile_run.sh +++ b/traffic_monitor/tests/_integration/Dockerfile_run.sh @@ -31,7 +31,7 @@ for v in $envvars; do done TO_API_VERSION=4.0 -CFG_FILE=/traffic-monitor-integration-test.cfg +CFG_FILE=/tm/traffic-monitor-integration-test.cfg start() { printf "DEBUG traffic_monitor_integration starting\n" @@ -47,11 +47,11 @@ init() { TESTCACHES_GATEWAY=$(echo $TESTCACHES_ADDRESS | sed "s/\([0-9]\+.[0-9]\+.[0-9]\+.\)[0-9]/\11/") jq "(.. | .address?) |= \"$TESTCACHES_ADDRESS\" | (.. | .gateway?) |= \"$TESTCACHES_GATEWAY\"" \ - /monitoring.json >/monitoring.json.tmp && mv /monitoring.json.tmp /monitoring.json + /tm/monitoring.json > /tm/monitoring.json.tmp && mv /tm/monitoring.json.tmp /tm/monitoring.json - curl -Lvsk ${TESTTO_URI}/api/${TO_API_VERSION}/cdns/fake/snapshot -X POST -d "@/snapshot.json" + curl -Lvsk ${TESTTO_URI}/api/${TO_API_VERSION}/cdns/fake/snapshot -X POST -d "@/tm/snapshot.json" - curl -Lvsk ${TESTTO_URI}/api/${TO_API_VERSION}/cdns/fake/configs/monitoring -X POST -d '@/monitoring.json' + curl -Lvsk ${TESTTO_URI}/api/${TO_API_VERSION}/cdns/fake/configs/monitoring -X POST -d '@/tm/monitoring.json' curl -Lvsk ${TESTTO_URI}/api/${TO_API_VERSION}/servers -X POST -d ' [ @@ -148,15 +148,14 @@ EOF function wait_for_endpoint() { try=0 - while curl -Lvsk "$1" 2>&1 1>/dev/null | grep -Eq '< HTTP/[0-9]\.?[0-9]* 200' && [[ $try -lt 5 ]]; do - if [[ $try -gt 0 ]]; then - echo "Waiting for $1 to return a 200 OK" - sleep 5 - else + while [ $(curl -Lsk --write-out "%{http_code}" "$1" -o /dev/null) -ne 200 ] ; do + echo "Waiting for $1 to return a 200 OK" + try=$(expr $try + 1) + if [[ $try -gt 5 ]]; then echo "Unable to get $1" exit 1 fi - try=$(expr $try + 1) + sleep 5 done } export -f wait_for_endpoint diff --git a/traffic_monitor/tests/_integration/README.md b/traffic_monitor/tests/_integration/README.md index 6c6e9c27db..7eaaf5866f 100644 --- a/traffic_monitor/tests/_integration/README.md +++ b/traffic_monitor/tests/_integration/README.md @@ -2,12 +2,8 @@ ## Building -From the `trafficcontrol` run `./pkg -v traffic_monitor_build` and copy the traffic_monitor rpm to the `traffic_monitor` directory. - -From the `trafficcontrol/traffic_monitor` directory run `build_tests.sh` +From this directory, run `build_tests.sh`. This will build the Traffic Monitor RPM as well as run a docker build. ## Running -From the `trafficcontrol/traffic_monitor` directory run: - -`sudo docker-compose -p tmi --project-directory . -f tests/_integration/docker-compose.yml run tmintegrationtest` +From this directory, run `docker-compose run tmintegrationtest` diff --git a/traffic_monitor/build_tests.sh b/traffic_monitor/tests/_integration/build_tests.sh similarity index 56% rename from traffic_monitor/build_tests.sh rename to traffic_monitor/tests/_integration/build_tests.sh index 999adf0412..371e130bdd 100755 --- a/traffic_monitor/build_tests.sh +++ b/traffic_monitor/tests/_integration/build_tests.sh @@ -12,26 +12,14 @@ # See the License for the specific language governing permissions and # limitations under the License. -export CGO_ENABLED=0 -export GOOS=linux - -if [ ! -f "traffic_monitor.rpm" ]; then - echo "Unable to find traffic_monitor.rpm." - echo "Run './pkg traffic_monitor_build' and copy TM rpm to this directory." - exit 1 +cd ../../../ +./pkg traffic_monitor_build +rpm=`ls dist | grep monitor | grep -v log | grep -v src | grep "$(git rev-parse --short=8 HEAD)"` +if [ $? -ne 0 ]; then + echo "Unable to build TM" + exit 1; fi -cd tools/testto -rm testto -go build -cd - > /dev/null - -cd tools/testcaches -rm testcaches -go build -cd - > /dev/null - -cd tests/_integration -rm traffic_monitor_integration_test -go test -c -o traffic_monitor_integration_test -cd - > /dev/null +cp "dist/$rpm" "traffic_monitor/tests/_integration/tm/traffic_monitor.rpm" +cd - +docker-compose build diff --git a/traffic_monitor/tests/_integration/docker-compose.yml b/traffic_monitor/tests/_integration/docker-compose.yml index 7aa1cb9ae8..a252c7f762 100644 --- a/traffic_monitor/tests/_integration/docker-compose.yml +++ b/traffic_monitor/tests/_integration/docker-compose.yml @@ -38,7 +38,7 @@ version: '2.1' services: testto: build: - context: ./tools/testto + context: ../../tools/testto dockerfile: ./Dockerfile domainname: traffic-monitor-integration.test env_file: @@ -50,7 +50,7 @@ services: testcaches: build: - context: ./tools/testcaches + context: ../../tools/testcaches dockerfile: ./Dockerfile domainname: traffic-monitor-integration.test env_file: @@ -62,7 +62,7 @@ services: trafficmonitor: build: - context: . + context: ./tm dockerfile: ./Dockerfile args: RPM: ./traffic_monitor.rpm @@ -80,8 +80,8 @@ services: tmintegrationtest: build: - context: ./tests/_integration - dockerfile: ./Dockerfile + context: ../../ + dockerfile: ./tests/_integration/Dockerfile depends_on: - testto - testcaches diff --git a/traffic_monitor/tests/_integration/kbps_test.go b/traffic_monitor/tests/_integration/kbps_test.go index d72cd108c3..410219f679 100644 --- a/traffic_monitor/tests/_integration/kbps_test.go +++ b/traffic_monitor/tests/_integration/kbps_test.go @@ -60,7 +60,7 @@ func TestKBPS(t *testing.T) { if err != nil { t.Fatalf("Error posting fake cache command '" + uri + "': " + err.Error()) } - log.Close(resp.Body, "Unable to close http client "+uri) + defer log.Close(resp.Body, "Unable to close http client "+uri) time.Sleep(time.Second * 5) // TODO determine if there's a faster or more precise way to wait for polled data? diff --git a/traffic_monitor/Dockerfile b/traffic_monitor/tests/_integration/tm/Dockerfile similarity index 100% rename from traffic_monitor/Dockerfile rename to traffic_monitor/tests/_integration/tm/Dockerfile diff --git a/traffic_monitor/Dockerfile_run.sh b/traffic_monitor/tests/_integration/tm/Dockerfile_run.sh similarity index 100% rename from traffic_monitor/Dockerfile_run.sh rename to traffic_monitor/tests/_integration/tm/Dockerfile_run.sh diff --git a/traffic_monitor/variables.env b/traffic_monitor/tests/_integration/variables.env similarity index 100% rename from traffic_monitor/variables.env rename to traffic_monitor/tests/_integration/variables.env diff --git a/traffic_monitor/tmclient/tmclient.go b/traffic_monitor/tmclient/tmclient.go index e610084d81..732a52ebe1 100644 --- a/traffic_monitor/tmclient/tmclient.go +++ b/traffic_monitor/tmclient/tmclient.go @@ -202,7 +202,7 @@ func (c *TMClient) getBytes(path string) ([]byte, error) { if err != nil { return nil, errors.New("getting from '" + url + "': " + err.Error()) } - log.Close(resp.Body, "Unable to close http client "+url) + defer log.Close(resp.Body, "Unable to close http client "+url) if resp.StatusCode < 200 || resp.StatusCode > 299 { return nil, fmt.Errorf("Monitor '"+url+"' returned bad status %v", resp.StatusCode) diff --git a/traffic_monitor/tools/testcaches/Dockerfile b/traffic_monitor/tools/testcaches/Dockerfile index 4aa9f63f58..595e8da548 100644 --- a/traffic_monitor/tools/testcaches/Dockerfile +++ b/traffic_monitor/tools/testcaches/Dockerfile @@ -18,8 +18,12 @@ FROM centos:8 MAINTAINER dev@trafficcontrol.apache.org -ARG APP_BIN=testcaches -ADD $APP_BIN /usr/sbin/ +RUN dnf -y install golang git +ENV GOPATH=/go -ADD Dockerfile_run.sh / -CMD /Dockerfile_run.sh +RUN go get -u github.com/apache/trafficcontrol/lib/go-rfc +ADD . ${GOPATH}/src/github.com/apache/trafficcontrol/traffic_monitor/tools/testcaches/ +WORKDIR ${GOPATH}/src/github.com/apache/trafficcontrol/traffic_monitor/tools/testcaches +RUN go build && cp testcaches /usr/sbin/ + +CMD ${GOPATH}/src/github.com/apache/trafficcontrol/traffic_monitor/tools/testcaches/Dockerfile_run.sh diff --git a/traffic_monitor/tools/testcaches/fakesrvr/server.go b/traffic_monitor/tools/testcaches/fakesrvr/server.go index 5fd5e0ae8e..7ba81d2419 100644 --- a/traffic_monitor/tools/testcaches/fakesrvr/server.go +++ b/traffic_monitor/tools/testcaches/fakesrvr/server.go @@ -22,7 +22,6 @@ package fakesrvr import ( "encoding/json" "fmt" - "github.com/apache/trafficcontrol/lib/go-rfc" "html" "math/rand" "net/http" @@ -31,6 +30,7 @@ import ( "sync/atomic" "time" + "github.com/apache/trafficcontrol/lib/go-rfc" "github.com/apache/trafficcontrol/traffic_monitor/tools/testcaches/fakesrvrdata" ) diff --git a/traffic_monitor/tools/testto/Dockerfile b/traffic_monitor/tools/testto/Dockerfile index 7e1f935c6b..52b4badf72 100644 --- a/traffic_monitor/tools/testto/Dockerfile +++ b/traffic_monitor/tools/testto/Dockerfile @@ -18,8 +18,12 @@ FROM centos:8 MAINTAINER dev@trafficcontrol.apache.org -ARG APP_BIN=testto -ADD $APP_BIN /usr/sbin/ +RUN dnf -y install golang git +ENV GOPATH=/go -ADD Dockerfile_run.sh / -CMD /Dockerfile_run.sh +RUN go get -u github.com/apache/trafficcontrol/lib/go-rfc github.com/apache/trafficcontrol/lib/go-tc +ADD . ${GOPATH}/src/github.com/apache/trafficcontrol/traffic_monitor/tools/testto +WORKDIR ${GOPATH}/src/github.com/apache/trafficcontrol/traffic_monitor/tools/testto +RUN go build && cp testto /usr/sbin + +CMD ${GOPATH}/src/github.com/apache/trafficcontrol/traffic_monitor/tools/testto/Dockerfile_run.sh diff --git a/traffic_monitor/tools/testto/testto.go b/traffic_monitor/tools/testto/testto.go index 5f18237325..24f310c2db 100644 --- a/traffic_monitor/tools/testto/testto.go +++ b/traffic_monitor/tools/testto/testto.go @@ -23,7 +23,6 @@ import ( "encoding/json" "flag" "fmt" - "github.com/apache/trafficcontrol/lib/go-rfc" "log" "net/http" "regexp" @@ -31,6 +30,7 @@ import ( "sync" "time" + "github.com/apache/trafficcontrol/lib/go-rfc" "github.com/apache/trafficcontrol/lib/go-tc" ) @@ -116,6 +116,7 @@ func RouteHandler(fakeTOData Ths) http.HandlerFunc { } } w.WriteHeader(http.StatusNotFound) + w.Write([]byte(r.URL.Path)) } } From 6e24d6b9c868e6375ee64c29babccdad0597e985 Mon Sep 17 00:00:00 2001 From: shamrickus Date: Mon, 24 May 2021 12:45:35 -0600 Subject: [PATCH 21/22] Missed some feedback --- traffic_monitor/tests/_integration/tm/Dockerfile_run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/traffic_monitor/tests/_integration/tm/Dockerfile_run.sh b/traffic_monitor/tests/_integration/tm/Dockerfile_run.sh index 2e1682dc5f..6ab040a228 100755 --- a/traffic_monitor/tests/_integration/tm/Dockerfile_run.sh +++ b/traffic_monitor/tests/_integration/tm/Dockerfile_run.sh @@ -24,7 +24,7 @@ # Check that env vars are set envvars=( TO_URI TO_USER TO_PASS CDN PORT ) -for v in $envvars +for v in ${envvars[@]} do if [[ -z ${!v} ]]; then echo "$v is unset"; exit 1; fi done From 7f40650b5f1efba1ae859d834a79981acdf9c397 Mon Sep 17 00:00:00 2001 From: shamrickus Date: Mon, 24 May 2021 12:59:54 -0600 Subject: [PATCH 22/22] Didnt mean to commit this --- traffic_monitor/tools/testto/testto.go | 1 - 1 file changed, 1 deletion(-) diff --git a/traffic_monitor/tools/testto/testto.go b/traffic_monitor/tools/testto/testto.go index 24f310c2db..950906f465 100644 --- a/traffic_monitor/tools/testto/testto.go +++ b/traffic_monitor/tools/testto/testto.go @@ -116,7 +116,6 @@ func RouteHandler(fakeTOData Ths) http.HandlerFunc { } } w.WriteHeader(http.StatusNotFound) - w.Write([]byte(r.URL.Path)) } }