You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by de...@apache.org on 2017/08/10 16:04:57 UTC

[16/43] incubator-trafficcontrol git commit: Add TO Golang configurable logging

Add TO Golang configurable logging


Project: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/commit/b5fa231b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/tree/b5fa231b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/diff/b5fa231b

Branch: refs/heads/master
Commit: b5fa231b1d258c257984c3a24226901092a81b9f
Parents: 5cb00dd
Author: Robert Butts <ro...@gmail.com>
Authored: Fri Jul 21 18:14:38 2017 -0600
Committer: Dewayne Richardson <de...@apache.org>
Committed: Thu Aug 10 09:46:03 2017 -0600

----------------------------------------------------------------------
 traffic_monitor_golang/common/log/log.go        |   1 +
 .../traffic_monitor/config/config.go            |   1 +
 traffic_ops/traffic_ops_golang/auth.go          |   7 +-
 traffic_ops/traffic_ops_golang/config.go        |  52 +-
 traffic_ops/traffic_ops_golang/monitoring.go    |   5 +-
 .../traffic_ops_golang/monitoring_test.go       | 634 ++++++++++++
 traffic_ops/traffic_ops_golang/routes.go        |   2 +-
 .../traffic_ops_golang/traffic_ops_golang.go    |  18 +-
 .../vendor/gopkg.in/DATA-DOG/go-sqlmock/LICENSE |  28 +
 .../gopkg.in/DATA-DOG/go-sqlmock/README.md      | 223 +++++
 .../gopkg.in/DATA-DOG/go-sqlmock/argument.go    |  24 +
 .../DATA-DOG/go-sqlmock/argument_test.go        |  58 ++
 .../gopkg.in/DATA-DOG/go-sqlmock/driver.go      |  78 ++
 .../gopkg.in/DATA-DOG/go-sqlmock/driver_test.go | 112 +++
 .../DATA-DOG/go-sqlmock/examples/basic/basic.go |  40 +
 .../go-sqlmock/examples/basic/basic_test.go     |  58 ++
 .../DATA-DOG/go-sqlmock/examples/blog/blog.go   |  81 ++
 .../go-sqlmock/examples/blog/blog_test.go       | 102 ++
 .../DATA-DOG/go-sqlmock/examples/doc.go         |   1 +
 .../go-sqlmock/examples/orders/orders.go        | 121 +++
 .../go-sqlmock/examples/orders/orders_test.go   | 108 ++
 .../DATA-DOG/go-sqlmock/expectations.go         | 344 +++++++
 .../go-sqlmock/expectations_before_go18.go      |  52 +
 .../DATA-DOG/go-sqlmock/expectations_go18.go    |  66 ++
 .../go-sqlmock/expectations_go18_test.go        |  64 ++
 .../DATA-DOG/go-sqlmock/expectations_test.go    | 154 +++
 .../gopkg.in/DATA-DOG/go-sqlmock/result.go      |  39 +
 .../gopkg.in/DATA-DOG/go-sqlmock/result_test.go |  62 ++
 .../vendor/gopkg.in/DATA-DOG/go-sqlmock/rows.go | 144 +++
 .../gopkg.in/DATA-DOG/go-sqlmock/rows_go18.go   |  20 +
 .../DATA-DOG/go-sqlmock/rows_go18_test.go       |  92 ++
 .../gopkg.in/DATA-DOG/go-sqlmock/rows_test.go   | 265 +++++
 .../gopkg.in/DATA-DOG/go-sqlmock/sqlmock.go     | 525 ++++++++++
 .../DATA-DOG/go-sqlmock/sqlmock_go18.go         | 101 ++
 .../DATA-DOG/go-sqlmock/sqlmock_go18_test.go    | 426 ++++++++
 .../DATA-DOG/go-sqlmock/sqlmock_test.go         | 998 +++++++++++++++++++
 .../gopkg.in/DATA-DOG/go-sqlmock/statement.go   |  27 +
 .../DATA-DOG/go-sqlmock/statement_test.go       |  33 +
 .../gopkg.in/DATA-DOG/go-sqlmock/stubs_test.go  |  76 ++
 .../vendor/gopkg.in/DATA-DOG/go-sqlmock/util.go |  13 +
 .../gopkg.in/DATA-DOG/go-sqlmock/util_test.go   |  21 +
 traffic_ops/traffic_ops_golang/wrappers.go      |  45 +-
 traffic_ops_golang/monitoring_test.go           | 634 ------------
 .../vendor/gopkg.in/DATA-DOG/go-sqlmock/LICENSE |  28 -
 .../gopkg.in/DATA-DOG/go-sqlmock/README.md      | 223 -----
 .../gopkg.in/DATA-DOG/go-sqlmock/argument.go    |  24 -
 .../DATA-DOG/go-sqlmock/argument_test.go        |  58 --
 .../gopkg.in/DATA-DOG/go-sqlmock/driver.go      |  78 --
 .../gopkg.in/DATA-DOG/go-sqlmock/driver_test.go | 112 ---
 .../DATA-DOG/go-sqlmock/examples/basic/basic.go |  40 -
 .../go-sqlmock/examples/basic/basic_test.go     |  58 --
 .../DATA-DOG/go-sqlmock/examples/blog/blog.go   |  81 --
 .../go-sqlmock/examples/blog/blog_test.go       | 102 --
 .../DATA-DOG/go-sqlmock/examples/doc.go         |   1 -
 .../go-sqlmock/examples/orders/orders.go        | 121 ---
 .../go-sqlmock/examples/orders/orders_test.go   | 108 --
 .../DATA-DOG/go-sqlmock/expectations.go         | 344 -------
 .../go-sqlmock/expectations_before_go18.go      |  52 -
 .../DATA-DOG/go-sqlmock/expectations_go18.go    |  66 --
 .../go-sqlmock/expectations_go18_test.go        |  64 --
 .../DATA-DOG/go-sqlmock/expectations_test.go    | 154 ---
 .../gopkg.in/DATA-DOG/go-sqlmock/result.go      |  39 -
 .../gopkg.in/DATA-DOG/go-sqlmock/result_test.go |  62 --
 .../vendor/gopkg.in/DATA-DOG/go-sqlmock/rows.go | 144 ---
 .../gopkg.in/DATA-DOG/go-sqlmock/rows_go18.go   |  20 -
 .../DATA-DOG/go-sqlmock/rows_go18_test.go       |  92 --
 .../gopkg.in/DATA-DOG/go-sqlmock/rows_test.go   | 265 -----
 .../gopkg.in/DATA-DOG/go-sqlmock/sqlmock.go     | 525 ----------
 .../DATA-DOG/go-sqlmock/sqlmock_go18.go         | 101 --
 .../DATA-DOG/go-sqlmock/sqlmock_go18_test.go    | 426 --------
 .../DATA-DOG/go-sqlmock/sqlmock_test.go         | 998 -------------------
 .../gopkg.in/DATA-DOG/go-sqlmock/statement.go   |  27 -
 .../DATA-DOG/go-sqlmock/statement_test.go       |  33 -
 .../gopkg.in/DATA-DOG/go-sqlmock/stubs_test.go  |  76 --
 .../vendor/gopkg.in/DATA-DOG/go-sqlmock/util.go |  13 -
 .../gopkg.in/DATA-DOG/go-sqlmock/util_test.go   |  21 -
 76 files changed, 5292 insertions(+), 5219 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_monitor_golang/common/log/log.go
----------------------------------------------------------------------
diff --git a/traffic_monitor_golang/common/log/log.go b/traffic_monitor_golang/common/log/log.go
index bfa4a6e..dd63892 100644
--- a/traffic_monitor_golang/common/log/log.go
+++ b/traffic_monitor_golang/common/log/log.go
@@ -23,6 +23,7 @@ package log
 import (
 	"fmt"
 	"io"
+	"io/ioutil"
 	"log"
 	"os"
 	"time"

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_monitor_golang/traffic_monitor/config/config.go
----------------------------------------------------------------------
diff --git a/traffic_monitor_golang/traffic_monitor/config/config.go b/traffic_monitor_golang/traffic_monitor/config/config.go
index 3939587..358541b 100644
--- a/traffic_monitor_golang/traffic_monitor/config/config.go
+++ b/traffic_monitor_golang/traffic_monitor/config/config.go
@@ -21,6 +21,7 @@ package config
 
 import (
 	"encoding/json"
+	"io"
 	"io/ioutil"
 	"time"
 

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/auth.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/auth.go b/traffic_ops/traffic_ops_golang/auth.go
index d7e5efe..8ad646a 100644
--- a/traffic_ops/traffic_ops_golang/auth.go
+++ b/traffic_ops/traffic_ops_golang/auth.go
@@ -21,7 +21,8 @@ package main
 
 import (
 	"database/sql"
-	"log" // TODO change to traffic_monitor_golang/common/log
+
+	"github.com/apache/incubator-trafficcontrol/traffic_monitor_golang/common/log"
 )
 
 func preparePrivLevelStmt(db *sql.DB) (*sql.Stmt, error) {
@@ -33,10 +34,10 @@ func hasPrivLevel(privLevelStmt *sql.Stmt, user string, level int) bool {
 	err := privLevelStmt.QueryRow(user).Scan(&privLevel)
 	switch {
 	case err == sql.ErrNoRows:
-		log.Println("Error checking user " + user + " priv level: user not in database")
+		log.Errorf("checking user %v priv level: user not in database", user)
 		return false
 	case err != nil:
-		log.Println("Error checking user " + user + " priv level: " + err.Error())
+		log.Errorf("Error checking user %v priv level: %v", user, err.Error())
 		return false
 	default:
 		return privLevel >= level

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/config.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/config.go b/traffic_ops/traffic_ops_golang/config.go
index 6e4e69c..641c9f3 100644
--- a/traffic_ops/traffic_ops_golang/config.go
+++ b/traffic_ops/traffic_ops_golang/config.go
@@ -24,23 +24,36 @@ import (
 	"fmt"
 	"io/ioutil"
 	"net/url"
+
+	"github.com/apache/incubator-trafficcontrol/traffic_monitor_golang/common/log"
 )
 
 type Config struct {
-	HTTPPort string   `json:"port"`
-	DBUser   string   `json:"db_user"`
-	DBPass   string   `json:"db_pass"`
-	DBServer string   `json:"db_server"`
-	DBDB     string   `json:"db_name"`
-	DBSSL    bool     `json:"db_ssl"`
-	TOSecret string   `json:"to_secret"`
-	TOURLStr string   `json:"to_url"`
-	TOURL    *url.URL `json:"-"`
-	NoAuth   bool     `json:"no_auth"`
-	CertPath string   `json:"cert_path"`
-	KeyPath  string   `json:"key_path"`
+	HTTPPort           string   `json:"port"`
+	DBUser             string   `json:"db_user"`
+	DBPass             string   `json:"db_pass"`
+	DBServer           string   `json:"db_server"`
+	DBDB               string   `json:"db_name"`
+	DBSSL              bool     `json:"db_ssl"`
+	TOSecret           string   `json:"to_secret"`
+	TOURLStr           string   `json:"to_url"`
+	TOURL              *url.URL `json:"-"`
+	NoAuth             bool     `json:"no_auth"`
+	CertPath           string   `json:"cert_path"`
+	KeyPath            string   `json:"key_path"`
+	LogLocationError   string   `json:"log_location_error"`
+	LogLocationWarning string   `json:"log_location_warning"`
+	LogLocationInfo    string   `json:"log_location_info"`
+	LogLocationDebug   string   `json:"log_location_debug"`
+	LogLocationEvent   string   `json:"log_location_event"`
 }
 
+func (c Config) Error() log.LogLocation   { return log.LogLocation(c.LogLocationError) }
+func (c Config) Warning() log.LogLocation { return log.LogLocation(c.LogLocationWarning) }
+func (c Config) Info() log.LogLocation    { return log.LogLocation(c.LogLocationInfo) }
+func (c Config) Debug() log.LogLocation   { return log.LogLocation(c.LogLocationDebug) }
+func (c Config) Event() log.LogLocation   { return log.LogLocation(c.LogLocationEvent) }
+
 func LoadConfig(fileName string) (Config, error) {
 	if fileName == "" {
 		return Config{}, fmt.Errorf("no filename")
@@ -89,6 +102,21 @@ func ParseConfig(cfg Config) (Config, error) {
 	if cfg.KeyPath == "" {
 		return Config{}, fmt.Errorf("missing certificate key path")
 	}
+	if cfg.LogLocationError == "" {
+		cfg.LogLocationError = log.LogLocationNull
+	}
+	if cfg.LogLocationWarning == "" {
+		cfg.LogLocationWarning = log.LogLocationNull
+	}
+	if cfg.LogLocationInfo == "" {
+		cfg.LogLocationInfo = log.LogLocationNull
+	}
+	if cfg.LogLocationDebug == "" {
+		cfg.LogLocationDebug = log.LogLocationNull
+	}
+	if cfg.LogLocationEvent == "" {
+		cfg.LogLocationEvent = log.LogLocationNull
+	}
 
 	var err error
 	if cfg.TOURL, err = url.Parse(cfg.TOURLStr); err != nil {

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/monitoring.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/monitoring.go b/traffic_ops/traffic_ops_golang/monitoring.go
index 4c4505f..62d356c 100644
--- a/traffic_ops/traffic_ops_golang/monitoring.go
+++ b/traffic_ops/traffic_ops_golang/monitoring.go
@@ -26,7 +26,8 @@ import (
 	"github.com/lib/pq"
 	"net/http"
 	"strings"
-	"time"
+
+	"github.com/apache/incubator-trafficcontrol/traffic_monitor_golang/common/log"
 )
 
 const MonitoringPrivLevel = 10
@@ -106,7 +107,7 @@ type DeliveryService struct {
 func monitoringHandler(db *sql.DB) RegexHandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request, p ParamMap) {
 		handleErr := func(err error, status int) {
-			fmt.Printf("%v %v error %v\n", time.Now(), r.RemoteAddr, err)
+			log.Errorf("%v %v\n", r.RemoteAddr, err)
 			w.WriteHeader(status)
 			fmt.Fprintf(w, http.StatusText(status))
 		}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/monitoring_test.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/monitoring_test.go b/traffic_ops/traffic_ops_golang/monitoring_test.go
new file mode 100644
index 0000000..f6958e3
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/monitoring_test.go
@@ -0,0 +1,634 @@
+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 (
+	"reflect"
+	"sort"
+	"testing"
+
+	"github.com/lib/pq"
+	"gopkg.in/DATA-DOG/go-sqlmock.v1"
+)
+
+func TestGetServers(t *testing.T) {
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	cdn := "mycdn"
+
+	monitor := Monitor{
+		BasicServer: BasicServer{Profile: "monitorProfile",
+			Status:     "monitorStatus",
+			IP:         "1.2.3.4",
+			IP6:        "2001::4",
+			Port:       8081,
+			Cachegroup: "monitorCachegroup",
+			HostName:   "monitorHost",
+			FQDN:       "monitorFqdn.me",
+		},
+	}
+
+	cacheType := "EDGE"
+	cache := Cache{
+		BasicServer: BasicServer{
+			Profile:    "cacheProfile",
+			Status:     "cacheStatus",
+			IP:         "1.2.3.4",
+			IP6:        "2001::4",
+			Port:       8081,
+			Cachegroup: "cacheCachegroup",
+			HostName:   "cacheHost",
+			FQDN:       "cacheFqdn.me",
+		},
+		InterfaceName: "cacheInterface",
+		Type:          cacheType,
+		HashID:        "cacheHash",
+	}
+
+	router := Router{
+		Type:    RouterType,
+		Profile: "routerProfile",
+	}
+
+	rows := sqlmock.NewRows([]string{"hostName", "fqdn", "status", "cachegroup", "port", "ip", "ip6", "profile", "interfaceName", "type", "hashId"})
+	rows = rows.AddRow(monitor.HostName, monitor.FQDN, monitor.Status, monitor.Cachegroup, monitor.Port, monitor.IP, monitor.IP6, monitor.Profile, "noInterface", MonitorType, "noHash")
+	rows = rows.AddRow(cache.HostName, cache.FQDN, cache.Status, cache.Cachegroup, cache.Port, cache.IP, cache.IP6, cache.Profile, cache.InterfaceName, cache.Type, cache.HashID)
+	rows = rows.AddRow("noHostname", "noFqdn", "noStatus", "noGroup", 0, "noIp", "noIp6", router.Profile, "noInterface", RouterType, "noHashid")
+
+	mock.ExpectQuery("SELECT").WithArgs(cdn).WillReturnRows(rows)
+
+	monitors, caches, routers, err := getServers(db, cdn)
+	if err != nil {
+		t.Errorf("getServers expected: nil error, actual: %v", err)
+	}
+
+	if len(monitors) != 1 {
+		t.Errorf("getServers expected: len(monitors) == 1, actual: %v", len(monitors))
+	}
+	sqlMonitor := monitors[0]
+	if sqlMonitor != monitor {
+		t.Errorf("getServers expected: monitor == %+v, actual: %+v", monitor, sqlMonitor)
+	}
+
+	if len(caches) != 1 {
+		t.Errorf("getServers expected: len(caches) == 1, actual: %v", len(caches))
+	}
+	sqlCache := caches[0]
+	if sqlCache != cache {
+		t.Errorf("getServers expected: cache == %+v, actual: %+v", cache, sqlCache)
+	}
+
+	if len(routers) != 1 {
+		t.Errorf("getServers expected: len(routers) == 1, actual: %v", len(routers))
+	}
+	sqlRouter := routers[0]
+	if sqlRouter != router {
+		t.Errorf("getServers expected: router == %+v, actual: %+v", router, sqlRouter)
+	}
+
+	if err := mock.ExpectationsWereMet(); err != nil {
+		t.Errorf("there were unfulfilled expections: %s", err)
+	}
+}
+
+func TestGetCachegroups(t *testing.T) {
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	cdn := "mycdn"
+
+	cachegroup := Cachegroup{
+		Name: "myCachegroup",
+		Coordinates: Coordinates{
+			Latitude:  42.42,
+			Longitude: 24.24,
+		},
+	}
+
+	rows := sqlmock.NewRows([]string{"name", "latitude", "longitude"})
+	rows = rows.AddRow(cachegroup.Name, cachegroup.Coordinates.Latitude, cachegroup.Coordinates.Longitude)
+
+	mock.ExpectQuery("SELECT").WithArgs(cdn).WillReturnRows(rows)
+
+	sqlCachegroups, err := getCachegroups(db, cdn)
+	if err != nil {
+		t.Errorf("getCachegroups expected: nil error, actual: %v", err)
+	}
+
+	if len(sqlCachegroups) != 1 {
+		t.Errorf("getCachegroups expected: len(monitors) == 1, actual: %v", len(sqlCachegroups))
+	}
+	sqlCachegroup := sqlCachegroups[0]
+	if sqlCachegroup != cachegroup {
+		t.Errorf("getServers expected: cachegroup == %+v, actual: %+v", cachegroup, sqlCachegroup)
+	}
+
+	if err := mock.ExpectationsWereMet(); err != nil {
+		t.Errorf("there were unfulfilled expections: %s", err)
+	}
+}
+
+type SortableProfiles []Profile
+
+func (s SortableProfiles) Len() int {
+	return len(s)
+}
+func (s SortableProfiles) Swap(i, j int) {
+	s[i], s[j] = s[j], s[i]
+}
+func (s SortableProfiles) Less(i, j int) bool {
+	return s[i].Name < s[j].Name
+}
+
+func sortProfiles(p []Profile) []Profile {
+	sort.Sort(SortableProfiles(p))
+	return p
+}
+
+type SortableMonitors []Monitor
+
+func (s SortableMonitors) Len() int {
+	return len(s)
+}
+func (s SortableMonitors) Swap(i, j int) {
+	s[i], s[j] = s[j], s[i]
+}
+func (s SortableMonitors) Less(i, j int) bool {
+	return s[i].HostName < s[j].HostName
+}
+
+func sortMonitors(p []Monitor) []Monitor {
+	sort.Sort(SortableMonitors(p))
+	return p
+}
+
+type SortableCaches []Cache
+
+func (s SortableCaches) Len() int {
+	return len(s)
+}
+func (s SortableCaches) Swap(i, j int) {
+	s[i], s[j] = s[j], s[i]
+}
+func (s SortableCaches) Less(i, j int) bool {
+	return s[i].HostName < s[j].HostName
+}
+
+func sortCaches(p []Cache) []Cache {
+	sort.Sort(SortableCaches(p))
+	return p
+}
+
+type SortableCachegroups []Cachegroup
+
+func (s SortableCachegroups) Len() int {
+	return len(s)
+}
+func (s SortableCachegroups) Swap(i, j int) {
+	s[i], s[j] = s[j], s[i]
+}
+func (s SortableCachegroups) Less(i, j int) bool {
+	return s[i].Name < s[j].Name
+}
+
+func sortCachegroups(p []Cachegroup) []Cachegroup {
+	sort.Sort(SortableCachegroups(p))
+	return p
+}
+
+type SortableDeliveryServices []DeliveryService
+
+func (s SortableDeliveryServices) Len() int {
+	return len(s)
+}
+func (s SortableDeliveryServices) Swap(i, j int) {
+	s[i], s[j] = s[j], s[i]
+}
+func (s SortableDeliveryServices) Less(i, j int) bool {
+	return s[i].XMLID < s[j].XMLID
+}
+
+func sortDeliveryServices(p []DeliveryService) []DeliveryService {
+	sort.Sort(SortableDeliveryServices(p))
+	return p
+}
+
+func TestGetProfiles(t *testing.T) {
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	cache := Cache{
+		BasicServer: BasicServer{
+			Profile:    "cacheProfile",
+			Status:     "cacheStatus",
+			IP:         "1.2.3.4",
+			IP6:        "2001::4",
+			Port:       8081,
+			Cachegroup: "cacheCachegroup",
+			HostName:   "cacheHost",
+			FQDN:       "cacheFqdn.me",
+		},
+		InterfaceName: "cacheInterface",
+		Type:          "EDGE",
+		HashID:        "cacheHash",
+	}
+
+	router := Router{
+		Type:    RouterType,
+		Profile: "routerProfile",
+	}
+
+	profiles := []Profile{
+		Profile{
+			Name: router.Profile,
+			Type: RouterType,
+			Parameters: map[string]string{
+				"param0": "param0Val",
+				"param1": "param1Val",
+			},
+		},
+		Profile{
+			Name: cache.Profile,
+			Type: "myType2",
+			Parameters: map[string]string{
+				"2param0": "2param0Val",
+				"2param1": "2param1Val",
+			},
+		},
+	}
+
+	rows := sqlmock.NewRows([]string{"profile", "name", "value"})
+	for _, profile := range profiles {
+		for paramName, paramVal := range profile.Parameters {
+			rows = rows.AddRow(profile.Name, paramName, paramVal)
+		}
+	}
+
+	caches := []Cache{cache}
+	routers := []Router{router}
+
+	profileNames := []string{"cacheProfile"}
+
+	mock.ExpectQuery("SELECT").WithArgs(pq.Array(profileNames), CacheMonitorConfigFile).WillReturnRows(rows)
+
+	sqlProfiles, err := getProfiles(db, caches, routers)
+	if err != nil {
+		t.Errorf("getProfiles expected: nil error, actual: %v", err)
+	}
+
+	if len(sqlProfiles) != len(profiles) {
+		t.Errorf("getProfiles expected: %+v actual: %+v", profiles, sqlProfiles)
+	}
+
+	profiles = sortProfiles(profiles)
+	sqlProfiles = sortProfiles(sqlProfiles)
+
+	for i, profile := range profiles {
+		if profile.Name != sqlProfiles[i].Name {
+			t.Errorf("getProfiles expected: profiles[%v].Name %v, actual: %v", i, profile.Name, sqlProfiles[i].Name)
+		}
+		for paramName, paramVal := range profile.Parameters {
+			val, ok := sqlProfiles[i].Parameters[paramName]
+			if !ok {
+				t.Errorf("getProfiles expected: profiles[%v].Parameters[%v] = %v, actual: nil", i, paramName, paramVal)
+			}
+			if val != paramVal {
+				t.Errorf("getProfiles expected: profiles[%v].Parameters[%v] = %v, actual: %v", i, paramName, paramVal)
+			}
+		}
+	}
+
+	if err := mock.ExpectationsWereMet(); err != nil {
+		t.Errorf("there were unfulfilled expections: %s", err)
+	}
+}
+
+func TestGetDeliveryServices(t *testing.T) {
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	router := Router{
+		Type:    RouterType,
+		Profile: "routerProfile",
+	}
+
+	deliveryservice := DeliveryService{
+		XMLID:              "myDsid",
+		TotalTPSThreshold:  42.42,
+		Status:             DeliveryServiceStatus,
+		TotalKBPSThreshold: 24.24,
+	}
+
+	deliveryservices := []DeliveryService{deliveryservice}
+	routers := []Router{router}
+
+	rows := sqlmock.NewRows([]string{"xml_id", "global_max_tps", "global_max_mbps"})
+	for _, deliveryservice := range deliveryservices {
+		rows = rows.AddRow(deliveryservice.XMLID, deliveryservice.TotalTPSThreshold, deliveryservice.TotalKBPSThreshold/KilobitsPerMegabit)
+	}
+
+	profileNames := []string{router.Profile}
+
+	mock.ExpectQuery("SELECT").WithArgs(pq.Array(profileNames)).WillReturnRows(rows)
+
+	sqlDeliveryservices, err := getDeliveryServices(db, routers)
+	if err != nil {
+		t.Errorf("getProfiles expected: nil error, actual: %v", err)
+	}
+
+	if len(deliveryservices) != len(sqlDeliveryservices) {
+		t.Errorf("getProfiles expected: %+v actual: %+v", deliveryservices, sqlDeliveryservices)
+	}
+
+	for i, sqlDeliveryservice := range sqlDeliveryservices {
+		deliveryservice := deliveryservices[i]
+		if deliveryservice != sqlDeliveryservice {
+			t.Errorf("getDeliveryServices expected: %v, actual: %v", deliveryservice, sqlDeliveryservice)
+		}
+	}
+
+	if err := mock.ExpectationsWereMet(); err != nil {
+		t.Errorf("there were unfulfilled expections: %s", err)
+	}
+}
+
+func TestGetConfig(t *testing.T) {
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	config := map[string]string{
+		"name0": "val0",
+		"name1": "val1",
+	}
+
+	rows := sqlmock.NewRows([]string{"name", "value"})
+	for name, val := range config {
+		rows = rows.AddRow(name, val)
+	}
+
+	mock.ExpectQuery("SELECT").WillReturnRows(rows)
+
+	sqlConfig, err := getConfig(db)
+	if err != nil {
+		t.Errorf("getProfiles expected: nil error, actual: %v", err)
+	}
+
+	if !reflect.DeepEqual(config, sqlConfig) {
+		t.Errorf("getConfig expected: %+v actual: %+v", config, sqlConfig)
+	}
+
+	if err := mock.ExpectationsWereMet(); err != nil {
+		t.Errorf("there were unfulfilled expections: %s", err)
+	}
+}
+
+func TestGetMonitoringJson(t *testing.T) {
+	resp := MonitoringResponse{}
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	cdn := "mycdn"
+
+	{
+		//
+		// getServers
+		//
+		monitor := Monitor{
+			BasicServer: BasicServer{Profile: "monitorProfile",
+				Status:     "monitorStatus",
+				IP:         "1.2.3.4",
+				IP6:        "2001::4",
+				Port:       8081,
+				Cachegroup: "monitorCachegroup",
+				HostName:   "monitorHost",
+				FQDN:       "monitorFqdn.me",
+			},
+		}
+
+		cacheType := "EDGE"
+		cache := Cache{
+			BasicServer: BasicServer{
+				Profile:    "cacheProfile",
+				Status:     "cacheStatus",
+				IP:         "1.2.3.4",
+				IP6:        "2001::4",
+				Port:       8081,
+				Cachegroup: "cacheCachegroup",
+				HostName:   "cacheHost",
+				FQDN:       "cacheFqdn.me",
+			},
+			InterfaceName: "cacheInterface",
+			Type:          cacheType,
+			HashID:        "cacheHash",
+		}
+
+		router := Router{
+			Type:    RouterType,
+			Profile: "routerProfile",
+		}
+
+		rows := sqlmock.NewRows([]string{"hostName", "fqdn", "status", "cachegroup", "port", "ip", "ip6", "profile", "interfaceName", "type", "hashId"})
+		rows = rows.AddRow(monitor.HostName, monitor.FQDN, monitor.Status, monitor.Cachegroup, monitor.Port, monitor.IP, monitor.IP6, monitor.Profile, "noInterface", MonitorType, "noHash")
+		rows = rows.AddRow(cache.HostName, cache.FQDN, cache.Status, cache.Cachegroup, cache.Port, cache.IP, cache.IP6, cache.Profile, cache.InterfaceName, cache.Type, cache.HashID)
+		rows = rows.AddRow("noHostname", "noFqdn", "noStatus", "noGroup", 0, "noIp", "noIp6", router.Profile, "noInterface", RouterType, "noHashid")
+
+		mock.ExpectQuery("SELECT").WithArgs(cdn).WillReturnRows(rows)
+		resp.Response.TrafficServers = []Cache{cache}
+		resp.Response.TrafficMonitors = []Monitor{monitor}
+	}
+	{
+		//
+		// getCachegroups
+		//
+		cachegroup := Cachegroup{
+			Name: "myCachegroup",
+			Coordinates: Coordinates{
+				Latitude:  42.42,
+				Longitude: 24.24,
+			},
+		}
+
+		rows := sqlmock.NewRows([]string{"name", "latitude", "longitude"})
+		rows = rows.AddRow(cachegroup.Name, cachegroup.Coordinates.Latitude, cachegroup.Coordinates.Longitude)
+
+		mock.ExpectQuery("SELECT").WithArgs(cdn).WillReturnRows(rows)
+		resp.Response.Cachegroups = []Cachegroup{cachegroup}
+	}
+	{
+		//
+		// getProfiles
+		//
+		cache := Cache{
+			BasicServer: BasicServer{
+				Profile:    "cacheProfile",
+				Status:     "cacheStatus",
+				IP:         "1.2.3.4",
+				IP6:        "2001::4",
+				Port:       8081,
+				Cachegroup: "cacheCachegroup",
+				HostName:   "cacheHost",
+				FQDN:       "cacheFqdn.me",
+			},
+			InterfaceName: "cacheInterface",
+			Type:          "EDGE",
+			HashID:        "cacheHash",
+		}
+
+		router := Router{
+			Type:    RouterType,
+			Profile: "routerProfile",
+		}
+
+		profiles := []Profile{
+			Profile{
+				Name: router.Profile,
+				Type: RouterType,
+				Parameters: map[string]string{
+					"param0": "param0Val",
+					"param1": "param1Val",
+				},
+			},
+			Profile{
+				Name: cache.Profile,
+				Type: "EDGE",
+				Parameters: map[string]string{
+					"2param0": "2param0Val",
+					"2param1": "2param1Val",
+				},
+			},
+		}
+
+		rows := sqlmock.NewRows([]string{"profile", "name", "value"})
+		for _, profile := range profiles {
+			for paramName, paramVal := range profile.Parameters {
+				rows = rows.AddRow(profile.Name, paramName, paramVal)
+			}
+		}
+
+		// caches := []Cache{cache}
+		// routers := []Router{router}
+
+		profileNames := []string{"cacheProfile"}
+
+		mock.ExpectQuery("SELECT").WithArgs(pq.Array(profileNames), CacheMonitorConfigFile).WillReturnRows(rows)
+		resp.Response.Profiles = profiles
+	}
+	{
+		//
+		// getDeliveryServices
+		//
+		router := Router{
+			Type:    RouterType,
+			Profile: "routerProfile",
+		}
+
+		deliveryservice := DeliveryService{
+			XMLID:              "myDsid",
+			TotalTPSThreshold:  42.42,
+			Status:             DeliveryServiceStatus,
+			TotalKBPSThreshold: 24.24,
+		}
+
+		deliveryservices := []DeliveryService{deliveryservice}
+		// routers := []Router{router}
+
+		rows := sqlmock.NewRows([]string{"xml_id", "global_max_tps", "global_max_mbps"})
+		for _, deliveryservice := range deliveryservices {
+			rows = rows.AddRow(deliveryservice.XMLID, deliveryservice.TotalTPSThreshold, deliveryservice.TotalKBPSThreshold/KilobitsPerMegabit)
+		}
+
+		profileNames := []string{router.Profile}
+
+		mock.ExpectQuery("SELECT").WithArgs(pq.Array(profileNames)).WillReturnRows(rows)
+		resp.Response.DeliveryServices = deliveryservices
+	}
+	{
+		//
+		// getConfig
+		//
+		config := map[string]string{
+			"name0": "val0",
+			"name1": "val1",
+		}
+
+		rows := sqlmock.NewRows([]string{"name", "value"})
+		for name, val := range config {
+			rows = rows.AddRow(name, val)
+		}
+
+		mock.ExpectQuery("SELECT").WillReturnRows(rows)
+		resp.Response.Config = config
+	}
+
+	sqlResp, err := getMonitoringJson(cdn, db)
+	if err != nil {
+		t.Errorf("getMonitoringJson expected: nil error, actual: %v", err)
+	}
+
+	resp.Response.TrafficServers = sortCaches(resp.Response.TrafficServers)
+	sqlResp.Response.TrafficServers = sortCaches(sqlResp.Response.TrafficServers)
+	resp.Response.TrafficMonitors = sortMonitors(resp.Response.TrafficMonitors)
+	sqlResp.Response.TrafficMonitors = sortMonitors(sqlResp.Response.TrafficMonitors)
+	resp.Response.Cachegroups = sortCachegroups(resp.Response.Cachegroups)
+	sqlResp.Response.Cachegroups = sortCachegroups(sqlResp.Response.Cachegroups)
+	resp.Response.Profiles = sortProfiles(resp.Response.Profiles)
+	sqlResp.Response.Profiles = sortProfiles(sqlResp.Response.Profiles)
+	resp.Response.DeliveryServices = sortDeliveryServices(resp.Response.DeliveryServices)
+	sqlResp.Response.DeliveryServices = sortDeliveryServices(sqlResp.Response.DeliveryServices)
+
+	if !reflect.DeepEqual(sqlResp.Response.TrafficServers, resp.Response.TrafficServers) {
+		t.Errorf("getMonitoringJson expected TrafficServers: %+v actual: %+v", resp.Response.TrafficServers, sqlResp.Response.TrafficServers)
+	}
+	if !reflect.DeepEqual(sqlResp.Response.TrafficMonitors, resp.Response.TrafficMonitors) {
+		t.Errorf("getMonitoringJson expected TrafficMonitors: %+v actual: %+v", resp.Response.TrafficMonitors, sqlResp.Response.TrafficMonitors)
+	}
+	if !reflect.DeepEqual(sqlResp.Response.Cachegroups, resp.Response.Cachegroups) {
+		t.Errorf("getMonitoringJson expected Cachegroups: %+v actual: %+v", resp.Response.Cachegroups, sqlResp.Response.Cachegroups)
+	}
+	if !reflect.DeepEqual(sqlResp.Response.Profiles, resp.Response.Profiles) {
+		t.Errorf("getMonitoringJson expected Profiles: %+v actual: %+v", resp.Response.Profiles, sqlResp.Response.Profiles)
+	}
+	if !reflect.DeepEqual(sqlResp.Response.DeliveryServices, resp.Response.DeliveryServices) {
+		t.Errorf("getMonitoringJson expected DeliveryServices: %+v actual: %+v", resp.Response.DeliveryServices, sqlResp.Response.DeliveryServices)
+	}
+	if !reflect.DeepEqual(sqlResp.Response.Config, resp.Response.Config) {
+		t.Errorf("getMonitoringJson expected Config: %+v actual: %+v", resp.Response.Config, sqlResp.Response.Config)
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/routes.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/routes.go b/traffic_ops/traffic_ops_golang/routes.go
index 5c71dfa..d7061b4 100644
--- a/traffic_ops/traffic_ops_golang/routes.go
+++ b/traffic_ops/traffic_ops_golang/routes.go
@@ -57,7 +57,7 @@ func GetRoutes(d ServerData) (map[string]RegexHandlerFunc, http.Handler, error)
 	}
 
 	return map[string]RegexHandlerFunc{
-		"api/1.2/cdns/{cdn}/configs/monitoring.json": wrapLogTime(wrapAuth(monitoringHandler(d.DB), d.NoAuth, d.TOSecret, privLevelStmt, MonitoringPrivLevel)),
+		"api/1.2/cdns/{cdn}/configs/monitoring.json": wrapAuth(monitoringHandler(d.DB), d.NoAuth, d.TOSecret, privLevelStmt, MonitoringPrivLevel),
 	}, getRootHandler(d), nil
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/traffic_ops_golang.go b/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
index 173b9b5..2689e7c 100644
--- a/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
+++ b/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
@@ -23,8 +23,11 @@ import (
 	"database/sql"
 	"flag"
 	"fmt"
-	_ "github.com/lib/pq"
 	"net/http"
+
+	"github.com/apache/incubator-trafficcontrol/traffic_monitor_golang/common/log"
+
+	_ "github.com/lib/pq"
 )
 
 const DefaultConfigPath = "/etc/goto/config.json"
@@ -42,6 +45,11 @@ func main() {
 		return
 	}
 
+	if err := log.InitCfg(cfg); err != nil {
+		fmt.Println("Error initializing loggers: %v", err)
+		return
+	}
+
 	sslStr := "require"
 	if !cfg.DBSSL {
 		sslStr = "disable"
@@ -49,19 +57,19 @@ func main() {
 
 	db, err := sql.Open("postgres", fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=%s", cfg.DBUser, cfg.DBPass, cfg.DBServer, cfg.DBDB, sslStr))
 	if err != nil {
-		fmt.Printf("Error opening database: %v\n", err)
+		log.Errorf("opening database: %v\n", err)
 		return
 	}
 	defer db.Close()
 
 	if err := RegisterRoutes(ServerData{DB: db, Config: cfg}); err != nil {
-		fmt.Printf("Error registering routes: %v\n", err)
+		log.Errorf("registering routes: %v\n", err)
 		return
 	}
 
-	fmt.Println("Listening on " + cfg.HTTPPort)
+	log.Infof("Listening on " + cfg.HTTPPort)
 	if err := http.ListenAndServeTLS(":"+cfg.HTTPPort, cfg.CertPath, cfg.KeyPath, nil); err != nil {
-		fmt.Printf("Error stopping server: %v\n", err)
+		log.Errorf("stopping server: %v\n", err)
 		return
 	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/LICENSE
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/LICENSE b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/LICENSE
new file mode 100644
index 0000000..7f8bedf
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/LICENSE
@@ -0,0 +1,28 @@
+The three clause BSD license (http://en.wikipedia.org/wiki/BSD_licenses)
+
+Copyright (c) 2013-2017, DATA-DOG team
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* The name DataDog.lt may not be used to endorse or promote products
+  derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL MICHAEL BOSTOCK BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/README.md
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/README.md b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/README.md
new file mode 100644
index 0000000..f3d5487
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/README.md
@@ -0,0 +1,223 @@
+[![Build Status](https://travis-ci.org/DATA-DOG/go-sqlmock.png)](https://travis-ci.org/DATA-DOG/go-sqlmock)
+[![GoDoc](https://godoc.org/github.com/DATA-DOG/go-sqlmock?status.png)](https://godoc.org/github.com/DATA-DOG/go-sqlmock)
+[![codecov.io](https://codecov.io/github/DATA-DOG/go-sqlmock/branch/master/graph/badge.svg)](https://codecov.io/github/DATA-DOG/go-sqlmock)
+
+# Sql driver mock for Golang
+
+**sqlmock** is a mock library implementing [sql/driver](https://godoc.org/database/sql/driver). Which has one and only
+purpose - to simulate any **sql** driver behavior in tests, without needing a real database connection. It helps to
+maintain correct **TDD** workflow.
+
+- this library is now complete and stable. (you may not find new changes for this reason)
+- supports concurrency and multiple connections.
+- supports **go1.8** Context related feature mocking and Named sql parameters.
+- does not require any modifications to your source code.
+- the driver allows to mock any sql driver method behavior.
+- has strict by default expectation order matching.
+- has no third party dependencies.
+
+## Install
+
+    go get gopkg.in/DATA-DOG/go-sqlmock.v1
+
+## Documentation and Examples
+
+Visit [godoc](http://godoc.org/github.com/DATA-DOG/go-sqlmock) for general examples and public api reference.
+See **.travis.yml** for supported **go** versions.
+Different use case, is to functionally test with a real database - [go-txdb](https://github.com/DATA-DOG/go-txdb)
+all database related actions are isolated within a single transaction so the database can remain in the same state.
+
+See implementation examples:
+
+- [blog API server](https://github.com/DATA-DOG/go-sqlmock/tree/master/examples/blog)
+- [the same orders example](https://github.com/DATA-DOG/go-sqlmock/tree/master/examples/orders)
+
+### Something you may want to test
+
+``` go
+package main
+
+import "database/sql"
+
+func recordStats(db *sql.DB, userID, productID int64) (err error) {
+	tx, err := db.Begin()
+	if err != nil {
+		return
+	}
+
+	defer func() {
+		switch err {
+		case nil:
+			err = tx.Commit()
+		default:
+			tx.Rollback()
+		}
+	}()
+
+	if _, err = tx.Exec("UPDATE products SET views = views + 1"); err != nil {
+		return
+	}
+	if _, err = tx.Exec("INSERT INTO product_viewers (user_id, product_id) VALUES (?, ?)", userID, productID); err != nil {
+		return
+	}
+	return
+}
+
+func main() {
+	// @NOTE: the real connection is not required for tests
+	db, err := sql.Open("mysql", "root@/blog")
+	if err != nil {
+		panic(err)
+	}
+	defer db.Close()
+
+	if err = recordStats(db, 1 /*some user id*/, 5 /*some product id*/); err != nil {
+		panic(err)
+	}
+}
+```
+
+### Tests with sqlmock
+
+``` go
+package main
+
+import (
+	"fmt"
+	"testing"
+
+	"gopkg.in/DATA-DOG/go-sqlmock.v1"
+)
+
+// a successful case
+func TestShouldUpdateStats(t *testing.T) {
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	mock.ExpectBegin()
+	mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1))
+	mock.ExpectExec("INSERT INTO product_viewers").WithArgs(2, 3).WillReturnResult(sqlmock.NewResult(1, 1))
+	mock.ExpectCommit()
+
+	// now we execute our method
+	if err = recordStats(db, 2, 3); err != nil {
+		t.Errorf("error was not expected while updating stats: %s", err)
+	}
+
+	// we make sure that all expectations were met
+	if err := mock.ExpectationsWereMet(); err != nil {
+		t.Errorf("there were unfulfilled expections: %s", err)
+	}
+}
+
+// a failing test case
+func TestShouldRollbackStatUpdatesOnFailure(t *testing.T) {
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	mock.ExpectBegin()
+	mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1))
+	mock.ExpectExec("INSERT INTO product_viewers").
+		WithArgs(2, 3).
+		WillReturnError(fmt.Errorf("some error"))
+	mock.ExpectRollback()
+
+	// now we execute our method
+	if err = recordStats(db, 2, 3); err == nil {
+		t.Errorf("was expecting an error, but there was none")
+	}
+
+	// we make sure that all expectations were met
+	if err := mock.ExpectationsWereMet(); err != nil {
+		t.Errorf("there were unfulfilled expections: %s", err)
+	}
+}
+```
+
+## Matching arguments like time.Time
+
+There may be arguments which are of `struct` type and cannot be compared easily by value like `time.Time`. In this case
+**sqlmock** provides an [Argument](https://godoc.org/github.com/DATA-DOG/go-sqlmock#Argument) interface which
+can be used in more sophisticated matching. Here is a simple example of time argument matching:
+
+``` go
+type AnyTime struct{}
+
+// Match satisfies sqlmock.Argument interface
+func (a AnyTime) Match(v driver.Value) bool {
+	_, ok := v.(time.Time)
+	return ok
+}
+
+func TestAnyTimeArgument(t *testing.T) {
+	t.Parallel()
+	db, mock, err := New()
+	if err != nil {
+		t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	mock.ExpectExec("INSERT INTO users").
+		WithArgs("john", AnyTime{}).
+		WillReturnResult(NewResult(1, 1))
+
+	_, err = db.Exec("INSERT INTO users(name, created_at) VALUES (?, ?)", "john", time.Now())
+	if err != nil {
+		t.Errorf("error '%s' was not expected, while inserting a row", err)
+	}
+
+	if err := mock.ExpectationsWereMet(); err != nil {
+		t.Errorf("there were unfulfilled expections: %s", err)
+	}
+}
+```
+
+It only asserts that argument is of `time.Time` type.
+
+## Run tests
+
+    go test -race
+
+## Change Log
+
+- **2017-02-09** - implemented support for **go1.8** features. **Rows** interface was changed to struct
+  but contains all methods as before and should maintain backwards compatibility. **ExpectedQuery.WillReturnRows** may now
+  accept multiple row sets.
+- **2016-11-02** - `db.Prepare()` was not validating expected prepare SQL
+  query. It should still be validated even if Exec or Query is not
+  executed on that prepared statement.
+- **2016-02-23** - added **sqlmock.AnyArg()** function to provide any kind
+  of argument matcher.
+- **2016-02-23** - convert expected arguments to driver.Value as natural
+  driver does, the change may affect time.Time comparison and will be
+  stricter. See [issue](https://github.com/DATA-DOG/go-sqlmock/issues/31).
+- **2015-08-27** - **v1** api change, concurrency support, all known issues fixed.
+- **2014-08-16** instead of **panic** during reflect type mismatch when comparing query arguments - now return error
+- **2014-08-14** added **sqlmock.NewErrorResult** which gives an option to return driver.Result with errors for
+interface methods, see [issue](https://github.com/DATA-DOG/go-sqlmock/issues/5)
+- **2014-05-29** allow to match arguments in more sophisticated ways, by providing an **sqlmock.Argument** interface
+- **2014-04-21** introduce **sqlmock.New()** to open a mock database connection for tests. This method
+calls sql.DB.Ping to ensure that connection is open, see [issue](https://github.com/DATA-DOG/go-sqlmock/issues/4).
+This way on Close it will surely assert if all expectations are met, even if database was not triggered at all.
+The old way is still available, but it is advisable to call db.Ping manually before asserting with db.Close.
+- **2014-02-14** RowsFromCSVString is now a part of Rows interface named as FromCSVString.
+It has changed to allow more ways to construct rows and to easily extend this API in future.
+See [issue 1](https://github.com/DATA-DOG/go-sqlmock/issues/1)
+**RowsFromCSVString** is deprecated and will be removed in future
+
+## Contributions
+
+Feel free to open a pull request. Note, if you wish to contribute an extension to public (exported methods or types) -
+please open an issue before, to discuss whether these changes can be accepted. All backward incompatible changes are
+and will be treated cautiously
+
+## License
+
+The [three clause BSD license](http://en.wikipedia.org/wiki/BSD_licenses)
+

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/argument.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/argument.go b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/argument.go
new file mode 100644
index 0000000..7727481
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/argument.go
@@ -0,0 +1,24 @@
+package sqlmock
+
+import "database/sql/driver"
+
+// Argument interface allows to match
+// any argument in specific way when used with
+// ExpectedQuery and ExpectedExec expectations.
+type Argument interface {
+	Match(driver.Value) bool
+}
+
+// AnyArg will return an Argument which can
+// match any kind of arguments.
+//
+// Useful for time.Time or similar kinds of arguments.
+func AnyArg() Argument {
+	return anyArgument{}
+}
+
+type anyArgument struct{}
+
+func (a anyArgument) Match(_ driver.Value) bool {
+	return true
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/argument_test.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/argument_test.go b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/argument_test.go
new file mode 100644
index 0000000..3088ed4
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/argument_test.go
@@ -0,0 +1,58 @@
+package sqlmock
+
+import (
+	"database/sql/driver"
+	"testing"
+	"time"
+)
+
+type AnyTime struct{}
+
+// Match satisfies sqlmock.Argument interface
+func (a AnyTime) Match(v driver.Value) bool {
+	_, ok := v.(time.Time)
+	return ok
+}
+
+func TestAnyTimeArgument(t *testing.T) {
+	t.Parallel()
+	db, mock, err := New()
+	if err != nil {
+		t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	mock.ExpectExec("INSERT INTO users").
+		WithArgs("john", AnyTime{}).
+		WillReturnResult(NewResult(1, 1))
+
+	_, err = db.Exec("INSERT INTO users(name, created_at) VALUES (?, ?)", "john", time.Now())
+	if err != nil {
+		t.Errorf("error '%s' was not expected, while inserting a row", err)
+	}
+
+	if err := mock.ExpectationsWereMet(); err != nil {
+		t.Errorf("there were unfulfilled expections: %s", err)
+	}
+}
+
+func TestByteSliceArgument(t *testing.T) {
+	t.Parallel()
+	db, mock, err := New()
+	if err != nil {
+		t.Errorf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	username := []byte("user")
+	mock.ExpectExec("INSERT INTO users").WithArgs(username).WillReturnResult(NewResult(1, 1))
+
+	_, err = db.Exec("INSERT INTO users(username) VALUES (?)", username)
+	if err != nil {
+		t.Errorf("error '%s' was not expected, while inserting a row", err)
+	}
+
+	if err := mock.ExpectationsWereMet(); err != nil {
+		t.Errorf("there were unfulfilled expections: %s", err)
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/driver.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/driver.go b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/driver.go
new file mode 100644
index 0000000..2a480fe
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/driver.go
@@ -0,0 +1,78 @@
+package sqlmock
+
+import (
+	"database/sql"
+	"database/sql/driver"
+	"fmt"
+	"sync"
+)
+
+var pool *mockDriver
+
+func init() {
+	pool = &mockDriver{
+		conns: make(map[string]*sqlmock),
+	}
+	sql.Register("sqlmock", pool)
+}
+
+type mockDriver struct {
+	sync.Mutex
+	counter int
+	conns   map[string]*sqlmock
+}
+
+func (d *mockDriver) Open(dsn string) (driver.Conn, error) {
+	d.Lock()
+	defer d.Unlock()
+
+	c, ok := d.conns[dsn]
+	if !ok {
+		return c, fmt.Errorf("expected a connection to be available, but it is not")
+	}
+
+	c.opened++
+	return c, nil
+}
+
+// New creates sqlmock database connection
+// and a mock to manage expectations.
+// Pings db so that all expectations could be
+// asserted.
+func New() (*sql.DB, Sqlmock, error) {
+	pool.Lock()
+	dsn := fmt.Sprintf("sqlmock_db_%d", pool.counter)
+	pool.counter++
+
+	smock := &sqlmock{dsn: dsn, drv: pool, ordered: true}
+	pool.conns[dsn] = smock
+	pool.Unlock()
+
+	return smock.open()
+}
+
+// NewWithDSN creates sqlmock database connection
+// with a specific DSN and a mock to manage expectations.
+// Pings db so that all expectations could be asserted.
+//
+// This method is introduced because of sql abstraction
+// libraries, which do not provide a way to initialize
+// with sql.DB instance. For example GORM library.
+//
+// Note, it will error if attempted to create with an
+// already used dsn
+//
+// It is not recommended to use this method, unless you
+// really need it and there is no other way around.
+func NewWithDSN(dsn string) (*sql.DB, Sqlmock, error) {
+	pool.Lock()
+	if _, ok := pool.conns[dsn]; ok {
+		pool.Unlock()
+		return nil, nil, fmt.Errorf("cannot create a new mock database with the same dsn: %s", dsn)
+	}
+	smock := &sqlmock{dsn: dsn, drv: pool, ordered: true}
+	pool.conns[dsn] = smock
+	pool.Unlock()
+
+	return smock.open()
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/driver_test.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/driver_test.go b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/driver_test.go
new file mode 100644
index 0000000..1a65c1c
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/driver_test.go
@@ -0,0 +1,112 @@
+package sqlmock
+
+import (
+	"fmt"
+	"testing"
+)
+
+type void struct{}
+
+func (void) Print(...interface{}) {}
+
+func ExampleNew() {
+	db, mock, err := New()
+	if err != nil {
+		fmt.Println("expected no error, but got:", err)
+		return
+	}
+	defer db.Close()
+	// now we can expect operations performed on db
+	mock.ExpectBegin().WillReturnError(fmt.Errorf("an error will occur on db.Begin() call"))
+}
+
+func TestShouldOpenConnectionIssue15(t *testing.T) {
+	db, mock, err := New()
+	if err != nil {
+		t.Errorf("expected no error, but got: %s", err)
+	}
+	if len(pool.conns) != 1 {
+		t.Errorf("expected 1 connection in pool, but there is: %d", len(pool.conns))
+	}
+
+	smock, _ := mock.(*sqlmock)
+	if smock.opened != 1 {
+		t.Errorf("expected 1 connection on mock to be opened, but there is: %d", smock.opened)
+	}
+
+	// defer so the rows gets closed first
+	defer func() {
+		if smock.opened != 0 {
+			t.Errorf("expected no connections on mock to be opened, but there is: %d", smock.opened)
+		}
+	}()
+
+	mock.ExpectQuery("SELECT").WillReturnRows(NewRows([]string{"one", "two"}).AddRow("val1", "val2"))
+	rows, err := db.Query("SELECT")
+	if err != nil {
+		t.Errorf("unexpected error: %s", err)
+	}
+	defer rows.Close()
+
+	mock.ExpectExec("UPDATE").WillReturnResult(NewResult(1, 1))
+	if _, err = db.Exec("UPDATE"); err != nil {
+		t.Errorf("unexpected error: %s", err)
+	}
+
+	// now there should be two connections open
+	if smock.opened != 2 {
+		t.Errorf("expected 2 connection on mock to be opened, but there is: %d", smock.opened)
+	}
+
+	mock.ExpectClose()
+	if err = db.Close(); err != nil {
+		t.Errorf("expected no error on close, but got: %s", err)
+	}
+
+	// one is still reserved for rows
+	if smock.opened != 1 {
+		t.Errorf("expected 1 connection on mock to be still reserved for rows, but there is: %d", smock.opened)
+	}
+}
+
+func TestTwoOpenConnectionsOnTheSameDSN(t *testing.T) {
+	db, mock, err := New()
+	if err != nil {
+		t.Errorf("expected no error, but got: %s", err)
+	}
+	db2, mock2, err := New()
+	if err != nil {
+		t.Errorf("expected no error, but got: %s", err)
+	}
+	if len(pool.conns) != 2 {
+		t.Errorf("expected 2 connection in pool, but there is: %d", len(pool.conns))
+	}
+
+	if db == db2 {
+		t.Errorf("expected not the same database instance, but it is the same")
+	}
+	if mock == mock2 {
+		t.Errorf("expected not the same mock instance, but it is the same")
+	}
+}
+
+func TestWrongDSN(t *testing.T) {
+	t.Parallel()
+	db, _, _ := New()
+	defer db.Close()
+	if _, err := db.Driver().Open("wrong_dsn"); err == nil {
+		t.Error("expected error on Open")
+	}
+}
+
+func TestNewDSN(t *testing.T) {
+	if _, _, err := NewWithDSN("sqlmock_db_99"); err != nil {
+		t.Errorf("expected no error on NewWithDSN, but got: %s", err)
+	}
+}
+
+func TestDuplicateNewDSN(t *testing.T) {
+	if _, _, err := NewWithDSN("sqlmock_db_1"); err == nil {
+		t.Error("expected error on NewWithDSN")
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/basic/basic.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/basic/basic.go b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/basic/basic.go
new file mode 100644
index 0000000..0fbf98d
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/basic/basic.go
@@ -0,0 +1,40 @@
+package main
+
+import "database/sql"
+
+func recordStats(db *sql.DB, userID, productID int64) (err error) {
+	tx, err := db.Begin()
+	if err != nil {
+		return
+	}
+
+	defer func() {
+		switch err {
+		case nil:
+			err = tx.Commit()
+		default:
+			tx.Rollback()
+		}
+	}()
+
+	if _, err = tx.Exec("UPDATE products SET views = views + 1"); err != nil {
+		return
+	}
+	if _, err = tx.Exec("INSERT INTO product_viewers (user_id, product_id) VALUES (?, ?)", userID, productID); err != nil {
+		return
+	}
+	return
+}
+
+func main() {
+	// @NOTE: the real connection is not required for tests
+	db, err := sql.Open("mysql", "root@/blog")
+	if err != nil {
+		panic(err)
+	}
+	defer db.Close()
+
+	if err = recordStats(db, 1 /*some user id*/, 5 /*some product id*/); err != nil {
+		panic(err)
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/basic/basic_test.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/basic/basic_test.go b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/basic/basic_test.go
new file mode 100644
index 0000000..0825f90
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/basic/basic_test.go
@@ -0,0 +1,58 @@
+package main
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/DATA-DOG/go-sqlmock"
+)
+
+// a successful case
+func TestShouldUpdateStats(t *testing.T) {
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	mock.ExpectBegin()
+	mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1))
+	mock.ExpectExec("INSERT INTO product_viewers").WithArgs(2, 3).WillReturnResult(sqlmock.NewResult(1, 1))
+	mock.ExpectCommit()
+
+	// now we execute our method
+	if err = recordStats(db, 2, 3); err != nil {
+		t.Errorf("error was not expected while updating stats: %s", err)
+	}
+
+	// we make sure that all expectations were met
+	if err := mock.ExpectationsWereMet(); err != nil {
+		t.Errorf("there were unfulfilled expections: %s", err)
+	}
+}
+
+// a failing test case
+func TestShouldRollbackStatUpdatesOnFailure(t *testing.T) {
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	mock.ExpectBegin()
+	mock.ExpectExec("UPDATE products").WillReturnResult(sqlmock.NewResult(1, 1))
+	mock.ExpectExec("INSERT INTO product_viewers").
+		WithArgs(2, 3).
+		WillReturnError(fmt.Errorf("some error"))
+	mock.ExpectRollback()
+
+	// now we execute our method
+	if err = recordStats(db, 2, 3); err == nil {
+		t.Errorf("was expecting an error, but there was none")
+	}
+
+	// we make sure that all expectations were met
+	if err := mock.ExpectationsWereMet(); err != nil {
+		t.Errorf("there were unfulfilled expections: %s", err)
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/blog/blog.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/blog/blog.go b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/blog/blog.go
new file mode 100644
index 0000000..c4aec06
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/blog/blog.go
@@ -0,0 +1,81 @@
+package main
+
+import (
+	"database/sql"
+	"encoding/json"
+	"net/http"
+)
+
+type api struct {
+	db *sql.DB
+}
+
+type post struct {
+	ID    int
+	Title string
+	Body  string
+}
+
+func (a *api) posts(w http.ResponseWriter, r *http.Request) {
+	rows, err := a.db.Query("SELECT id, title, body FROM posts")
+	if err != nil {
+		a.fail(w, "failed to fetch posts: "+err.Error(), 500)
+		return
+	}
+	defer rows.Close()
+
+	var posts []*post
+	for rows.Next() {
+		p := &post{}
+		if err := rows.Scan(&p.ID, &p.Title, &p.Body); err != nil {
+			a.fail(w, "failed to scan post: "+err.Error(), 500)
+			return
+		}
+		posts = append(posts, p)
+	}
+	if rows.Err() != nil {
+		a.fail(w, "failed to read all posts: "+rows.Err().Error(), 500)
+		return
+	}
+
+	data := struct {
+		Posts []*post
+	}{posts}
+
+	a.ok(w, data)
+}
+
+func main() {
+	// @NOTE: the real connection is not required for tests
+	db, err := sql.Open("mysql", "root@/blog")
+	if err != nil {
+		panic(err)
+	}
+	app := &api{db: db}
+	http.HandleFunc("/posts", app.posts)
+	http.ListenAndServe(":8080", nil)
+}
+
+func (a *api) fail(w http.ResponseWriter, msg string, status int) {
+	w.Header().Set("Content-Type", "application/json")
+
+	data := struct {
+		Error string
+	}{Error: msg}
+
+	resp, _ := json.Marshal(data)
+	w.WriteHeader(status)
+	w.Write(resp)
+}
+
+func (a *api) ok(w http.ResponseWriter, data interface{}) {
+	w.Header().Set("Content-Type", "application/json")
+
+	resp, err := json.Marshal(data)
+	if err != nil {
+		w.WriteHeader(http.StatusInternalServerError)
+		a.fail(w, "oops something evil has happened", 500)
+		return
+	}
+	w.Write(resp)
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/blog/blog_test.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/blog/blog_test.go b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/blog/blog_test.go
new file mode 100644
index 0000000..aa1881a
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/blog/blog_test.go
@@ -0,0 +1,102 @@
+package main
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/DATA-DOG/go-sqlmock"
+)
+
+func (a *api) assertJSON(actual []byte, data interface{}, t *testing.T) {
+	expected, err := json.Marshal(data)
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when marshaling expected json data", err)
+	}
+
+	if bytes.Compare(expected, actual) != 0 {
+		t.Errorf("the expected json: %s is different from actual %s", expected, actual)
+	}
+}
+
+func TestShouldGetPosts(t *testing.T) {
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	// create app with mocked db, request and response to test
+	app := &api{db}
+	req, err := http.NewRequest("GET", "http://localhost/posts", nil)
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected while creating request", err)
+	}
+	w := httptest.NewRecorder()
+
+	// before we actually execute our api function, we need to expect required DB actions
+	rows := sqlmock.NewRows([]string{"id", "title", "body"}).
+		AddRow(1, "post 1", "hello").
+		AddRow(2, "post 2", "world")
+
+	mock.ExpectQuery("^SELECT (.+) FROM posts$").WillReturnRows(rows)
+
+	// now we execute our request
+	app.posts(w, req)
+
+	if w.Code != 200 {
+		t.Fatalf("expected status code to be 200, but got: %d", w.Code)
+	}
+
+	data := struct {
+		Posts []*post
+	}{Posts: []*post{
+		{ID: 1, Title: "post 1", Body: "hello"},
+		{ID: 2, Title: "post 2", Body: "world"},
+	}}
+	app.assertJSON(w.Body.Bytes(), data, t)
+
+	// we make sure that all expectations were met
+	if err := mock.ExpectationsWereMet(); err != nil {
+		t.Errorf("there were unfulfilled expections: %s", err)
+	}
+}
+
+func TestShouldRespondWithErrorOnFailure(t *testing.T) {
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	// create app with mocked db, request and response to test
+	app := &api{db}
+	req, err := http.NewRequest("GET", "http://localhost/posts", nil)
+	if err != nil {
+		t.Fatalf("an error '%s' was not expected while creating request", err)
+	}
+	w := httptest.NewRecorder()
+
+	// before we actually execute our api function, we need to expect required DB actions
+	mock.ExpectQuery("^SELECT (.+) FROM posts$").WillReturnError(fmt.Errorf("some error"))
+
+	// now we execute our request
+	app.posts(w, req)
+
+	if w.Code != 500 {
+		t.Fatalf("expected status code to be 500, but got: %d", w.Code)
+	}
+
+	data := struct {
+		Error string
+	}{"failed to fetch posts: some error"}
+	app.assertJSON(w.Body.Bytes(), data, t)
+
+	// we make sure that all expectations were met
+	if err := mock.ExpectationsWereMet(); err != nil {
+		t.Errorf("there were unfulfilled expections: %s", err)
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/doc.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/doc.go b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/doc.go
new file mode 100644
index 0000000..c7842af
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/doc.go
@@ -0,0 +1 @@
+package examples

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/orders/orders.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/orders/orders.go b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/orders/orders.go
new file mode 100644
index 0000000..fb7e47e
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/orders/orders.go
@@ -0,0 +1,121 @@
+package main
+
+import (
+	"database/sql"
+	"fmt"
+	"log"
+
+	"github.com/kisielk/sqlstruct"
+)
+
+const ORDER_PENDING = 0
+const ORDER_CANCELLED = 1
+
+type User struct {
+	Id       int     `sql:"id"`
+	Username string  `sql:"username"`
+	Balance  float64 `sql:"balance"`
+}
+
+type Order struct {
+	Id          int     `sql:"id"`
+	Value       float64 `sql:"value"`
+	ReservedFee float64 `sql:"reserved_fee"`
+	Status      int     `sql:"status"`
+}
+
+func cancelOrder(id int, db *sql.DB) (err error) {
+	tx, err := db.Begin()
+	if err != nil {
+		return
+	}
+
+	var order Order
+	var user User
+	sql := fmt.Sprintf(`
+SELECT %s, %s
+FROM orders AS o
+INNER JOIN users AS u ON o.buyer_id = u.id
+WHERE o.id = ?
+FOR UPDATE`,
+		sqlstruct.ColumnsAliased(order, "o"),
+		sqlstruct.ColumnsAliased(user, "u"))
+
+	// fetch order to cancel
+	rows, err := tx.Query(sql, id)
+	if err != nil {
+		tx.Rollback()
+		return
+	}
+
+	defer rows.Close()
+	// no rows, nothing to do
+	if !rows.Next() {
+		tx.Rollback()
+		return
+	}
+
+	// read order
+	err = sqlstruct.ScanAliased(&order, rows, "o")
+	if err != nil {
+		tx.Rollback()
+		return
+	}
+
+	// ensure order status
+	if order.Status != ORDER_PENDING {
+		tx.Rollback()
+		return
+	}
+
+	// read user
+	err = sqlstruct.ScanAliased(&user, rows, "u")
+	if err != nil {
+		tx.Rollback()
+		return
+	}
+	rows.Close() // manually close before other prepared statements
+
+	// refund order value
+	sql = "UPDATE users SET balance = balance + ? WHERE id = ?"
+	refundStmt, err := tx.Prepare(sql)
+	if err != nil {
+		tx.Rollback()
+		return
+	}
+	defer refundStmt.Close()
+	_, err = refundStmt.Exec(order.Value+order.ReservedFee, user.Id)
+	if err != nil {
+		tx.Rollback()
+		return
+	}
+
+	// update order status
+	order.Status = ORDER_CANCELLED
+	sql = "UPDATE orders SET status = ?, updated = NOW() WHERE id = ?"
+	orderUpdStmt, err := tx.Prepare(sql)
+	if err != nil {
+		tx.Rollback()
+		return
+	}
+	defer orderUpdStmt.Close()
+	_, err = orderUpdStmt.Exec(order.Status, order.Id)
+	if err != nil {
+		tx.Rollback()
+		return
+	}
+	return tx.Commit()
+}
+
+func main() {
+	// @NOTE: the real connection is not required for tests
+	db, err := sql.Open("mysql", "root:@/orders")
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer db.Close()
+	err = cancelOrder(1, db)
+	if err != nil {
+		log.Fatal(err)
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/orders/orders_test.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/orders/orders_test.go b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/orders/orders_test.go
new file mode 100644
index 0000000..7562b8f
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/examples/orders/orders_test.go
@@ -0,0 +1,108 @@
+package main
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/DATA-DOG/go-sqlmock"
+)
+
+// will test that order with a different status, cannot be cancelled
+func TestShouldNotCancelOrderWithNonPendingStatus(t *testing.T) {
+	// open database stub
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Errorf("An error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	// columns are prefixed with "o" since we used sqlstruct to generate them
+	columns := []string{"o_id", "o_status"}
+	// expect transaction begin
+	mock.ExpectBegin()
+	// expect query to fetch order and user, match it with regexp
+	mock.ExpectQuery("SELECT (.+) FROM orders AS o INNER JOIN users AS u (.+) FOR UPDATE").
+		WithArgs(1).
+		WillReturnRows(sqlmock.NewRows(columns).FromCSVString("1,1"))
+	// expect transaction rollback, since order status is "cancelled"
+	mock.ExpectRollback()
+
+	// run the cancel order function
+	err = cancelOrder(1, db)
+	if err != nil {
+		t.Errorf("Expected no error, but got %s instead", err)
+	}
+	// we make sure that all expectations were met
+	if err := mock.ExpectationsWereMet(); err != nil {
+		t.Errorf("there were unfulfilled expections: %s", err)
+	}
+}
+
+// will test order cancellation
+func TestShouldRefundUserWhenOrderIsCancelled(t *testing.T) {
+	// open database stub
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Errorf("An error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	// columns are prefixed with "o" since we used sqlstruct to generate them
+	columns := []string{"o_id", "o_status", "o_value", "o_reserved_fee", "u_id", "u_balance"}
+	// expect transaction begin
+	mock.ExpectBegin()
+	// expect query to fetch order and user, match it with regexp
+	mock.ExpectQuery("SELECT (.+) FROM orders AS o INNER JOIN users AS u (.+) FOR UPDATE").
+		WithArgs(1).
+		WillReturnRows(sqlmock.NewRows(columns).AddRow(1, 0, 25.75, 3.25, 2, 10.00))
+	// expect user balance update
+	mock.ExpectPrepare("UPDATE users SET balance").ExpectExec().
+		WithArgs(25.75+3.25, 2).                  // refund amount, user id
+		WillReturnResult(sqlmock.NewResult(0, 1)) // no insert id, 1 affected row
+	// expect order status update
+	mock.ExpectPrepare("UPDATE orders SET status").ExpectExec().
+		WithArgs(ORDER_CANCELLED, 1).             // status, id
+		WillReturnResult(sqlmock.NewResult(0, 1)) // no insert id, 1 affected row
+	// expect a transaction commit
+	mock.ExpectCommit()
+
+	// run the cancel order function
+	err = cancelOrder(1, db)
+	if err != nil {
+		t.Errorf("Expected no error, but got %s instead", err)
+	}
+	// we make sure that all expectations were met
+	if err := mock.ExpectationsWereMet(); err != nil {
+		t.Errorf("there were unfulfilled expections: %s", err)
+	}
+}
+
+// will test order cancellation
+func TestShouldRollbackOnError(t *testing.T) {
+	// open database stub
+	db, mock, err := sqlmock.New()
+	if err != nil {
+		t.Errorf("An error '%s' was not expected when opening a stub database connection", err)
+	}
+	defer db.Close()
+
+	// expect transaction begin
+	mock.ExpectBegin()
+	// expect query to fetch order and user, match it with regexp
+	mock.ExpectQuery("SELECT (.+) FROM orders AS o INNER JOIN users AS u (.+) FOR UPDATE").
+		WithArgs(1).
+		WillReturnError(fmt.Errorf("Some error"))
+	// should rollback since error was returned from query execution
+	mock.ExpectRollback()
+
+	// run the cancel order function
+	err = cancelOrder(1, db)
+	// error should return back
+	if err == nil {
+		t.Error("Expected error, but got none")
+	}
+	// we make sure that all expectations were met
+	if err := mock.ExpectationsWereMet(); err != nil {
+		t.Errorf("there were unfulfilled expections: %s", err)
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/expectations.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/expectations.go b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/expectations.go
new file mode 100644
index 0000000..415759e
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/expectations.go
@@ -0,0 +1,344 @@
+package sqlmock
+
+import (
+	"database/sql/driver"
+	"fmt"
+	"regexp"
+	"strings"
+	"sync"
+	"time"
+)
+
+// an expectation interface
+type expectation interface {
+	fulfilled() bool
+	Lock()
+	Unlock()
+	String() string
+}
+
+// common expectation struct
+// satisfies the expectation interface
+type commonExpectation struct {
+	sync.Mutex
+	triggered bool
+	err       error
+}
+
+func (e *commonExpectation) fulfilled() bool {
+	return e.triggered
+}
+
+// ExpectedClose is used to manage *sql.DB.Close expectation
+// returned by *Sqlmock.ExpectClose.
+type ExpectedClose struct {
+	commonExpectation
+}
+
+// WillReturnError allows to set an error for *sql.DB.Close action
+func (e *ExpectedClose) WillReturnError(err error) *ExpectedClose {
+	e.err = err
+	return e
+}
+
+// String returns string representation
+func (e *ExpectedClose) String() string {
+	msg := "ExpectedClose => expecting database Close"
+	if e.err != nil {
+		msg += fmt.Sprintf(", which should return error: %s", e.err)
+	}
+	return msg
+}
+
+// ExpectedBegin is used to manage *sql.DB.Begin expectation
+// returned by *Sqlmock.ExpectBegin.
+type ExpectedBegin struct {
+	commonExpectation
+	delay time.Duration
+}
+
+// WillReturnError allows to set an error for *sql.DB.Begin action
+func (e *ExpectedBegin) WillReturnError(err error) *ExpectedBegin {
+	e.err = err
+	return e
+}
+
+// String returns string representation
+func (e *ExpectedBegin) String() string {
+	msg := "ExpectedBegin => expecting database transaction Begin"
+	if e.err != nil {
+		msg += fmt.Sprintf(", which should return error: %s", e.err)
+	}
+	return msg
+}
+
+// WillDelayFor allows to specify duration for which it will delay
+// result. May be used together with Context
+func (e *ExpectedBegin) WillDelayFor(duration time.Duration) *ExpectedBegin {
+	e.delay = duration
+	return e
+}
+
+// ExpectedCommit is used to manage *sql.Tx.Commit expectation
+// returned by *Sqlmock.ExpectCommit.
+type ExpectedCommit struct {
+	commonExpectation
+}
+
+// WillReturnError allows to set an error for *sql.Tx.Close action
+func (e *ExpectedCommit) WillReturnError(err error) *ExpectedCommit {
+	e.err = err
+	return e
+}
+
+// String returns string representation
+func (e *ExpectedCommit) String() string {
+	msg := "ExpectedCommit => expecting transaction Commit"
+	if e.err != nil {
+		msg += fmt.Sprintf(", which should return error: %s", e.err)
+	}
+	return msg
+}
+
+// ExpectedRollback is used to manage *sql.Tx.Rollback expectation
+// returned by *Sqlmock.ExpectRollback.
+type ExpectedRollback struct {
+	commonExpectation
+}
+
+// WillReturnError allows to set an error for *sql.Tx.Rollback action
+func (e *ExpectedRollback) WillReturnError(err error) *ExpectedRollback {
+	e.err = err
+	return e
+}
+
+// String returns string representation
+func (e *ExpectedRollback) String() string {
+	msg := "ExpectedRollback => expecting transaction Rollback"
+	if e.err != nil {
+		msg += fmt.Sprintf(", which should return error: %s", e.err)
+	}
+	return msg
+}
+
+// ExpectedQuery is used to manage *sql.DB.Query, *dql.DB.QueryRow, *sql.Tx.Query,
+// *sql.Tx.QueryRow, *sql.Stmt.Query or *sql.Stmt.QueryRow expectations.
+// Returned by *Sqlmock.ExpectQuery.
+type ExpectedQuery struct {
+	queryBasedExpectation
+	rows  driver.Rows
+	delay time.Duration
+}
+
+// WithArgs will match given expected args to actual database query arguments.
+// if at least one argument does not match, it will return an error. For specific
+// arguments an sqlmock.Argument interface can be used to match an argument.
+func (e *ExpectedQuery) WithArgs(args ...driver.Value) *ExpectedQuery {
+	e.args = args
+	return e
+}
+
+// WillReturnError allows to set an error for expected database query
+func (e *ExpectedQuery) WillReturnError(err error) *ExpectedQuery {
+	e.err = err
+	return e
+}
+
+// WillDelayFor allows to specify duration for which it will delay
+// result. May be used together with Context
+func (e *ExpectedQuery) WillDelayFor(duration time.Duration) *ExpectedQuery {
+	e.delay = duration
+	return e
+}
+
+// String returns string representation
+func (e *ExpectedQuery) String() string {
+	msg := "ExpectedQuery => expecting Query or QueryRow which:"
+	msg += "\n  - matches sql: '" + e.sqlRegex.String() + "'"
+
+	if len(e.args) == 0 {
+		msg += "\n  - is without arguments"
+	} else {
+		msg += "\n  - is with arguments:\n"
+		for i, arg := range e.args {
+			msg += fmt.Sprintf("    %d - %+v\n", i, arg)
+		}
+		msg = strings.TrimSpace(msg)
+	}
+
+	if e.rows != nil {
+		msg += fmt.Sprintf("\n  - %s", e.rows)
+	}
+
+	if e.err != nil {
+		msg += fmt.Sprintf("\n  - should return error: %s", e.err)
+	}
+
+	return msg
+}
+
+// ExpectedExec is used to manage *sql.DB.Exec, *sql.Tx.Exec or *sql.Stmt.Exec expectations.
+// Returned by *Sqlmock.ExpectExec.
+type ExpectedExec struct {
+	queryBasedExpectation
+	result driver.Result
+	delay  time.Duration
+}
+
+// WithArgs will match given expected args to actual database exec operation arguments.
+// if at least one argument does not match, it will return an error. For specific
+// arguments an sqlmock.Argument interface can be used to match an argument.
+func (e *ExpectedExec) WithArgs(args ...driver.Value) *ExpectedExec {
+	e.args = args
+	return e
+}
+
+// WillReturnError allows to set an error for expected database exec action
+func (e *ExpectedExec) WillReturnError(err error) *ExpectedExec {
+	e.err = err
+	return e
+}
+
+// WillDelayFor allows to specify duration for which it will delay
+// result. May be used together with Context
+func (e *ExpectedExec) WillDelayFor(duration time.Duration) *ExpectedExec {
+	e.delay = duration
+	return e
+}
+
+// String returns string representation
+func (e *ExpectedExec) String() string {
+	msg := "ExpectedExec => expecting Exec which:"
+	msg += "\n  - matches sql: '" + e.sqlRegex.String() + "'"
+
+	if len(e.args) == 0 {
+		msg += "\n  - is without arguments"
+	} else {
+		msg += "\n  - is with arguments:\n"
+		var margs []string
+		for i, arg := range e.args {
+			margs = append(margs, fmt.Sprintf("    %d - %+v", i, arg))
+		}
+		msg += strings.Join(margs, "\n")
+	}
+
+	if e.result != nil {
+		res, _ := e.result.(*result)
+		msg += "\n  - should return Result having:"
+		msg += fmt.Sprintf("\n      LastInsertId: %d", res.insertID)
+		msg += fmt.Sprintf("\n      RowsAffected: %d", res.rowsAffected)
+		if res.err != nil {
+			msg += fmt.Sprintf("\n      Error: %s", res.err)
+		}
+	}
+
+	if e.err != nil {
+		msg += fmt.Sprintf("\n  - should return error: %s", e.err)
+	}
+
+	return msg
+}
+
+// WillReturnResult arranges for an expected Exec() to return a particular
+// result, there is sqlmock.NewResult(lastInsertID int64, affectedRows int64) method
+// to build a corresponding result. Or if actions needs to be tested against errors
+// sqlmock.NewErrorResult(err error) to return a given error.
+func (e *ExpectedExec) WillReturnResult(result driver.Result) *ExpectedExec {
+	e.result = result
+	return e
+}
+
+// ExpectedPrepare is used to manage *sql.DB.Prepare or *sql.Tx.Prepare expectations.
+// Returned by *Sqlmock.ExpectPrepare.
+type ExpectedPrepare struct {
+	commonExpectation
+	mock      *sqlmock
+	sqlRegex  *regexp.Regexp
+	statement driver.Stmt
+	closeErr  error
+	delay     time.Duration
+}
+
+// WillReturnError allows to set an error for the expected *sql.DB.Prepare or *sql.Tx.Prepare action.
+func (e *ExpectedPrepare) WillReturnError(err error) *ExpectedPrepare {
+	e.err = err
+	return e
+}
+
+// WillReturnCloseError allows to set an error for this prapared statement Close action
+func (e *ExpectedPrepare) WillReturnCloseError(err error) *ExpectedPrepare {
+	e.closeErr = err
+	return e
+}
+
+// WillDelayFor allows to specify duration for which it will delay
+// result. May be used together with Context
+func (e *ExpectedPrepare) WillDelayFor(duration time.Duration) *ExpectedPrepare {
+	e.delay = duration
+	return e
+}
+
+// ExpectQuery allows to expect Query() or QueryRow() on this prepared statement.
+// this method is convenient in order to prevent duplicating sql query string matching.
+func (e *ExpectedPrepare) ExpectQuery() *ExpectedQuery {
+	eq := &ExpectedQuery{}
+	eq.sqlRegex = e.sqlRegex
+	e.mock.expected = append(e.mock.expected, eq)
+	return eq
+}
+
+// ExpectExec allows to expect Exec() on this prepared statement.
+// this method is convenient in order to prevent duplicating sql query string matching.
+func (e *ExpectedPrepare) ExpectExec() *ExpectedExec {
+	eq := &ExpectedExec{}
+	eq.sqlRegex = e.sqlRegex
+	e.mock.expected = append(e.mock.expected, eq)
+	return eq
+}
+
+// String returns string representation
+func (e *ExpectedPrepare) String() string {
+	msg := "ExpectedPrepare => expecting Prepare statement which:"
+	msg += "\n  - matches sql: '" + e.sqlRegex.String() + "'"
+
+	if e.err != nil {
+		msg += fmt.Sprintf("\n  - should return error: %s", e.err)
+	}
+
+	if e.closeErr != nil {
+		msg += fmt.Sprintf("\n  - should return error on Close: %s", e.closeErr)
+	}
+
+	return msg
+}
+
+// query based expectation
+// adds a query matching logic
+type queryBasedExpectation struct {
+	commonExpectation
+	sqlRegex *regexp.Regexp
+	args     []driver.Value
+}
+
+func (e *queryBasedExpectation) attemptMatch(sql string, args []namedValue) (err error) {
+	if !e.queryMatches(sql) {
+		return fmt.Errorf(`could not match sql: "%s" with expected regexp "%s"`, sql, e.sqlRegex.String())
+	}
+
+	// catch panic
+	defer func() {
+		if e := recover(); e != nil {
+			_, ok := e.(error)
+			if !ok {
+				err = fmt.Errorf(e.(string))
+			}
+		}
+	}()
+
+	err = e.argsMatches(args)
+	return
+}
+
+func (e *queryBasedExpectation) queryMatches(sql string) bool {
+	return e.sqlRegex.MatchString(sql)
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/expectations_before_go18.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/expectations_before_go18.go b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/expectations_before_go18.go
new file mode 100644
index 0000000..146f240
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/expectations_before_go18.go
@@ -0,0 +1,52 @@
+// +build !go1.8
+
+package sqlmock
+
+import (
+	"database/sql/driver"
+	"fmt"
+	"reflect"
+)
+
+// WillReturnRows specifies the set of resulting rows that will be returned
+// by the triggered query
+func (e *ExpectedQuery) WillReturnRows(rows *Rows) *ExpectedQuery {
+	e.rows = &rowSets{sets: []*Rows{rows}}
+	return e
+}
+
+func (e *queryBasedExpectation) argsMatches(args []namedValue) error {
+	if nil == e.args {
+		return nil
+	}
+	if len(args) != len(e.args) {
+		return fmt.Errorf("expected %d, but got %d arguments", len(e.args), len(args))
+	}
+	for k, v := range args {
+		// custom argument matcher
+		matcher, ok := e.args[k].(Argument)
+		if ok {
+			// @TODO: does it make sense to pass value instead of named value?
+			if !matcher.Match(v.Value) {
+				return fmt.Errorf("matcher %T could not match %d argument %T - %+v", matcher, k, args[k], args[k])
+			}
+			continue
+		}
+
+		dval := e.args[k]
+		// convert to driver converter
+		darg, err := driver.DefaultParameterConverter.ConvertValue(dval)
+		if err != nil {
+			return fmt.Errorf("could not convert %d argument %T - %+v to driver value: %s", k, e.args[k], e.args[k], err)
+		}
+
+		if !driver.IsValue(darg) {
+			return fmt.Errorf("argument %d: non-subset type %T returned from Value", k, darg)
+		}
+
+		if !reflect.DeepEqual(darg, v.Value) {
+			return fmt.Errorf("argument %d expected [%T - %+v] does not match actual [%T - %+v]", k, darg, darg, v.Value, v.Value)
+		}
+	}
+	return nil
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/expectations_go18.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/expectations_go18.go b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/expectations_go18.go
new file mode 100644
index 0000000..2b4b44e
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/expectations_go18.go
@@ -0,0 +1,66 @@
+// +build go1.8
+
+package sqlmock
+
+import (
+	"database/sql"
+	"database/sql/driver"
+	"fmt"
+	"reflect"
+)
+
+// WillReturnRows specifies the set of resulting rows that will be returned
+// by the triggered query
+func (e *ExpectedQuery) WillReturnRows(rows ...*Rows) *ExpectedQuery {
+	sets := make([]*Rows, len(rows))
+	for i, r := range rows {
+		sets[i] = r
+	}
+	e.rows = &rowSets{sets: sets}
+	return e
+}
+
+func (e *queryBasedExpectation) argsMatches(args []namedValue) error {
+	if nil == e.args {
+		return nil
+	}
+	if len(args) != len(e.args) {
+		return fmt.Errorf("expected %d, but got %d arguments", len(e.args), len(args))
+	}
+	// @TODO should we assert either all args are named or ordinal?
+	for k, v := range args {
+		// custom argument matcher
+		matcher, ok := e.args[k].(Argument)
+		if ok {
+			if !matcher.Match(v.Value) {
+				return fmt.Errorf("matcher %T could not match %d argument %T - %+v", matcher, k, args[k], args[k])
+			}
+			continue
+		}
+
+		dval := e.args[k]
+		if named, isNamed := dval.(sql.NamedArg); isNamed {
+			dval = named.Value
+			if v.Name != named.Name {
+				return fmt.Errorf("named argument %d: name: \"%s\" does not match expected: \"%s\"", k, v.Name, named.Name)
+			}
+		} else if k+1 != v.Ordinal {
+			return fmt.Errorf("argument %d: ordinal position: %d does not match expected: %d", k, k+1, v.Ordinal)
+		}
+
+		// convert to driver converter
+		darg, err := driver.DefaultParameterConverter.ConvertValue(dval)
+		if err != nil {
+			return fmt.Errorf("could not convert %d argument %T - %+v to driver value: %s", k, e.args[k], e.args[k], err)
+		}
+
+		if !driver.IsValue(darg) {
+			return fmt.Errorf("argument %d: non-subset type %T returned from Value", k, darg)
+		}
+
+		if !reflect.DeepEqual(darg, v.Value) {
+			return fmt.Errorf("argument %d expected [%T - %+v] does not match actual [%T - %+v]", k, darg, darg, v.Value, v.Value)
+		}
+	}
+	return nil
+}

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/b5fa231b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/expectations_go18_test.go
----------------------------------------------------------------------
diff --git a/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/expectations_go18_test.go b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/expectations_go18_test.go
new file mode 100644
index 0000000..5f30d2f
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/vendor/gopkg.in/DATA-DOG/go-sqlmock/expectations_go18_test.go
@@ -0,0 +1,64 @@
+// +build go1.8
+
+package sqlmock
+
+import (
+	"database/sql"
+	"database/sql/driver"
+	"testing"
+)
+
+func TestQueryExpectationNamedArgComparison(t *testing.T) {
+	e := &queryBasedExpectation{}
+	against := []namedValue{{Value: int64(5), Name: "id"}}
+	if err := e.argsMatches(against); err != nil {
+		t.Errorf("arguments should match, since the no expectation was set, but got err: %s", err)
+	}
+
+	e.args = []driver.Value{
+		sql.Named("id", 5),
+		sql.Named("s", "str"),
+	}
+
+	if err := e.argsMatches(against); err == nil {
+		t.Error("arguments should not match, since the size is not the same")
+	}
+
+	against = []namedValue{
+		{Value: int64(5), Name: "id"},
+		{Value: "str", Name: "s"},
+	}
+
+	if err := e.argsMatches(against); err != nil {
+		t.Errorf("arguments should have matched, but it did not: %v", err)
+	}
+
+	against = []namedValue{
+		{Value: int64(5), Name: "id"},
+		{Value: "str", Name: "username"},
+	}
+
+	if err := e.argsMatches(against); err == nil {
+		t.Error("arguments matched, but it should have not due to Name")
+	}
+
+	e.args = []driver.Value{int64(5), "str"}
+
+	against = []namedValue{
+		{Value: int64(5), Ordinal: 0},
+		{Value: "str", Ordinal: 1},
+	}
+
+	if err := e.argsMatches(against); err == nil {
+		t.Error("arguments matched, but it should have not due to wrong Ordinal position")
+	}
+
+	against = []namedValue{
+		{Value: int64(5), Ordinal: 1},
+		{Value: "str", Ordinal: 2},
+	}
+
+	if err := e.argsMatches(against); err != nil {
+		t.Errorf("arguments should have matched, but it did not: %v", err)
+	}
+}