This is an automated email from the ASF dual-hosted git repository.

dgelinas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new 3871f80  Fix TM polling urls with ports (#3605)
3871f80 is described below

commit 3871f80bba888bcca535d84d8638cd20e8633fe7
Author: Robert Butts <[email protected]>
AuthorDate: Wed May 29 10:55:37 2019 -0600

    Fix TM polling urls with ports (#3605)
    
    * Fix TM polling urls with ports
    
    * Add Changelog issue number '#'
    
    * Add Docs terms for monitor poll url info
---
 CHANGELOG.md                                  |   2 +-
 docs/source/admin/traffic_monitor.rst         |  21 ++++++
 lib/go-tc/traffic_router.go                   |   1 +
 traffic_monitor/manager/monitorconfig.go      |  64 +++++++++++-----
 traffic_monitor/manager/monitorconfig_test.go | 102 ++++++++++++++++++++++++++
 traffic_monitor/towrap/towrap.go              |   5 ++
 6 files changed, 177 insertions(+), 18 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a14f8b7..97dedc2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -48,7 +48,7 @@ The format is based on [Keep a 
Changelog](http://keepachangelog.com/en/1.0.0/).
 - Issue 3476: Traffic Router returns partial result for CLIENT_STEERING 
Delivery Services when Regional Geoblocking or Anonymous Blocking is enabled.
 - Upgraded Traffic Portal to AngularJS 1.7.8
 - Issue 3275: Improved the snapshot diff performance and experience.
-
+- Issue #3605: Fixed Traffic Monitor custom ports in health polling URL.
 
 ## [3.0.0] - 2018-10-30
 ### Added
diff --git a/docs/source/admin/traffic_monitor.rst 
b/docs/source/admin/traffic_monitor.rst
index 85dab8c..046350a 100755
--- a/docs/source/admin/traffic_monitor.rst
+++ b/docs/source/admin/traffic_monitor.rst
@@ -52,6 +52,27 @@ Configuration Overview
 ----------------------
 Traffic Monitor is configured via two JSON configuration files, 
:file:`traffic_ops.cfg` and :file:`traffic_monitor.cfg`, by default located in 
the ``conf`` directory in the install location. :file:`traffic_ops.cfg` 
contains Traffic Ops connection information. Specify the URL, username, and 
password for the instance of Traffic Ops of which this Traffic Monitor is a 
member. :file:`traffic_monitor.cfg` contains log file locations, as well as 
detailed application configuration variables such [...]
 
+
+Cache Polling URL
+-----------------------------------
+
+The :term:`cache servers` are polled at the URL specified in the 
``health.polling.url`` :term:`parameter`, on the :term:`cache server`'s 
:term:`profile`.
+
+This :term:`parameter` must have the config file ``rascal.properties``.
+
+The value is a template with the text ``${hostname}`` being replaced with the 
:term:`cache server`'s Network IP (IPv4), and ``${interface_name}`` being 
replaced with the :term:`cache server`'s network Interface Name. For example, 
``http://${hostname}/_astats?application=&inf.name=${interface_name}``.
+
+If the template contains a port, that port will be used, and the :term:`cache 
server`'s HTTPS and TCP Ports will not be added.
+
+If the template does not contain a port, then if the template starts with 
``https`` the :term:`cache server`'s HTTPS Port will be added, and if the 
template doesn't start with ``https`` the :term:`cache server`'s TCP Port will 
be added.
+
+Examples:
+
+Template 
``http://${hostname}/_astats?application=&inf.name=${interface_name}`` Server 
IP ``192.0.2.42`` Server TCP Port ``8080`` HTTPS Port ``8443`` becomes 
``http://192.0.2.42:8080/_astats?application=&inf.name=${interface_name}``.
+Template 
``https://${hostname}/_astats?application=&inf.name=${interface_name}`` Server 
IP ``192.0.2.42`` Server TCP Port ``8080`` HTTPS Port ``8443`` becomes 
``https://192.0.2.42:8443/_astats?application=&inf.name=${interface_name}``.
+Template 
``http://${hostname}:1234/_astats?application=&inf.name=${interface_name}`` 
Server IP ``192.0.2.42`` Server TCP Port ``8080`` HTTPS Port ``8443`` becomes 
``http://192.0.2.42:1234/_astats?application=&inf.name=${interface_name}``.
+Template 
``https://${hostname}:1234/_astats?application=&inf.name=${interface_name}`` 
Server IP ``192.0.2.42`` Server TCP Port ``8080`` HTTPS Port ``8443`` becomes 
``https://192.0.2.42:1234/_astats?application=&inf.name=${interface_name}``.
+
 Stat and Health Flush Configuration
 -----------------------------------
 The Monitor has a health flush interval, a stat flush interval, and a stat 
buffer interval. Recall that the monitor polls both stats and health. The 
health poll is so small and fast, a buffer is largely unnecessary. However, in 
a large CDN, the stat poll may involve thousands of :term:`cache server` s with 
thousands of stats each, or more, and CPU may be a bottleneck.
diff --git a/lib/go-tc/traffic_router.go b/lib/go-tc/traffic_router.go
index f636f7a..554f64b 100644
--- a/lib/go-tc/traffic_router.go
+++ b/lib/go-tc/traffic_router.go
@@ -136,6 +136,7 @@ type TrafficServer struct {
        CacheGroup       string              `json:"cacheGroup"`
        IP6              string              `json:"ip6"`
        Port             int                 `json:"port"`
+       HTTPSPort        int                 `json:"httpsPort,omitempty"`
        HostName         string              `json:"hostName"`
        FQDN             string              `json:"fqdn"`
        InterfaceName    string              `json:"interfaceName"`
diff --git a/traffic_monitor/manager/monitorconfig.go 
b/traffic_monitor/manager/monitorconfig.go
index ab0bfbc..341c743 100644
--- a/traffic_monitor/manager/monitorconfig.go
+++ b/traffic_monitor/manager/monitorconfig.go
@@ -21,6 +21,7 @@ package manager
 
 import (
        "fmt"
+       "net/url"
        "os"
        "strconv"
        "strings"
@@ -250,8 +251,8 @@ func monitorConfigListen(
                                localStates.AddCache(cacheName, 
tc.IsAvailable{IsAvailable: false})
                        }
 
-                       url := 
monitorConfig.Profile[srv.Profile].Parameters.HealthPollingURL
-                       if url == "" {
+                       pollURLStr := 
monitorConfig.Profile[srv.Profile].Parameters.HealthPollingURL
+                       if pollURLStr == "" {
                                log.Errorf("monitor config server %v profile %v 
has no polling URL; can't poll", srv.HostName, srv.Profile)
                                continue
                        }
@@ -268,18 +269,7 @@ func monitorConfigListen(
                                log.Infof("health.polling.type for '%v' is 
empty, using default '%v'", srv.HostName, pollType)
                        }
 
-                       port := ""
-                       if srv.Port != 0 {
-                               port = ":" + strconv.Itoa(srv.Port)
-                       }
-
-                       r := strings.NewReplacer(
-                               "${hostname}", srv.IP+port,
-                               "${interface_name}", srv.InterfaceName,
-                               "application=plugin.remap", 
"application=system",
-                               "application=", "application=system",
-                       )
-                       url = r.Replace(url)
+                       pollURLStr = createServerHealthPollURL(pollURLStr, srv)
 
                        connTimeout := 
trafficOpsHealthConnectionTimeoutToDuration(monitorConfig.Profile[srv.Profile].Parameters.HealthConnectionTimeout)
                        if connTimeout == 0 {
@@ -287,9 +277,9 @@ func monitorConfigListen(
                                log.Warnln("profile " + srv.Profile + " 
health.connection.timeout Parameter is missing or zero, using default " + 
DefaultHealthConnectionTimeout.String())
                        }
 
-                       healthURLs[srv.HostName] = poller.PollConfig{URL: url, 
Host: srv.FQDN, Timeout: connTimeout, Format: format, PollType: pollType}
-                       r = strings.NewReplacer("application=system", 
"application=")
-                       statURL := r.Replace(url)
+                       healthURLs[srv.HostName] = poller.PollConfig{URL: 
pollURLStr, Host: srv.FQDN, Timeout: connTimeout, Format: format, PollType: 
pollType}
+
+                       statURL := createServerStatPollURL(pollURLStr)
                        statURLs[srv.HostName] = poller.PollConfig{URL: 
statURL, Host: srv.FQDN, Timeout: connTimeout, Format: format, PollType: 
pollType}
                }
 
@@ -341,3 +331,43 @@ func monitorConfigListen(
                }
        }
 }
+
+// createServerHealthPollURL takes the template pollingURLStr, and replaces 
variables with data from srv, and returns the polling URL for srv.
+func createServerHealthPollURL(pollingURLStr string, srv tc.TrafficServer) 
string {
+       pollingURLStr = strings.NewReplacer(
+               "${hostname}", srv.IP,
+               "${interface_name}", srv.InterfaceName,
+               "application=plugin.remap", "application=system",
+               "application=", "application=system",
+       ).Replace(pollingURLStr)
+
+       if strings.HasPrefix(strings.ToLower(pollingURLStr), "https") {
+               if srv.HTTPSPort != 0 {
+                       pollURL, err := url.Parse(pollingURLStr)
+                       if err != nil {
+                               log.Warnln("profile " + srv.Profile + " cache 
'" + srv.FQDN + "' polling URL '" + pollingURLStr + "' failed to parse, may not 
be a valid URL! Using anyway, not using custom HTTPS Port " + 
strconv.Itoa(srv.HTTPSPort) + "!")
+                       } else if pollURL.Port() == "" { // if there's both an 
HTTPS Port and a port in the polling URL, the polling URL takes precedence
+                               pollURL.Host += ":" + 
strconv.Itoa(srv.HTTPSPort)
+                               pollingURLStr = pollURL.String()
+                       }
+               }
+       } else {
+               if srv.Port != 0 {
+                       pollURL, err := url.Parse(pollingURLStr)
+                       if err != nil {
+                               log.Warnln("profile " + srv.Profile + " cache 
'" + srv.FQDN + "' polling URL '" + pollingURLStr + "' failed to parse, may not 
be a valid URL! Using anyway, not using custom TCP Port " + 
strconv.Itoa(srv.Port) + "!")
+                       } else if pollURL.Port() == "" { // if there's both a 
TCP Port and a port in the polling URL, the polling URL takes precedence
+                               pollURL.Host += ":" + strconv.Itoa(srv.Port)
+                               pollingURLStr = pollURL.String()
+                       }
+               }
+       }
+
+       return pollingURLStr
+}
+
+// createServerStatPollURL takes the health polling URL string, and modifies 
it to be the stat poll URL.
+// Note this does not replace template variables with server values, 
healthPollURLStr must be the health URL for a given server, not a template.
+func createServerStatPollURL(healthPollURLStr string) string {
+       return strings.NewReplacer("application=system", 
"application=").Replace(healthPollURLStr)
+}
diff --git a/traffic_monitor/manager/monitorconfig_test.go 
b/traffic_monitor/manager/monitorconfig_test.go
new file mode 100644
index 0000000..5d5b145
--- /dev/null
+++ b/traffic_monitor/manager/monitorconfig_test.go
@@ -0,0 +1,102 @@
+package manager
+
+/*
+ * 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 (
+       "strconv"
+       "testing"
+
+       "github.com/apache/trafficcontrol/lib/go-tc"
+)
+
+func TestCreateServerHealthPollURL(t *testing.T) {
+       tmpl := 
`http://${hostname}/_astats?application=&inf.name=${interface_name}`
+       srv := tc.TrafficServer{IP: "192.0.2.42", InterfaceName: "george"}
+
+       expected := `http://` + srv.IP + 
`/_astats?application=system&inf.name=` + srv.InterfaceName
+       actual := createServerHealthPollURL(tmpl, srv)
+
+       if expected != actual {
+               t.Errorf("expected createServerHealthPollURL '" + expected + "' 
actual: '" + actual + "'")
+       }
+}
+
+func TestCreateServerHealthPollURLTemplatePort(t *testing.T) {
+       tmpl := 
`http://${hostname}:1234/_astats?application=&inf.name=${interface_name}`
+       srv := tc.TrafficServer{IP: "192.0.2.42", InterfaceName: "george"}
+
+       expected := `http://` + srv.IP + 
`:1234/_astats?application=system&inf.name=` + srv.InterfaceName
+       actual := createServerHealthPollURL(tmpl, srv)
+
+       if expected != actual {
+               t.Errorf("expected createServerHealthPollURL '" + expected + "' 
actual: '" + actual + "'")
+       }
+}
+
+func TestCreateServerHealthPollURLServerPort(t *testing.T) {
+       tmpl := 
`http://${hostname}/_astats?application=&inf.name=${interface_name}`
+       srv := tc.TrafficServer{IP: "192.0.2.42", Port: 5678, HTTPSPort: 910, 
InterfaceName: "george"}
+
+       expected := `http://` + srv.IP + ":" + strconv.Itoa(srv.Port) + 
`/_astats?application=system&inf.name=` + srv.InterfaceName
+       actual := createServerHealthPollURL(tmpl, srv)
+
+       if expected != actual {
+               t.Errorf("expected createServerHealthPollURL '" + expected + "' 
actual: '" + actual + "'")
+       }
+}
+
+func TestCreateServerHealthPollURLServerPortHTTPS(t *testing.T) {
+       tmpl := 
`hTTps://${hostname}/_astats?application=&inf.name=${interface_name}`
+       srv := tc.TrafficServer{IP: "192.0.2.42", Port: 5678, HTTPSPort: 910, 
InterfaceName: "george"}
+
+       expected := `https://` + srv.IP + ":" + strconv.Itoa(srv.HTTPSPort) + 
`/_astats?application=system&inf.name=` + srv.InterfaceName
+       actual := createServerHealthPollURL(tmpl, srv)
+
+       if expected != actual {
+               t.Errorf("expected createServerHealthPollURL '" + expected + "' 
actual: '" + actual + "'")
+       }
+}
+
+func TestCreateServerHealthPollURLTemplateAndServerPort(t *testing.T) {
+
+       // if both template and server ports exist, template takes precedence
+
+       tmpl := 
`http://${hostname}:1234/_astats?application=&inf.name=${interface_name}`
+       srv := tc.TrafficServer{IP: "192.0.2.42", Port: 5678, HTTPSPort: 910, 
InterfaceName: "george"}
+
+       expected := `http://` + srv.IP + 
`:1234/_astats?application=system&inf.name=` + srv.InterfaceName
+       actual := createServerHealthPollURL(tmpl, srv)
+
+       if expected != actual {
+               t.Errorf("expected createServerHealthPollURL '" + expected + "' 
actual: '" + actual + "'")
+       }
+}
+
+func TestCreateServerStatPollURL(t *testing.T) {
+       tmpl := 
`http://${hostname}/_astats?application=&inf.name=${interface_name}`
+       srv := tc.TrafficServer{IP: "192.0.2.42", InterfaceName: "george"}
+
+       expected := `http://` + srv.IP + `/_astats?application=&inf.name=` + 
srv.InterfaceName
+       actual := createServerStatPollURL(createServerHealthPollURL(tmpl, srv))
+
+       if expected != actual {
+               t.Errorf("expected createServerStatPollURL '" + expected + "' 
actual: '" + actual + "'")
+       }
+}
diff --git a/traffic_monitor/towrap/towrap.go b/traffic_monitor/towrap/towrap.go
index 8b3b82f..87139f0 100644
--- a/traffic_monitor/towrap/towrap.go
+++ b/traffic_monitor/towrap/towrap.go
@@ -470,6 +470,11 @@ func CreateMonitorConfig(crConfig tc.CRConfig, mc 
*tc.TrafficMonitorConfigMap) (
                } else {
                        log.Warnf("Creating monitor config: CRConfig server %s 
missing HashId field\n", name)
                }
+               if srv.HttpsPort != nil {
+                       s.HTTPSPort = *srv.HttpsPort
+               } else {
+                       log.Warnf("Creating monitor config: CRConfig server %s 
missing HttpsPort field\n", name)
+               }
                mc.TrafficServer[name] = s
        }
 

Reply via email to