This is an automated email from the ASF dual-hosted git repository.
ocket8888 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 efef336 Add TO Go remap.config (#3688)
efef336 is described below
commit efef3369bf5de61ae6dc4ca580ad2b77d331ade4
Author: Robert Butts <[email protected]>
AuthorDate: Tue Oct 1 18:41:19 2019 -0600
Add TO Go remap.config (#3688)
* Add TO Go remap.config
* Add atscfg error log for multiple cacheurl/key
Adds error logging whenever the config gen inserts multiple cacheurl
or cachekey plugins on the same line.
This isn't supported in ATS, and will usually do the wrong thing.
But, we've always generated config like this, so for now, this
doesn't change the config gen, but it logs the error
* Add atstccfg (TO ATS config gen) remap.config
* Fix godoc comments
* Change sort.Sort(sort.StringSlice to sort.Strings
* Fix atscfg ATS major version for >9.
* Add GoDoc for util.HashInts
* Change for loop to strings.Join
---
CHANGELOG.md | 3 +
lib/go-atscfg/atscfg.go | 20 +-
lib/go-atscfg/atscfg_test.go | 36 ++
lib/go-atscfg/parentdotconfig.go | 4 -
lib/go-atscfg/remapdotconfig.go | 409 +++++++++++++++
lib/go-atscfg/remapdotconfig_test.go | 129 +++++
lib/go-tc/enum.go | 18 +
lib/go-util/num.go | 18 +
traffic_ops/ort/atstccfg/cachedotconfig.go | 2 +-
traffic_ops/ort/atstccfg/parentdotconfig.go | 2 +-
traffic_ops/ort/atstccfg/remapdotconfig.go | 372 +++++++++++++
traffic_ops/ort/atstccfg/routing.go | 1 +
traffic_ops/ort/atstccfg/toreq.go | 34 +-
traffic_ops/ort/atstccfg/trafficops.go | 17 +
.../testing/api/v14/parentdotconfig_test.go | 21 +-
traffic_ops/testing/api/v14/remapdotconfig_test.go | 95 ++++
traffic_ops/testing/api/v14/tc-fixtures.json | 2 +-
.../traffic_ops_golang/ats/{ => atsserver}/meta.go | 137 +----
.../ats/{ => atsserver}/parentdotconfig.go | 7 +-
.../ats/atsserver/remapdotconfig.go | 93 ++++
traffic_ops/traffic_ops_golang/ats/config.go | 23 -
traffic_ops/traffic_ops_golang/ats/db.go | 575 +++++++++++++++++++++
traffic_ops/traffic_ops_golang/routing/routes.go | 7 +-
23 files changed, 1837 insertions(+), 188 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b1130b..a0900e5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,9 @@ The format is based on [Keep a
Changelog](http://keepachangelog.com/en/1.0.0/).
- /api/1.4/user/login/oauth `POST`
- /api/1.1/profiles/:name/configfiles/ats/* `GET`
- /api/1.1/dbdump `GET`
+ - /api/1.1/servers/:name/configfiles/ats/parent.config
+ - /api/1.1/servers/:name/configfiles/ats/remap.config
+
- To support reusing a single riak cluster connection, an optional parameter
is added to riak.conf: "HealthCheckInterval". This options takes a 'Duration'
value (ie: 10s, 5m) which affects how often the riak cluster is health checked.
Default is currently set to: "HealthCheckInterval": "5s".
- Added a new Go db/admin binary to replace the Perl db/admin.pl script which
is now deprecated and will be removed in a future release. The new db/admin
binary is essentially a drop-in replacement for db/admin.pl since it supports
all of the same commands and options; therefore, it should be used in place of
db/admin.pl for all the same tasks.
- Added an API 1.4 endpoint, /api/1.4/cdns/dnsseckeys/refresh, to perform
necessary behavior previously served outside the API under `/internal`.
diff --git a/lib/go-atscfg/atscfg.go b/lib/go-atscfg/atscfg.go
index 4209b8f..f2c52ed 100644
--- a/lib/go-atscfg/atscfg.go
+++ b/lib/go-atscfg/atscfg.go
@@ -28,6 +28,10 @@ import (
"github.com/apache/trafficcontrol/lib/go-tc"
)
+const InvalidID = -1
+
+const DefaultATSVersion = "5" // TODO Emulates Perl; change to 6? ATC no
longer officially supports ATS 5.
+
const HeaderCommentDateFormat = "Mon Jan 2 15:04:05 MST 2006"
type ServerInfo struct {
@@ -36,6 +40,7 @@ type ServerInfo struct {
CDNID int
DomainName string
HostName string
+ HTTPSPort int
ID int
IP string
ParentCacheGroupID int
@@ -67,16 +72,19 @@ func GenericHeaderComment(name string, toolName string, url
string) string {
// GetATSMajorVersionFromATSVersion returns the major version of the given
profile's package trafficserver parameter.
// The atsVersion is typically a Parameter on the Server's Profile, with the
configFile "package" name "trafficserver".
-// Returns an error if atsVersion is empty or not a number.
+// Returns an error if atsVersion is empty or does not start with an unsigned
integer followed by a period or nothing.
func GetATSMajorVersionFromATSVersion(atsVersion string) (int, error) {
- if len(atsVersion) == 0 {
- return 0, errors.New("ats version missing")
+ dotPos := strings.Index(atsVersion, ".")
+ if dotPos == -1 {
+ dotPos = len(atsVersion) // if there's no '.' then assume the
whole string is just a major version.
}
- atsMajorVer, err := strconv.Atoi(atsVersion[:1])
+ majorVerStr := atsVersion[:dotPos]
+
+ majorVer, err := strconv.ParseUint(majorVerStr, 10, 64)
if err != nil {
- return 0, errors.New("ats version parameter '" + atsVersion +
"' is not a number")
+ return 0, errors.New("unexpected version format, expected e.g.
'7.1.2.whatever'")
}
- return atsMajorVer, nil
+ return int(majorVer), nil
}
type DeliveryServiceID int
diff --git a/lib/go-atscfg/atscfg_test.go b/lib/go-atscfg/atscfg_test.go
index f81b053..27fd9ff 100644
--- a/lib/go-atscfg/atscfg_test.go
+++ b/lib/go-atscfg/atscfg_test.go
@@ -80,3 +80,39 @@ func TestTrimParamUnderscoreNumSuffix(t *testing.T) {
}
}
}
+
+func TestGetATSMajorVersionFromATSVersion(t *testing.T) {
+ inputExpected := map[string]int{
+ `7.1.2-34.56abcde.el7.centos.x86_64`: 7,
+ `8`: 8,
+ `8.1`: 8,
+ `10.1`: 10,
+ `1234.1.2-34.56abcde.el7.centos.x86_64`: 1234,
+ }
+ errExpected := []string{
+ "a7.1.2-34.56abcde.el7.centos.x86_64",
+ `-7.1.2-34.56abcde.el7.centos.x86_64`,
+ ".7.1.2-34.56abcde.el7.centos.x86_64",
+ "7a.1.2-34.56abcde.el7.centos.x86_64",
+ "7-a.1.2-34.56abcde.el7.centos.x86_64",
+ "7-2.1.2-34.56abcde.el7.centos.x86_64",
+ "100-2.1.2-34.56abcde.el7.centos.x86_64",
+ "7a",
+ "",
+ "-",
+ ".",
+ }
+
+ for input, expected := range inputExpected {
+ if actual, err := GetATSMajorVersionFromATSVersion(input); err
!= nil {
+ t.Errorf("expected %v actual: error '%v'", expected,
err)
+ } else if actual != expected {
+ t.Errorf("expected %v actual: %v", expected, actual)
+ }
+ }
+ for _, input := range errExpected {
+ if actual, err := GetATSMajorVersionFromATSVersion(input); err
== nil {
+ t.Errorf("input %v expected: error, actual: nil error
'%v'", input, actual)
+ }
+ }
+}
diff --git a/lib/go-atscfg/parentdotconfig.go b/lib/go-atscfg/parentdotconfig.go
index e39b4b1..17a01ea 100644
--- a/lib/go-atscfg/parentdotconfig.go
+++ b/lib/go-atscfg/parentdotconfig.go
@@ -31,8 +31,6 @@ import (
"github.com/apache/trafficcontrol/lib/go-util"
)
-const InvalidID = -1
-
const ParentConfigParamQStringHandling = "psel.qstring_handling"
const ParentConfigParamMSOAlgorithm = "mso.algorithm"
const ParentConfigParamMSOParentRetry = "mso.parent_retry"
@@ -57,8 +55,6 @@ const ParentConfigCacheParamNotAParent = "not_a_parent"
// TODO change, this is terrible practice, using a hard-coded key. What if
there were a delivery service named "all_parents" (transliterated Perl)
const DeliveryServicesAllParentsKey = "all_parents"
-const DefaultATSVersion = "5" // TODO Emulates Perl; change to 6? ATC no
longer officially supports ATS 5.
-
type ParentConfigDS struct {
Name tc.DeliveryServiceName
QStringIgnore tc.QStringIgnore
diff --git a/lib/go-atscfg/remapdotconfig.go b/lib/go-atscfg/remapdotconfig.go
new file mode 100644
index 0000000..2ac8d04
--- /dev/null
+++ b/lib/go-atscfg/remapdotconfig.go
@@ -0,0 +1,409 @@
+package atscfg
+
+/*
+ * 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 (
+ "errors"
+ "sort"
+ "strconv"
+ "strings"
+
+ "github.com/apache/trafficcontrol/lib/go-log"
+ "github.com/apache/trafficcontrol/lib/go-tc"
+)
+
+const CacheURLParameterConfigFile = "cacheurl.config"
+const CacheKeyParameterConfigFile = "cachekey.config"
+
+type RemapConfigDSData struct {
+ ID int
+ Type tc.DSType
+ OriginFQDN *string
+ MidHeaderRewrite *string
+ CacheURL *string
+ RangeRequestHandling *int
+ CacheKeyConfigParams map[string]string
+ RemapText *string
+ EdgeHeaderRewrite *string
+ SigningAlgorithm *string
+ Name string
+ QStringIgnore *int
+ RegexRemap *string
+ FQPacingRate *int
+ DSCP int
+ RoutingName *string
+ MultiSiteOrigin *string
+ Pattern *string
+ RegexType *string
+ Domain *string
+ RegexSetNumber *string
+ OriginShield *string
+ ProfileID *int
+ Protocol *int
+ AnonymousBlockingEnabled *bool
+ Active bool
+}
+
+func MakeRemapDotConfig(
+ serverName tc.CacheName,
+ toToolName string, // tm.toolname global parameter (TODO: cache itself?)
+ toURL string, // tm.url global parameter (TODO: cache itself?)
+ atsMajorVersion int,
+ cacheURLConfigParams map[string]string, // map[name]value for this
server's profile and config file 'cacheurl.config'
+ dsProfilesCacheKeyConfigParams map[int]map[string]string, //
map[profileID][paramName]paramVal for this server's profile, config file
'cachekey.config'
+ serverPackageParamData map[string]string, // map[paramName]paramVal for
this server, config file 'package'
+ serverInfo *ServerInfo, // ServerInfo for this server
+ remapDSData []RemapConfigDSData,
+) string {
+ hdr := GenericHeaderComment(string(serverName), toToolName, toURL)
+ text := ""
+ if tc.CacheTypeFromString(serverInfo.Type) == tc.CacheTypeMid {
+ text = GetServerConfigRemapDotConfigForMid(atsMajorVersion,
dsProfilesCacheKeyConfigParams, serverInfo, remapDSData, hdr)
+ } else {
+ text =
GetServerConfigRemapDotConfigForEdge(cacheURLConfigParams,
dsProfilesCacheKeyConfigParams, serverPackageParamData, serverInfo,
remapDSData, atsMajorVersion, hdr)
+ }
+ return text
+}
+
+func GetServerConfigRemapDotConfigForMid(
+ atsMajorVersion int,
+ profilesCacheKeyConfigParams map[int]map[string]string,
+ server *ServerInfo,
+ dses []RemapConfigDSData,
+ header string,
+) string {
+ midRemaps := map[string]string{}
+ for _, ds := range dses {
+ if ds.Type.IsLive() && !ds.Type.IsNational() {
+ continue // Live local delivery services skip mids
+ }
+
+ if ds.OriginFQDN == nil || *ds.OriginFQDN == "" {
+ log.Warnf("GetServerConfigRemapDotConfigForMid ds '" +
ds.Name + "' has no origin fqdn, skipping!") // TODO confirm - Perl uses
without checking!
+ continue
+ }
+
+ if midRemaps[*ds.OriginFQDN] != "" {
+ continue // skip remap rules from extra HOST_REGEXP
entries
+ }
+
+ // multiple uses of cacheurl and cachekey plugins don't work
right in ATS, but Perl has always done it.
+ // So for now, keep track of it, so we can log an error when it
happens.
+ hasCacheURL := false
+ hasCacheKey := false
+
+ midRemap := ""
+ if ds.MidHeaderRewrite != nil && *ds.MidHeaderRewrite != "" {
+ midRemap += ` @plugin=header_rewrite.so @pparam=` +
MidHeaderRewriteConfigFileName(ds.Name)
+ }
+ if ds.QStringIgnore != nil && *ds.QStringIgnore ==
tc.QueryStringIgnoreIgnoreInCacheKeyAndPassUp {
+ qstr, addedCacheURL, addedCacheKey :=
GetQStringIgnoreRemap(atsMajorVersion)
+ if addedCacheURL {
+ hasCacheURL = true
+ }
+ if addedCacheKey {
+ hasCacheKey = true
+ }
+ midRemap += qstr
+ }
+ if ds.CacheURL != nil && *ds.CacheURL != "" {
+ if hasCacheURL {
+ log.Errorln("DeliveryService qstring_ignore and
cacheurl both add cacheurl, but ATS cacheurl doesn't work correctly with
multiple entries! Adding anyway!")
+ }
+ midRemap += ` @plugin=cacheurl.so @pparam=` +
CacheURLConfigFileName(ds.Name)
+ }
+
+ if ds.ProfileID != nil &&
len(profilesCacheKeyConfigParams[*ds.ProfileID]) > 0 {
+ if hasCacheKey {
+ log.Errorln("DeliveryService qstring_ignore and
cachekey params both add cachekey, but ATS cachekey doesn't work correctly with
multiple entries! Adding anyway!")
+ }
+ midRemap += ` @plugin=cachekey.so`
+ for name, val := range
profilesCacheKeyConfigParams[*ds.ProfileID] {
+ midRemap += ` @pparam=--` + name + "=" + val
+ }
+ }
+ if ds.RangeRequestHandling != nil && *ds.RangeRequestHandling
== tc.RangeRequestHandlingCacheRangeRequest {
+ midRemap += ` @plugin=cache_range_requests.so`
+ }
+
+ if midRemap != "" {
+ midRemaps[*ds.OriginFQDN] = midRemap
+ }
+ }
+
+ textLines := []string{}
+ for originFQDN, midRemap := range midRemaps {
+ textLines = append(textLines, "map "+originFQDN+"
"+originFQDN+midRemap+"\n")
+ }
+ sort.Strings(textLines)
+
+ text := header
+ text += strings.Join(textLines, "")
+ return text
+}
+
+func GetServerConfigRemapDotConfigForEdge(
+ cacheURLConfigParams map[string]string,
+ profilesCacheKeyConfigParams map[int]map[string]string,
+ serverPackageParamData map[string]string, // map[paramName]paramVal for
this server, config file 'package'
+ server *ServerInfo,
+ dses []RemapConfigDSData,
+ atsMajorVersion int,
+ header string,
+) string {
+ for name, val := range serverPackageParamData {
+ serverPackageParamData[name] = strings.Replace(val,
"__HOSTNAME__", server.HostName+"."+server.DomainName, -1)
+ }
+
+ textLines := []string{}
+
+ for _, ds := range dses {
+ remapText := ""
+ if ds.Type == tc.DSTypeAnyMap {
+ if ds.RemapText == nil {
+ log.Errorln("ds '" + ds.Name + "' is ANY_MAP,
but has no remap text - skipping")
+ continue
+ }
+ remapText = *ds.RemapText + "\n"
+ textLines = append(textLines, remapText)
+ continue
+ }
+
+ remapLines, err := MakeEdgeDSDataRemapLines(ds, server)
+ if err != nil {
+ log.Errorln("making remap lines for DS '" + ds.Name +
"' - skipping! : " + err.Error())
+ continue
+ }
+
+ for _, line := range remapLines {
+ profilecacheKeyConfigParams := (map[string]string)(nil)
+ if ds.ProfileID != nil {
+ profilecacheKeyConfigParams =
profilesCacheKeyConfigParams[*ds.ProfileID]
+ }
+ remapText = BuildRemapLine(cacheURLConfigParams,
atsMajorVersion, server, serverPackageParamData, remapText, ds, line.From,
line.To, profilecacheKeyConfigParams)
+ }
+ textLines = append(textLines, remapText)
+ }
+
+ text := header
+ sort.Strings(textLines)
+ text += strings.Join(textLines, "")
+ return text
+}
+
+// BuildRemapLine builds the remap line for the given server and delivery
service.
+// The cacheKeyConfigParams map may be nil, if this ds profile had no cache
key config params.
+func BuildRemapLine(cacheURLConfigParams map[string]string, atsMajorVersion
int, server *ServerInfo, pData map[string]string, text string, ds
RemapConfigDSData, mapFrom string, mapTo string, cacheKeyConfigParams
map[string]string) string {
+ // ds = 'remap' in perl
+
+ mapFrom = strings.Replace(mapFrom, `__http__`, server.HostName, -1)
+
+ if _, hasDSCPRemap := pData["dscp_remap"]; hasDSCPRemap {
+ text += "map " + mapFrom + " " + mapTo + `
@plugin=dscp_remap.so @pparam=` + strconv.Itoa(ds.DSCP)
+ } else {
+ text += "map " + mapFrom + " " + mapTo + `
@plugin=header_rewrite.so @pparam=dscp/set_dscp_` + strconv.Itoa(ds.DSCP) +
".config"
+ }
+
+ if ds.EdgeHeaderRewrite != nil && *ds.EdgeHeaderRewrite != "" {
+ text += ` @plugin=header_rewrite.so @pparam=` +
EdgeHeaderRewriteConfigFileName(ds.Name)
+ }
+
+ if ds.SigningAlgorithm != nil && *ds.SigningAlgorithm != "" {
+ if *ds.SigningAlgorithm == tc.SigningAlgorithmURLSig {
+ text += ` @plugin=url_sig.so @pparam=url_sig_` +
ds.Name + ".config"
+ } else if *ds.SigningAlgorithm == tc.SigningAlgorithmURISigning
{
+ text += ` @plugin=uri_signing.so @pparam=uri_signing_`
+ ds.Name + ".config"
+ }
+ }
+
+ // multiple uses of cacheurl and cachekey plugins don't work right in
ATS, but Perl has always done it.
+ // So for now, keep track of it, so we can log an error when it happens.
+ hasCacheURL := false
+ hasCacheKey := false
+
+ if ds.QStringIgnore != nil {
+ if *ds.QStringIgnore == tc.QueryStringIgnoreDropAtEdge {
+ dqsFile := "drop_qstring.config"
+ text += ` @plugin=regex_remap.so @pparam=` + dqsFile
+ } else if *ds.QStringIgnore ==
tc.QueryStringIgnoreIgnoreInCacheKeyAndPassUp {
+ if _, globalExists := cacheURLConfigParams["location"];
globalExists {
+ log.Warnln("qstring_ignore == 1, but global
cacheurl.config param exists, so skipping remap rename
config_file=cacheurl.config parameter")
+ } else {
+ qstr, addedCacheURL, addedCacheKey :=
GetQStringIgnoreRemap(atsMajorVersion)
+ if addedCacheURL {
+ hasCacheURL = true
+ }
+ if addedCacheKey {
+ hasCacheKey = true
+ }
+ text += qstr
+ }
+ }
+ }
+
+ if ds.CacheURL != nil && *ds.CacheURL != "" {
+ if hasCacheURL {
+ log.Errorln("DeliveryService qstring_ignore and
cacheurl both add cacheurl, but ATS cacheurl doesn't work correctly with
multiple entries! Adding anyway!")
+ }
+ text += ` @plugin=cacheurl.so @pparam=` +
CacheURLConfigFileName(ds.Name)
+ }
+
+ if len(cacheKeyConfigParams) > 0 {
+ if hasCacheKey {
+ log.Errorln("DeliveryService qstring_ignore and params
both add cachekey, but ATS cachekey doesn't work correctly with multiple
entries! Adding anyway!")
+ }
+ text += ` @plugin=cachekey.so`
+
+ keys := []string{}
+ for key, _ := range cacheKeyConfigParams {
+ keys = append(keys, key)
+ }
+ sort.Sort(sort.StringSlice(keys))
+
+ for _, key := range keys {
+ text += ` @pparam=--` + key + "=" +
cacheKeyConfigParams[key]
+ }
+ }
+
+ // Note: should use full path here?
+ if ds.RegexRemap != nil && *ds.RegexRemap != "" {
+ text += ` @plugin=regex_remap.so @pparam=regex_remap_` +
ds.Name + ".config"
+ }
+ if ds.RangeRequestHandling != nil {
+ if *ds.RangeRequestHandling ==
tc.RangeRequestHandlingBackgroundFetch {
+ text += ` @plugin=background_fetch.so
@pparam=bg_fetch.config`
+ } else if *ds.RangeRequestHandling ==
tc.RangeRequestHandlingCacheRangeRequest {
+ text += ` @plugin=cache_range_requests.so `
+ }
+ }
+ if ds.RemapText != nil && *ds.RemapText != "" {
+ text += " " + *ds.RemapText
+ }
+
+ if ds.FQPacingRate != nil && *ds.FQPacingRate > 0 {
+ text += ` @plugin=fq_pacing.so @pparam=--rate=` +
strconv.Itoa(*ds.FQPacingRate)
+ }
+ text += "\n"
+ return text
+}
+
+func DSProfileIDs(dses []RemapConfigDSData) []int {
+ dsProfileIDs := []int{}
+ for _, ds := range dses {
+ if ds.ProfileID != nil {
+ // TODO determine if this is right, if the DS has no
profile
+ dsProfileIDs = append(dsProfileIDs, *ds.ProfileID)
+ }
+ }
+ return dsProfileIDs
+}
+
+type RemapLine struct {
+ From string
+ To string
+}
+
+// MakeEdgeDSDataRemapLines returns the remap lines for the given server and
delivery service.
+// Returns nil, if the given server and ds have no remap lines, i.e. the DS
match is not a host regex, or has no origin FQDN.
+func MakeEdgeDSDataRemapLines(ds RemapConfigDSData, server *ServerInfo)
([]RemapLine, error) {
+ if ds.RegexType == nil || tc.DSMatchType(*ds.RegexType) !=
tc.DSMatchTypeHostRegex || ds.OriginFQDN == nil || *ds.OriginFQDN == "" {
+ return nil, nil
+ }
+
+ if ds.OriginFQDN == nil {
+ return nil, errors.New("ds missing origin fqdn")
+ }
+ if ds.Pattern == nil {
+ return nil, errors.New("ds missing regex pattern")
+ }
+ if ds.Protocol == nil {
+ return nil, errors.New("ds missing protocol")
+ }
+ if ds.Domain == nil {
+ return nil, errors.New("ds missing domain")
+ }
+
+ remapLines := []RemapLine{}
+ hostRegex := *ds.Pattern
+ mapTo := *ds.OriginFQDN + "/"
+
+ mapFromHTTP := "http://" + hostRegex + "/"
+ mapFromHTTPS := "https://" + hostRegex + "/"
+ if strings.HasSuffix(hostRegex, `.*`) {
+ re := hostRegex
+ re = strings.Replace(re, `\`, ``, -1)
+ re = strings.Replace(re, `.*`, ``, -1)
+
+ hName := "__http__"
+ if ds.Type.IsDNS() {
+ if ds.RoutingName == nil {
+ return nil, errors.New("ds is dns, but missing
routing name")
+ }
+ hName = *ds.RoutingName
+ }
+
+ portStr := ""
+ if hName == "__http__" && server.Port > 0 && server.Port != 80 {
+ portStr = ":" + strconv.Itoa(server.Port)
+ }
+
+ httpsPortStr := ""
+ if hName == "__http__" && server.HTTPSPort > 0 &&
server.HTTPSPort != 443 {
+ httpsPortStr = ":" + strconv.Itoa(server.HTTPSPort)
+ }
+
+ mapFromHTTP = "http://" + hName + re + *ds.Domain + portStr +
"/"
+ mapFromHTTPS = "https://" + hName + re + *ds.Domain +
httpsPortStr + "/"
+ }
+
+ if *ds.Protocol == tc.DSProtocolHTTP || *ds.Protocol ==
tc.DSProtocolHTTPAndHTTPS {
+ remapLines = append(remapLines, RemapLine{From: mapFromHTTP,
To: mapTo})
+ }
+ if *ds.Protocol == tc.DSProtocolHTTPS || *ds.Protocol ==
tc.DSProtocolHTTPToHTTPS || *ds.Protocol == tc.DSProtocolHTTPAndHTTPS {
+ remapLines = append(remapLines, RemapLine{From: mapFromHTTPS,
To: mapTo})
+ }
+
+ return remapLines, nil
+}
+
+func EdgeHeaderRewriteConfigFileName(dsName string) string {
+ return "hdr_rw_" + dsName + ".config"
+}
+
+func MidHeaderRewriteConfigFileName(dsName string) string {
+ return "hdr_rw_mid_" + dsName + ".config"
+}
+
+func CacheURLConfigFileName(dsName string) string {
+ return "cacheurl_" + dsName + ".config"
+}
+
+// GetQStringIgnoreRemap returns the remap, whether cacheurl was added, and
whether cachekey was added.
+func GetQStringIgnoreRemap(atsMajorVersion int) (string, bool, bool) {
+ if atsMajorVersion >= 6 {
+ addingCacheURL := false
+ addingCacheKey := true
+ return ` @plugin=cachekey.so @pparam=--separator=
@pparam=--remove-all-params=true @pparam=--remove-path=true
@pparam=--capture-prefix-uri=/^([^?]*)/$1/`, addingCacheURL, addingCacheKey
+ } else {
+ addingCacheURL := true
+ addingCacheKey := false
+ return ` @plugin=cacheurl.so @pparam=cacheurl_qstring.config`,
addingCacheURL, addingCacheKey
+ }
+}
diff --git a/lib/go-atscfg/remapdotconfig_test.go
b/lib/go-atscfg/remapdotconfig_test.go
new file mode 100644
index 0000000..94a1933
--- /dev/null
+++ b/lib/go-atscfg/remapdotconfig_test.go
@@ -0,0 +1,129 @@
+package atscfg
+
+/*
+ * 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 (
+ "strings"
+ "testing"
+
+ "github.com/apache/trafficcontrol/lib/go-tc"
+ "github.com/apache/trafficcontrol/lib/go-util"
+)
+
+func TestMakeRemapDotConfig(t *testing.T) {
+ serverName := tc.CacheName("server0")
+ toToolName := "to0"
+ toURL := "trafficops.example.net"
+ atsMajorVersion := 7
+
+ cacheURLConfigParams := map[string]string{
+ "not_location": "notinconfig",
+ }
+
+ dsProfilesCacheKeyConfigParams := map[int]map[string]string{
+ 46: map[string]string{
+ "cachekeykey": "cachekeyval",
+ },
+ }
+
+ serverPackageParamData := map[string]string{
+ "serverpkgval": "serverpkgval __HOSTNAME__ foo",
+ }
+
+ serverInfo := &ServerInfo{
+ CacheGroupID: 42,
+ CDN: "mycdn",
+ CDNID: 43,
+ DomainName: "mydomain",
+ HostName: "myhost",
+ HTTPSPort: 12443,
+ ID: 44,
+ IP: "192.168.2.4",
+ ParentCacheGroupID: 45,
+ ParentCacheGroupType: "CGType4",
+ ProfileID: 46,
+ ProfileName: "MyProfile",
+ Port: 12080,
+ SecondaryParentCacheGroupID: 47,
+ SecondaryParentCacheGroupType: "MySecondaryParentCG",
+ Type: "EDGE",
+ }
+
+ remapDSData := []RemapConfigDSData{
+ RemapConfigDSData{
+ ID: 48,
+ Type: "HTTP_LIVE",
+ OriginFQDN:
util.StrPtr("origin.example.test"),
+ MidHeaderRewrite: util.StrPtr("mymidrewrite"),
+ CacheURL: util.StrPtr("mycacheurl"),
+ RangeRequestHandling: util.IntPtr(0),
+ CacheKeyConfigParams:
map[string]string{"cachekeyparamname": "cachekeyparamval"},
+ RemapText: util.StrPtr("myremaptext"),
+ EdgeHeaderRewrite:
util.StrPtr("myedgeheaderrewrite"),
+ SigningAlgorithm: util.StrPtr("url_sig"),
+ Name: "mydsname",
+ QStringIgnore: util.IntPtr(0),
+ RegexRemap: util.StrPtr("myregexremap"),
+ FQPacingRate: util.IntPtr(0),
+ DSCP: 0,
+ RoutingName: util.StrPtr("myroutingname"),
+ MultiSiteOrigin: util.StrPtr("mymso"),
+ Pattern: util.StrPtr("myregexpattern"),
+ RegexType:
util.StrPtr(string(tc.DSMatchTypeHostRegex)),
+ Domain: util.StrPtr("mydomain"),
+ RegexSetNumber: util.StrPtr("myregexsetnum"),
+ OriginShield: util.StrPtr("myoriginshield"),
+ ProfileID: util.IntPtr(49),
+ Protocol: util.IntPtr(0),
+ AnonymousBlockingEnabled: util.BoolPtr(false),
+ Active: true,
+ },
+ }
+
+ txt := MakeRemapDotConfig(serverName, toToolName, toURL,
atsMajorVersion, cacheURLConfigParams, dsProfilesCacheKeyConfigParams,
serverPackageParamData, serverInfo, remapDSData)
+
+ txt = strings.TrimSpace(txt)
+
+ testComment(t, txt, string(serverName), toToolName, toURL)
+
+ txtLines := strings.Split(txt, "\n")
+
+ if len(txtLines) != 2 {
+ t.Errorf("expected one line for each remap plus a comment,
actual: '%v' count %v", txt, len(txtLines))
+ }
+
+ remapLine := txtLines[1]
+
+ if !strings.HasPrefix(remapLine, "map") {
+ t.Errorf("expected to start with 'map', actual '%v'", txt)
+ }
+
+ if !strings.Contains(remapLine, "http://myregexpattern") {
+ t.Errorf("expected to contain routing name, actual '%v'", txt)
+ }
+
+ if !strings.Contains(remapLine, "http://myregexpattern") {
+ t.Errorf("expected to contain routing name, actual '%v'", txt)
+ }
+
+ if !strings.Contains(remapLine, "origin.example.test") {
+ t.Errorf("expected to contain origin FQDN, actual '%v'", txt)
+ }
+}
diff --git a/lib/go-tc/enum.go b/lib/go-tc/enum.go
index 44c1a00..4020a89 100644
--- a/lib/go-tc/enum.go
+++ b/lib/go-tc/enum.go
@@ -55,6 +55,8 @@ type DeliveryServiceName string
// CacheType is the type (or tier) of a CDN cache.
type CacheType string
+const OriginLocationType = "ORG_LOC"
+
const (
// CacheTypeEdge represents an edge cache.
CacheTypeEdge = CacheType("EDGE")
@@ -112,6 +114,16 @@ func CacheTypeFromString(s string) CacheType {
return CacheTypeInvalid
}
+// These are prefixed "QueryStringIgnore" even though the values don't always
indicate ignoring, because the database column is named "qstring_ignore"
+
+const QueryStringIgnoreUseInCacheKeyAndPassUp = 0
+const QueryStringIgnoreIgnoreInCacheKeyAndPassUp = 1
+const QueryStringIgnoreDropAtEdge = 2
+
+const RangeRequestHandlingDontCache = 0
+const RangeRequestHandlingBackgroundFetch = 1
+const RangeRequestHandlingCacheRangeRequest = 2
+
// DSTypeCategory is the Delivery Service type category: HTTP or DNS
type DSTypeCategory string
@@ -150,6 +162,12 @@ func DSTypeCategoryFromString(s string) DSTypeCategory {
}
const SigningAlgorithmURLSig = "url_sig"
+const SigningAlgorithmURISigning = "uri_signing"
+
+const DSProtocolHTTP = 0
+const DSProtocolHTTPS = 1
+const DSProtocolHTTPAndHTTPS = 2
+const DSProtocolHTTPToHTTPS = 3
// CacheStatus represents the Traffic Server status set in Traffic Ops
(online, offline, admin_down, reported). The string values of this type should
match the Traffic Ops values.
type CacheStatus string
diff --git a/lib/go-util/num.go b/lib/go-util/num.go
index 32ccd47..bcbd313 100644
--- a/lib/go-util/num.go
+++ b/lib/go-util/num.go
@@ -20,6 +20,9 @@ package util
*/
import (
+ "crypto/sha512"
+ "encoding/base64"
+ "encoding/binary"
"errors"
"strconv"
)
@@ -106,3 +109,18 @@ func BytesLenSplit(s []byte, n int) [][]byte {
}
return ss
}
+
+// HashInts returns a string hash of the given ints.
+func HashInts(ints []int) string {
+ if len(ints) == 0 {
+ return ""
+ }
+ hsh := sha512.New()
+ buf := make([]byte, binary.MaxVarintLen64)
+ for _, i := range ints {
+ wroteLen := binary.PutVarint(buf, int64(i))
+ hsh.Write(buf[:wroteLen])
+ }
+ bts := hsh.Sum(nil)
+ return base64.RawURLEncoding.EncodeToString(bts)
+}
diff --git a/traffic_ops/ort/atstccfg/cachedotconfig.go
b/traffic_ops/ort/atstccfg/cachedotconfig.go
index 87e6554..8c3e772 100644
--- a/traffic_ops/ort/atstccfg/cachedotconfig.go
+++ b/traffic_ops/ort/atstccfg/cachedotconfig.go
@@ -54,7 +54,7 @@ func GetConfigFileProfileCacheDotConfig(cfg TCCfg,
profileNameOrID string) (stri
profileServerIDsMap[sv.ID] = struct{}{}
}
- dsServers, err := GetDeliveryServiceServers(cfg, profileServerIDs)
+ dsServers, err := GetDeliveryServiceServers(cfg, nil, profileServerIDs)
if err != nil {
return "", errors.New("getting parent.config cachegroup parent
server delivery service servers: " + err.Error())
}
diff --git a/traffic_ops/ort/atstccfg/parentdotconfig.go
b/traffic_ops/ort/atstccfg/parentdotconfig.go
index b44a50a..04e985e 100644
--- a/traffic_ops/ort/atstccfg/parentdotconfig.go
+++ b/traffic_ops/ort/atstccfg/parentdotconfig.go
@@ -200,7 +200,7 @@ func GetConfigFileServerParentDotConfig(cfg TCCfg,
serverNameOrID string) (strin
}
cgServerIDs = append(cgServerIDs, server.ID)
- cgDSServers, err := GetDeliveryServiceServers(cfg, cgServerIDs)
+ cgDSServers, err := GetDeliveryServiceServers(cfg, nil, cgServerIDs)
if err != nil {
return "", errors.New("getting parent.config cachegroup parent
server delivery service servers: " + err.Error())
}
diff --git a/traffic_ops/ort/atstccfg/remapdotconfig.go
b/traffic_ops/ort/atstccfg/remapdotconfig.go
new file mode 100755
index 0000000..d0809d2
--- /dev/null
+++ b/traffic_ops/ort/atstccfg/remapdotconfig.go
@@ -0,0 +1,372 @@
+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 (
+ "errors"
+ "sort"
+ "strconv"
+ "strings"
+
+ "github.com/apache/trafficcontrol/lib/go-atscfg"
+ "github.com/apache/trafficcontrol/lib/go-log"
+ "github.com/apache/trafficcontrol/lib/go-tc"
+ "github.com/apache/trafficcontrol/lib/go-util"
+)
+
+func GetConfigFileServerRemapDotConfig(cfg TCCfg, serverNameOrID string)
(string, error) {
+ // TODO TOAPI add /servers?cdn=1 query param
+ servers, err := GetServers(cfg)
+ if err != nil {
+ return "", errors.New("getting servers: " + err.Error())
+ }
+
+ server := tc.Server{ID: atscfg.InvalidID}
+ if serverID, err := strconv.Atoi(serverNameOrID); err == nil {
+ for _, toServer := range servers {
+ if toServer.ID == serverID {
+ server = toServer
+ break
+ }
+ }
+ } else {
+ serverName := serverNameOrID
+ for _, toServer := range servers {
+ if toServer.HostName == serverName {
+ server = toServer
+ break
+ }
+ }
+ }
+ if server.ID == atscfg.InvalidID {
+ return "", errors.New("server '" + serverNameOrID + " not found
in servers")
+ }
+
+ serverName := server.HostName
+
+ cdn, err := GetCDN(cfg, tc.CDNName(server.CDNName))
+ if err != nil {
+ return "", errors.New("getting cdn '" + string(server.CDNName)
+ "': " + err.Error())
+ }
+
+ serverCDNDomain := cdn.DomainName
+
+ toToolName, toURL, err := GetTOToolNameAndURLFromTO(cfg)
+ if err != nil {
+ return "", errors.New("getting global parameters: " +
err.Error())
+ }
+
+ serverProfileParameters, err := GetServerProfileParameters(cfg,
server.Profile)
+ if err != nil {
+ return "", errors.New("getting server profile '" +
server.Profile + "' parameters: " + err.Error())
+ }
+
+ atsVersionParam := ""
+ for _, param := range serverProfileParameters {
+ if param.ConfigFile != "package" || param.Name !=
"trafficserver" {
+ continue
+ }
+ atsVersionParam = param.Value
+ break
+ }
+ if atsVersionParam == "" {
+ atsVersionParam = atscfg.DefaultATSVersion
+ }
+
+ atsMajorVer, err :=
atscfg.GetATSMajorVersionFromATSVersion(atsVersionParam)
+ if err != nil {
+ return "", errors.New("getting ATS major version from version
parameter (profile '" + server.Profile + "' configFile 'package' name
'trafficserver'): " + err.Error())
+ }
+
+ deliveryServices, err := GetCDNDeliveryServices(cfg, server.CDNID)
+ if err != nil {
+ return "", errors.New("getting delivery services: " +
err.Error())
+ }
+
+ dsIDs := []int{}
+ for _, ds := range deliveryServices {
+ if ds.ID == nil {
+ // TODO log error?
+ continue
+ }
+ dsIDs = append(dsIDs, *ds.ID)
+ }
+
+ isMid := strings.HasPrefix(server.Type, string(tc.CacheTypeMid))
+
+ serverIDs := ([]int)(nil)
+ if !isMid {
+ // mids use all servers, so pass nil=all. Edges only use this
current server
+ serverIDs = append(serverIDs, server.ID)
+ }
+
+ dsServers, err := GetDeliveryServiceServers(cfg, dsIDs, serverIDs)
+ if err != nil {
+ return "", errors.New("getting parent.config cachegroup parent
server delivery service servers: " + err.Error())
+ }
+
+ dssMap := map[int]map[int]struct{}{} // set of map[dsID][serverID]
+ for _, dss := range dsServers {
+ if dss.Server == nil || dss.DeliveryService == nil {
+ continue // TODO log?
+ }
+ if dssMap[*dss.DeliveryService] == nil {
+ dssMap[*dss.DeliveryService] = map[int]struct{}{}
+ }
+ dssMap[*dss.DeliveryService][*dss.Server] = struct{}{}
+ }
+
+ useInactive := false
+ if !isMid {
+ // mids get inactive DSes, edges don't. This is how it's always
behaved, not necessarily how it should.
+ useInactive = true
+ }
+
+ filteredDSes := []tc.DeliveryServiceNullable{}
+ for _, ds := range deliveryServices {
+ if ds.ID == nil {
+ continue // TODO log?
+ }
+ if ds.Active == nil {
+ continue // TODO log?
+ }
+ if _, ok := dssMap[*ds.ID]; !ok {
+ continue
+ }
+ if !useInactive && !*ds.Active {
+ continue
+ }
+ filteredDSes = append(filteredDSes, ds)
+ }
+
+ dsRegexes, err := GetDeliveryServiceRegexes(cfg)
+ if err != nil {
+ return "", errors.New("getting delivery service regexes: " +
err.Error())
+ }
+
+ dsRegexMap := map[tc.DeliveryServiceName][]tc.DeliveryServiceRegex{}
+ for _, dsRegex := range dsRegexes {
+
sort.Sort(DeliveryServiceRegexesSortByTypeThenSetNum(dsRegex.Regexes))
+ dsRegexMap[tc.DeliveryServiceName(dsRegex.DSName)] =
dsRegex.Regexes
+ }
+
+ remapConfigDSData := []atscfg.RemapConfigDSData{}
+ for _, ds := range filteredDSes {
+ if ds.ID == nil || ds.Type == nil || ds.XMLID == nil || ds.DSCP
== nil || ds.Active == nil {
+ continue // TODO log error?
+ }
+ // TODO sort by DS ID? the old Perl query does, but it
shouldn't be necessary, except for determinism.
+ // TODO warn if no regexes?
+ for _, dsRegex := range
dsRegexMap[tc.DeliveryServiceName(*ds.XMLID)] {
+ remapConfigDSData = append(remapConfigDSData,
atscfg.RemapConfigDSData{
+ ID: *ds.ID,
+ Type: *ds.Type,
+ OriginFQDN: ds.OrgServerFQDN,
+ MidHeaderRewrite: ds.MidHeaderRewrite,
+ CacheURL: ds.CacheURL,
+ RangeRequestHandling:
ds.RangeRequestHandling,
+ RemapText: ds.RemapText,
+ EdgeHeaderRewrite: ds.EdgeHeaderRewrite,
+ SigningAlgorithm: ds.SigningAlgorithm,
+ Name: *ds.XMLID,
+ QStringIgnore: ds.QStringIgnore,
+ RegexRemap: ds.RegexRemap,
+ FQPacingRate: ds.FQPacingRate,
+ DSCP: *ds.DSCP,
+ RoutingName: ds.RoutingName,
+ Pattern:
util.StrPtr(dsRegex.Pattern),
+ RegexType:
util.StrPtr(dsRegex.Type),
+ Domain:
util.StrPtr(serverCDNDomain), // note this is intentionally the CDN domain, not
the DS or Server Domain. Must be the remap domain.
+ OriginShield: ds.OriginShield,
+ ProfileID: ds.ProfileID,
+ Protocol: ds.Protocol,
+ AnonymousBlockingEnabled:
ds.AnonymousBlockingEnabled,
+ Active: *ds.Active,
+ })
+ }
+ }
+
+ serverProfileParams, err := GetProfileParameters(cfg, server.Profile)
+ if err != nil {
+ return "", errors.New("getting profile parameters from server
(profile '" + server.Profile + ": " + err.Error())
+ }
+
+ serverPackageParamData := map[string]string{}
+ for _, param := range serverProfileParams {
+ if param.ConfigFile != "package" { // TODO put in const
+ continue
+ }
+
+ if param.Name == "location" { // TODO put in const
+ continue
+ }
+
+ paramName := param.Name
+ // some files have multiple lines with the same key... handle
that with param id.
+ if _, ok := serverPackageParamData[param.Name]; ok {
+ paramName += "__" + strconv.Itoa(param.ID)
+ }
+ paramValue := param.Value
+ if paramValue == "STRING __HOSTNAME__" {
+ paramValue = server.HostName + "." + server.DomainName
// TODO strings.Replace to replace all anywhere, instead of just an exact match?
+ }
+ serverPackageParamData[paramName] = paramValue
+ }
+
+ cacheURLParams := map[string]string{}
+ for _, param := range serverProfileParams {
+ if param.ConfigFile != atscfg.CacheURLParameterConfigFile {
+ continue
+ }
+ if existingVal, ok := cacheURLParams[param.Name]; ok {
+ log.Warnln("generating remap.config: server profile '"
+ server.Profile + "' cacheurl.config has multiple parameters for '" +
param.Name + "' - using '" + existingVal + "' and ignoring the rest!")
+ continue
+ }
+ cacheURLParams[param.Name] = param.Value
+ }
+
+ cacheKeyParams, err := GetConfigFileParameters(cfg,
atscfg.CacheKeyParameterConfigFile)
+ if err != nil {
+ return "", errors.New("getting cache key parameters: " +
err.Error())
+ }
+
+ cacheKeyParamsWithProfiles, err :=
TCParamsToParamsWithProfiles(cacheKeyParams)
+ if err != nil {
+ return "", errors.New("decoding cache key parameter profiles: "
+ err.Error())
+ }
+
+ cacheKeyParamsWithProfilesMap :=
ParameterWithProfilesToMap(cacheKeyParamsWithProfiles)
+
+ dsProfileNamesToIDs := map[string]int{}
+ for _, ds := range filteredDSes {
+ if ds.ProfileID == nil || ds.ProfileName == nil {
+ continue // TODO log
+ }
+ dsProfileNamesToIDs[*ds.ProfileName] = *ds.ProfileID
+ }
+
+ dsProfilesCacheKeyConfigParams := map[int]map[string]string{}
+ for _, param := range cacheKeyParamsWithProfilesMap {
+ for dsProfileName, dsProfileID := range dsProfileNamesToIDs {
+ if _, ok := param.ProfileNames[dsProfileName]; ok {
+ if _, ok :=
dsProfilesCacheKeyConfigParams[dsProfileID]; !ok {
+
dsProfilesCacheKeyConfigParams[dsProfileID] = map[string]string{}
+ }
+ if _, ok :=
dsProfilesCacheKeyConfigParams[dsProfileID][param.Name]; ok {
+ // TODO warn
+ continue
+ }
+
dsProfilesCacheKeyConfigParams[dsProfileID][param.Name] = param.Value
+ }
+ }
+ }
+
+ // TODO get dses first, so we can get the profile names-to-IDs without
fetching all profiles
+
+ // TODO put parentcg logic in func, to remove duplication with
parent.config
+
+ cacheGroups, err := GetCacheGroups(cfg)
+ if err != nil {
+ return "", errors.New("getting cachegroups: " + err.Error())
+ }
+
+ cgMap := map[string]tc.CacheGroupNullable{}
+ for _, cg := range cacheGroups {
+ if cg.Name == nil {
+ return "", errors.New("got cachegroup with nil name!'")
+ }
+ cgMap[*cg.Name] = cg
+ }
+
+ serverCG, ok := cgMap[server.Cachegroup]
+ if !ok {
+ return "", errors.New("server '" + serverNameOrID + "'
cachegroup '" + server.Cachegroup + "' not found in CacheGroups")
+ }
+
+ parentCGID := -1
+ parentCGType := ""
+ if serverCG.ParentName != nil && *serverCG.ParentName != "" {
+ parentCG, ok := cgMap[*serverCG.ParentName]
+ if !ok {
+ return "", errors.New("server '" + serverNameOrID + "'
cachegroup '" + server.Cachegroup + "' parent '" + *serverCG.ParentName + "'
not found in CacheGroups")
+ }
+ if parentCG.ID == nil {
+ return "", errors.New("got cachegroup '" +
*parentCG.Name + "' with nil ID!'")
+ }
+ parentCGID = *parentCG.ID
+
+ if parentCG.Type == nil {
+ return "", errors.New("got cachegroup '" +
*parentCG.Name + "' with nil Type!'")
+ }
+ parentCGType = *parentCG.Type
+ }
+
+ secondaryParentCGID := -1
+ secondaryParentCGType := ""
+ if serverCG.SecondaryParentName != nil && *serverCG.SecondaryParentName
!= "" {
+ parentCG, ok := cgMap[*serverCG.SecondaryParentName]
+ if !ok {
+ return "", errors.New("server '" + serverNameOrID + "'
cachegroup '" + server.Cachegroup + "' secondary parent '" +
*serverCG.SecondaryParentName + "' not found in CacheGroups")
+ }
+
+ if parentCG.ID == nil {
+ return "", errors.New("got cachegroup '" +
*parentCG.Name + "' with nil ID!'")
+ }
+ secondaryParentCGID = *parentCG.ID
+ if parentCG.Type == nil {
+ return "", errors.New("got cachegroup '" +
*parentCG.Name + "' with nil Type!'")
+ }
+
+ secondaryParentCGType = *parentCG.Type
+ }
+
+ serverInfo := &atscfg.ServerInfo{
+ CacheGroupID: server.CachegroupID,
+ CDN: tc.CDNName(server.CDNName),
+ CDNID: server.CDNID,
+ DomainName: serverCDNDomain, // note this is
intentionally the CDN domain, not the server domain. It's what's remapped to.
+ HostName: server.HostName,
+ ID: server.ID,
+ IP: server.IPAddress,
+ ParentCacheGroupID: parentCGID,
+ ParentCacheGroupType: parentCGType,
+ ProfileID:
atscfg.ProfileID(server.ProfileID),
+ ProfileName: server.Profile,
+ Port: server.TCPPort,
+ HTTPSPort: server.HTTPSPort,
+ SecondaryParentCacheGroupID: secondaryParentCGID,
+ SecondaryParentCacheGroupType: secondaryParentCGType,
+ Type: server.Type,
+ }
+
+ txt := atscfg.MakeRemapDotConfig(tc.CacheName(serverName), toToolName,
toURL, atsMajorVer, cacheURLParams, dsProfilesCacheKeyConfigParams,
serverPackageParamData, serverInfo, remapConfigDSData)
+ return txt, nil
+}
+
+type DeliveryServiceRegexesSortByTypeThenSetNum []tc.DeliveryServiceRegex
+
+func (r DeliveryServiceRegexesSortByTypeThenSetNum) Len() int { return len(r) }
+func (r DeliveryServiceRegexesSortByTypeThenSetNum) Less(i, j int) bool {
+ if rc := strings.Compare(r[i].Type, r[j].Type); rc != 0 {
+ return rc < 0
+ }
+ return r[i].SetNumber < r[j].SetNumber
+}
+func (r DeliveryServiceRegexesSortByTypeThenSetNum) Swap(i, j int) { r[i],
r[j] = r[j], r[i] }
diff --git a/traffic_ops/ort/atstccfg/routing.go
b/traffic_ops/ort/atstccfg/routing.go
index 5d67a42..27e3086 100644
--- a/traffic_ops/ort/atstccfg/routing.go
+++ b/traffic_ops/ort/atstccfg/routing.go
@@ -125,6 +125,7 @@ func ProfileConfigFileFuncs() map[string]func(cfg TCCfg,
serverNameOrID string)
func ServerConfigFileFuncs() map[string]func(cfg TCCfg, serverNameOrID string)
(string, error) {
return map[string]func(cfg TCCfg, serverNameOrID string) (string,
error){
"parent.config": GetConfigFileServerParentDotConfig,
+ "remap.config": GetConfigFileServerRemapDotConfig,
}
}
diff --git a/traffic_ops/ort/atstccfg/toreq.go
b/traffic_ops/ort/atstccfg/toreq.go
index 56f50e0..76e4935 100644
--- a/traffic_ops/ort/atstccfg/toreq.go
+++ b/traffic_ops/ort/atstccfg/toreq.go
@@ -23,10 +23,10 @@ import (
"errors"
"sort"
"strconv"
- "strings"
"github.com/apache/trafficcontrol/lib/go-log"
"github.com/apache/trafficcontrol/lib/go-tc"
+ "github.com/apache/trafficcontrol/lib/go-util"
)
func GetProfile(cfg TCCfg, profileID int) (tc.Profile, error) {
@@ -171,22 +171,25 @@ func GetCacheGroups(cfg TCCfg) ([]tc.CacheGroupNullable,
error) {
return cacheGroups, nil
}
-func GetDeliveryServiceServers(cfg TCCfg, serverIDs []int)
([]tc.DeliveryServiceServer, error) {
+func GetDeliveryServiceServers(cfg TCCfg, dsIDs []int, serverIDs []int)
([]tc.DeliveryServiceServer, error) {
serverIDsSorted := make([]int, 0, len(serverIDs))
for _, id := range serverIDs {
serverIDsSorted = append(serverIDsSorted, id)
}
sort.Ints(serverIDsSorted)
- serverIDStrs := []string{}
- for _, id := range serverIDs {
- serverIDStrs = append(serverIDStrs, strconv.Itoa(id))
+ dsIDsSorted := make([]int, 0, len(dsIDs))
+ for _, id := range dsIDs {
+ dsIDsSorted = append(dsIDsSorted, id)
}
- serverIDsStr := strings.Join(serverIDStrs, "-")
+ sort.Ints(dsIDsSorted)
+
+ serverIDsStr := util.HashInts(serverIDsSorted)
+ dsIDsStr := util.HashInts(dsIDsSorted)
dsServers := []tc.DeliveryServiceServer{}
// TODO make this filename shorter (but still unique) somehow. The
filename is almost always too long.
- err := GetCachedJSON(cfg,
"deliveryservice_servers_"+serverIDsStr+".json", &dsServers, func(obj
interface{}) error {
+ err := GetCachedJSON(cfg,
"deliveryservice_servers_d_"+dsIDsStr+"_s_"+serverIDsStr+".json", &dsServers,
func(obj interface{}) error {
const noLimit = 999999 // TODO add "no limit" param to DSS
endpoint
toDSS, reqInf, err :=
(*cfg.TOClient).GetDeliveryServiceServersWithLimits(noLimit, nil, serverIDs)
if err != nil {
@@ -343,3 +346,20 @@ func GetParametersByName(cfg TCCfg, paramName string)
([]tc.Parameter, error) {
}
return params, nil
}
+
+func GetDeliveryServiceRegexes(cfg TCCfg) ([]tc.DeliveryServiceRegexes, error)
{
+ regexes := []tc.DeliveryServiceRegexes{}
+ err := GetCachedJSON(cfg, "ds_regexes.json", ®exes, func(obj
interface{}) error {
+ toRegexes, reqInf, err :=
(*cfg.TOClient).GetDeliveryServiceRegexes()
+ if err != nil {
+ return errors.New("getting ds regexes from Traffic Ops
'" + MaybeIPStr(reqInf) + "': " + err.Error())
+ }
+ regexes := obj.(*[]tc.DeliveryServiceRegexes)
+ *regexes = toRegexes
+ return nil
+ })
+ if err != nil {
+ return nil, errors.New("getting ds regexes: " + err.Error())
+ }
+ return regexes, nil
+}
diff --git a/traffic_ops/ort/atstccfg/trafficops.go
b/traffic_ops/ort/atstccfg/trafficops.go
index a503b37..15719a8 100644
--- a/traffic_ops/ort/atstccfg/trafficops.go
+++ b/traffic_ops/ort/atstccfg/trafficops.go
@@ -282,3 +282,20 @@ type ParameterWithProfiles struct {
tc.Parameter
ProfileNames []string
}
+
+type ParameterWithProfilesMap struct {
+ tc.Parameter
+ ProfileNames map[string]struct{}
+}
+
+func ParameterWithProfilesToMap(tcParams []ParameterWithProfiles)
[]ParameterWithProfilesMap {
+ params := []ParameterWithProfilesMap{}
+ for _, tcParam := range tcParams {
+ param := ParameterWithProfilesMap{Parameter: tcParam.Parameter,
ProfileNames: map[string]struct{}{}}
+ for _, profile := range tcParam.ProfileNames {
+ param.ProfileNames[profile] = struct{}{}
+ }
+ params = append(params, param)
+ }
+ return params
+}
diff --git a/traffic_ops/testing/api/v14/parentdotconfig_test.go
b/traffic_ops/testing/api/v14/parentdotconfig_test.go
index 909e01b..1321f9a 100644
--- a/traffic_ops/testing/api/v14/parentdotconfig_test.go
+++ b/traffic_ops/testing/api/v14/parentdotconfig_test.go
@@ -20,8 +20,6 @@ import (
"strconv"
"strings"
"testing"
-
- "github.com/apache/trafficcontrol/lib/go-log"
)
func TestParentDotConfig(t *testing.T) {
@@ -71,8 +69,6 @@ func GetTestParentDotConfig(t *testing.T) {
}
func CreateTestDeliveryServiceServers(t *testing.T) {
- log.Debugln("DeleteTestDeliveryServiceServers")
-
dses, _, err := TOSession.GetDeliveryServices()
if err != nil {
t.Errorf("cannot GET DeliveryServices: %v\n", err)
@@ -80,7 +76,6 @@ func CreateTestDeliveryServiceServers(t *testing.T) {
if len(dses) < 1 {
t.Errorf("GET DeliveryServices returned no dses, must have at
least 1 to test ds-servers")
}
- ds := dses[0]
servers, _, err := TOSession.GetServers()
if err != nil {
@@ -89,11 +84,17 @@ func CreateTestDeliveryServiceServers(t *testing.T) {
if len(servers) < 1 {
t.Errorf("GET Servers returned no dses, must have at least 1 to
test ds-servers")
}
- server := servers[0]
- _, err = TOSession.CreateDeliveryServiceServers(ds.ID,
[]int{server.ID}, true)
- if err != nil {
- t.Errorf("POST delivery service servers: %v\n", err)
+ for _, ds := range dses {
+ serverIDs := []int{}
+ for _, server := range servers {
+ serverIDs = append(serverIDs, server.ID)
+ }
+
+ _, err = TOSession.CreateDeliveryServiceServers(ds.ID,
serverIDs, true)
+ if err != nil {
+ t.Errorf("POST delivery service servers: %v\n", err)
+ }
}
}
@@ -117,7 +118,7 @@ func DeleteTestDeliveryServiceServersCreated(t *testing.T) {
}
server := servers[0]
- dsServers, _, err := TOSession.GetDeliveryServiceServers()
+ dsServers, _, err := TOSession.GetDeliveryServiceServersN(1000000)
if err != nil {
t.Errorf("GET delivery service servers: %v\n", err)
}
diff --git a/traffic_ops/testing/api/v14/remapdotconfig_test.go
b/traffic_ops/testing/api/v14/remapdotconfig_test.go
new file mode 100644
index 0000000..80a3bc6
--- /dev/null
+++ b/traffic_ops/testing/api/v14/remapdotconfig_test.go
@@ -0,0 +1,95 @@
+package v14
+
+/*
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+import (
+ "net/url"
+ "strconv"
+ "strings"
+ "testing"
+
+ "github.com/apache/trafficcontrol/lib/go-tc"
+)
+
+func TestRemapDotConfig(t *testing.T) {
+ WithObjs(t, []TCObj{CDNs, Types, Tenants, Parameters, Profiles,
Statuses, Divisions, Regions, PhysLocations, CacheGroups, Servers,
DeliveryServices}, func() {
+ defer DeleteTestDeliveryServiceServersCreated(t)
+ CreateTestDeliveryServiceServers(t)
+ GetTestRemapDotConfig(t)
+ })
+}
+
+func GetTestRemapDotConfig(t *testing.T) {
+ dsServers, _, err := TOSession.GetDeliveryServiceServers()
+ if err != nil {
+ t.Fatalf("GET delivery service servers: %v\n", err)
+ } else if len(dsServers.Response) == 0 {
+ t.Fatalf("GET delivery service servers: no servers found\n")
+ } else if dsServers.Response[0].Server == nil {
+ t.Fatalf("GET delivery service servers: returned nil server\n")
+ } else if dsServers.Response[0].DeliveryService == nil {
+ t.Fatalf("GET delivery service servers: returned nil ds\n")
+ }
+ serverID := *dsServers.Response[0].Server
+
+ ds := (*tc.DeliveryService)(nil)
+ for _, dsServer := range dsServers.Response {
+ ds, _, err =
TOSession.GetDeliveryService(strconv.Itoa(*dsServer.DeliveryService))
+ if err != nil {
+ t.Fatalf("Getting ds %+v: "+err.Error()+"\n",
*dsServers.Response[0].DeliveryService)
+ } else if ds == nil {
+ t.Fatalf("Getting ds %+v: "+"got nil response"+"\n",
*dsServers.Response[0].DeliveryService)
+ } else if ds.OrgServerFQDN == "" {
+ t.Fatalf("Getting ds %+v: "+"got empty
ds.OrgServerFQDN"+"\n", *dsServers.Response[0].DeliveryService)
+ }
+ if ds.Type == tc.DSTypeAnyMap {
+ continue
+ }
+ break
+ }
+ if ds == nil || ds.XMLID == "" {
+ t.Fatalf("no Delivery Service found with assigned servers that
isn't an ANY_MAP service, can't test remap.config")
+ }
+
+ originURI, err := url.Parse(ds.OrgServerFQDN)
+ if err != nil {
+ t.Fatalf("Getting ds %+v: "+" ds.OrgServerFQDN '%+v' failed to
parse as a URL: %+v\n", *dsServers.Response[0].DeliveryService,
ds.OrgServerFQDN, err)
+ }
+ originHost := originURI.Hostname()
+
+ remapDotConfig, _, err := TOSession.GetATSServerConfig(serverID,
"remap.config")
+ if err != nil {
+ t.Fatalf("Getting server %+v config remap.config:
"+err.Error()+"\n", serverID)
+ }
+
+ if !strings.Contains(remapDotConfig, originHost) {
+ t.Errorf("expected: remap.config to contain delivery service
origin FQDN '%+v' host '%+v', actual: '''%+v'''", ds.OrgServerFQDN, originHost,
remapDotConfig)
+ }
+
+ remapDotConfigLines := strings.Split(remapDotConfig, "\n")
+ for i, line := range remapDotConfigLines {
+ line = strings.TrimSpace(line)
+ if len(line) == 0 {
+ continue
+ }
+ if line[0] == '#' {
+ continue
+ }
+ if !strings.HasPrefix(line, "map") {
+ t.Errorf("expected: remap.config line %v to start with
'map', actual: '%v'\n", i, line)
+ }
+ }
+}
diff --git a/traffic_ops/testing/api/v14/tc-fixtures.json
b/traffic_ops/testing/api/v14/tc-fixtures.json
index e0eb4e0..1e1e647 100644
--- a/traffic_ops/testing/api/v14/tc-fixtures.json
+++ b/traffic_ops/testing/api/v14/tc-fixtures.json
@@ -481,7 +481,7 @@
"rangeRequestHandling": 0,
"regexRemap": "",
"regionalGeoBlocking": false,
- "remapText": "some raw remap text",
+ "remapText": "map some raw remap text",
"routingName": "",
"signed": false,
"signingAlgorithm": "url_sig",
diff --git a/traffic_ops/traffic_ops_golang/ats/meta.go
b/traffic_ops/traffic_ops_golang/ats/atsserver/meta.go
similarity index 64%
rename from traffic_ops/traffic_ops_golang/ats/meta.go
rename to traffic_ops/traffic_ops_golang/ats/atsserver/meta.go
index c2dbe9c..d675b7e 100644
--- a/traffic_ops/traffic_ops_golang/ats/meta.go
+++ b/traffic_ops/traffic_ops_golang/ats/atsserver/meta.go
@@ -1,4 +1,4 @@
-package ats
+package atsserver
/*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -28,6 +28,7 @@ import (
"github.com/apache/trafficcontrol/lib/go-log"
"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
)
func GetConfigMetaData(w http.ResponseWriter, r *http.Request) {
@@ -38,13 +39,13 @@ func GetConfigMetaData(w http.ResponseWriter, r
*http.Request) {
}
defer inf.Close()
- serverName, userErr, sysErr, errCode :=
getServerNameFromNameOrID(inf.Tx.Tx, inf.Params["server-name-or-id"])
+ serverName, userErr, sysErr, errCode :=
ats.GetServerNameFromNameOrID(inf.Tx.Tx, inf.Params["server-name-or-id"])
if userErr != nil || sysErr != nil {
api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
return
}
- server, ok, err := getServerInfoByHost(inf.Tx.Tx, serverName)
+ server, ok, err := ats.GetServerInfoByHost(inf.Tx.Tx, serverName)
if err != nil {
api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("GetConfigMetaData getting server info: "+err.Error()))
return
@@ -53,7 +54,7 @@ func GetConfigMetaData(w http.ResponseWriter, r
*http.Request) {
return
}
- tmParams, err := GetTMParams(inf.Tx.Tx)
+ tmParams, err := ats.GetTMParams(inf.Tx.Tx)
if err != nil {
api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("GetConfigMetaData getting tm.url parameter: "+err.Error()))
return
@@ -78,7 +79,7 @@ func GetConfigMetaData(w http.ResponseWriter, r
*http.Request) {
ConfigFiles: []tc.ATSConfigMetaDataConfigFile{},
}
- locationParams, err := GetLocationParams(inf.Tx.Tx,
int(server.ProfileID))
+ locationParams, err := ats.GetLocationParams(inf.Tx.Tx,
int(server.ProfileID))
if err != nil {
api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("GetConfigMetaData getting location parameters: "+err.Error()))
return
@@ -86,7 +87,7 @@ func GetConfigMetaData(w http.ResponseWriter, r
*http.Request) {
if locationParams["remap.config"].Location != "" {
configLocation := locationParams["remap.config"].Location
- uriSignedDSes, err := GetServerURISignedDSes(inf.Tx.Tx,
server.HostName, server.Port)
+ uriSignedDSes, err := ats.GetServerURISignedDSes(inf.Tx.Tx,
server.HostName, server.Port)
if err != nil {
api.HandleErr(w, r, inf.Tx.Tx,
http.StatusInternalServerError, nil, errors.New("GetConfigMetaData getting
server uri-signed dses: "+err.Error()))
return
@@ -137,104 +138,6 @@ func GetConfigMetaData(w http.ResponseWriter, r
*http.Request) {
api.WriteRespRaw(w, r, atsData)
}
-// GetTMParams returns the global "tm.url" and "tm.rev_proxy.url" parameters,
and any error. If either param doesn't exist, an empty string is returned
without error.
-func GetTMParams(tx *sql.Tx) (TMParams, error) {
- rows, err := tx.Query(`SELECT name, value from parameter where
config_file = 'global' AND (name = 'tm.url' OR name = 'tm.rev_proxy.url')`)
- if err != nil {
- return TMParams{}, errors.New("querying: " + err.Error())
- }
- defer rows.Close()
-
- p := TMParams{}
- for rows.Next() {
- name := ""
- val := ""
- if err := rows.Scan(&name, &val); err != nil {
- return TMParams{}, errors.New("scanning: " +
err.Error())
- }
- if name == "tm.url" {
- p.URL = val
- } else if name == "tm.rev_proxy.url" {
- p.ReverseProxyURL = val
- } else {
- return TMParams{}, errors.New("querying got unexpected
parameter: " + name + " (value: '" + val + "')") // should never happen
- }
- }
- return p, nil
-}
-
-// GetLocationParams returns a map[configFile]locationParams, and any error.
If either param doesn't exist, an empty string is returned without error.
-func GetLocationParams(tx *sql.Tx, profileID int)
(map[string]ConfigProfileParams, error) {
- qry := `
-SELECT
- p.name,
- p.config_file,
- p.value
-FROM
- parameter p
- JOIN profile_parameter pp ON pp.parameter = p.id
-WHERE
- pp.profile = $1
-`
- rows, err := tx.Query(qry, profileID)
- if err != nil {
- return nil, errors.New("querying: " + err.Error())
- }
- defer rows.Close()
-
- params := map[string]ConfigProfileParams{}
- for rows.Next() {
- name := ""
- file := ""
- val := ""
- if err := rows.Scan(&name, &file, &val); err != nil {
- return nil, errors.New("scanning: " + err.Error())
- }
- if name == "location" {
- p := params[file]
- p.FileNameOnDisk = file
- p.Location = val
- params[file] = p
- } else if name == "URL" {
- p := params[file]
- p.URL = val
- params[file] = p
- }
- }
- return params, nil
-}
-
-// GetServerURISignedDSes returns a list of delivery service names which have
the given server assigned and have URI signing enabled, and any error.
-func GetServerURISignedDSes(tx *sql.Tx, serverHostName string, serverPort int)
([]tc.DeliveryServiceName, error) {
- qry := `
-SELECT
- ds.xml_id
-FROM
- deliveryservice ds
- JOIN deliveryservice_server dss ON ds.id = dss.deliveryservice
- JOIN server s ON s.id = dss.server
-WHERE
- s.host_name = $1
- AND s.tcp_port = $2
- AND ds.signing_algorithm = 'uri_signing'
-`
- rows, err := tx.Query(qry, serverHostName, serverPort)
- if err != nil {
- return nil, errors.New("querying: " + err.Error())
- }
- defer rows.Close()
-
- dses := []tc.DeliveryServiceName{}
- for rows.Next() {
- ds := tc.DeliveryServiceName("")
- if err := rows.Scan(&ds); err != nil {
- return nil, errors.New("scanning: " + err.Error())
- }
- dses = append(dses, ds)
- }
- return dses, nil
-}
-
func getServerScope(tx *sql.Tx, cfgFile string, serverType string)
(tc.ATSConfigMetaDataConfigFileScope, error) {
switch {
case cfgFile == "cache.config" && tc.CacheTypeFromString(serverType) ==
tc.CacheTypeMid:
@@ -306,7 +209,7 @@ func getScope(tx *sql.Tx, cfgFile string)
(tc.ATSConfigMetaDataConfigFileScope,
return tc.ATSConfigMetaDataConfigFileScopeCDNs, nil
}
- scope, ok, err := GetFirstScopeParameter(tx, cfgFile)
+ scope, ok, err := ats.GetFirstScopeParameter(tx, cfgFile)
if err != nil {
return tc.ATSConfigMetaDataConfigFileScopeInvalid,
errors.New("getting scope parameter: " + err.Error())
}
@@ -315,27 +218,3 @@ func getScope(tx *sql.Tx, cfgFile string)
(tc.ATSConfigMetaDataConfigFileScope,
}
return tc.ATSConfigMetaDataConfigFileScope(scope), nil
}
-
-type TMParams struct {
- URL string
- ReverseProxyURL string
-}
-
-type ConfigProfileParams struct {
- FileNameOnDisk string
- Location string
- URL string
- APIURI string
-}
-
-// GetFirstScopeParameter returns the value of the arbitrarily-first parameter
with the name 'scope' and the given config file, whether a parameter was found,
and any error.
-func GetFirstScopeParameter(tx *sql.Tx, cfgFile string) (string, bool, error) {
- v := ""
- if err := tx.QueryRow(`SELECT p.value FROM parameter p WHERE
p.config_file = $1 AND p.name = 'scope'`, cfgFile).Scan(&v); err != nil {
- if err == sql.ErrNoRows {
- return "", false, nil
- }
- return "", false, errors.New("querying first scope parameter: "
+ err.Error())
- }
- return v, true, nil
-}
diff --git a/traffic_ops/traffic_ops_golang/ats/parentdotconfig.go
b/traffic_ops/traffic_ops_golang/ats/atsserver/parentdotconfig.go
similarity index 99%
rename from traffic_ops/traffic_ops_golang/ats/parentdotconfig.go
rename to traffic_ops/traffic_ops_golang/ats/atsserver/parentdotconfig.go
index 5396e62..59c6647 100644
--- a/traffic_ops/traffic_ops_golang/ats/parentdotconfig.go
+++ b/traffic_ops/traffic_ops_golang/ats/atsserver/parentdotconfig.go
@@ -1,4 +1,4 @@
-package ats
+package atsserver
/*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -30,6 +30,7 @@ import (
"github.com/apache/trafficcontrol/lib/go-log"
"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
"github.com/lib/pq"
)
@@ -72,7 +73,7 @@ func GetParentDotConfig(w http.ResponseWriter, r
*http.Request) {
return
}
- toolName, toURL, err := GetToolNameAndURL(inf.Tx.Tx)
+ toolName, toURL, err := ats.GetToolNameAndURL(inf.Tx.Tx)
if err != nil {
api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("getting toolname and TO url parameters: "+err.Error()))
return
@@ -166,7 +167,7 @@ FROM
// GetATSMajorVersion returns the major version of the given profile's package
trafficserver parameter.
// If no parameter exists, this does not return an error, but rather logs a
warning and uses DefaultATSVersion.
func GetATSMajorVersion(tx *sql.Tx, serverProfileID int) (int, error) {
- atsVersion, _, err := GetProfileParamValue(tx, serverProfileID,
"package", "trafficserver")
+ atsVersion, _, err := ats.GetProfileParamValue(tx, serverProfileID,
"package", "trafficserver")
if err != nil {
return 0, errors.New("getting profile param value: " +
err.Error())
}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsserver/remapdotconfig.go
b/traffic_ops/traffic_ops_golang/ats/atsserver/remapdotconfig.go
new file mode 100644
index 0000000..502af24
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsserver/remapdotconfig.go
@@ -0,0 +1,93 @@
+package atsserver
+
+/*
+ * 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 (
+ "errors"
+ "io"
+ "net/http"
+
+ "github.com/apache/trafficcontrol/lib/go-atscfg"
+ "github.com/apache/trafficcontrol/lib/go-tc"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
+)
+
+func GetServerConfigRemap(w http.ResponseWriter, r *http.Request) {
+ inf, userErr, sysErr, errCode := api.NewInfo(r,
[]string{"server-name-or-id"}, nil)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+ return
+ }
+ defer inf.Close()
+
+ serverName, userErr, sysErr, errCode :=
ats.GetServerNameFromNameOrID(inf.Tx.Tx, inf.Params["server-name-or-id"])
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+ return
+ }
+
+ toToolName, toURL, err := ats.GetToolNameAndURL(inf.Tx.Tx)
+ if err != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("getting tool name and url: "+err.Error()))
+ return
+ }
+
+ atsMajorVersion, err := ats.GetATSMajorVersionFromServerName(inf.Tx.Tx,
serverName)
+ if err != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("getting ATS major version: "+err.Error()))
+ return
+ }
+
+ cacheURLConfigParams, err := ats.GetServerProfileParamData(inf.Tx.Tx,
serverName, "cacheurl.config")
+ if err != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("getting cacheurl.config params: "+err.Error()))
+ return
+ }
+
+ serverInfo, ok, err := ats.GetServerInfoByHost(inf.Tx.Tx, serverName)
+ if !ok {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound,
errors.New("server not found"), nil)
+ return
+ }
+
+ remapDSData, err := ats.GetRemapDSData(inf.Tx.Tx, serverInfo)
+ if err != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("Getting remap ds data: "+err.Error()))
+ return
+ }
+
+ dsProfilesCacheKeyConfigParams, err :=
ats.GetProfilesParamData(inf.Tx.Tx, atscfg.DSProfileIDs(remapDSData),
"cachekey.config")
+ if err != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("Getting profiles param data for cachekey: "+err.Error()))
+ return
+ }
+
+ serverPackageParamData, err := ats.GetServerParamData(inf.Tx.Tx,
int(serverInfo.ProfileID), "package", serverInfo.HostName,
serverInfo.DomainName)
+ if err != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("Getting server package param data: "+err.Error()))
+ return
+ }
+
+ txt := atscfg.MakeRemapDotConfig(serverName, toToolName, toURL,
atsMajorVersion, cacheURLConfigParams, dsProfilesCacheKeyConfigParams,
serverPackageParamData, serverInfo, remapDSData)
+
+ w.Header().Set(tc.ContentType, tc.ContentTypeTextPlain)
+ io.WriteString(w, txt)
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/config.go
b/traffic_ops/traffic_ops_golang/ats/config.go
index c0b999d..ca7c82f 100644
--- a/traffic_ops/traffic_ops_golang/ats/config.go
+++ b/traffic_ops/traffic_ops_golang/ats/config.go
@@ -106,29 +106,6 @@ func getCDNNameFromNameOrID(tx *sql.Tx, cdnNameOrID
string) (string, error, erro
return cdnName, nil, nil, http.StatusOK
}
-// getServerNameFromNameOrID returns the server name from a parameter which
may be the name or ID.
-// This also checks and verifies the existence of the given server, and
returns an appropriate user error if it doesn't exist.
-// Returns the name, any user error, any system error, and any error code.
-func getServerNameFromNameOrID(tx *sql.Tx, serverNameOrID string) (string,
error, error, int) {
- if serverID, err := strconv.Atoi(serverNameOrID); err == nil {
- serverName, ok, err := dbhelpers.GetServerNameFromID(tx,
int64(serverID))
- if err != nil {
- return "", nil, fmt.Errorf("getting server name from id
%v: %v", serverID, err), http.StatusInternalServerError
- } else if !ok {
- return "", errors.New("server not found"), nil,
http.StatusNotFound
- }
- return string(serverName), nil, nil, http.StatusOK
- }
-
- serverName := serverNameOrID
- if ok, err := dbhelpers.ServerExists(serverName, tx); err != nil {
- return "", nil, fmt.Errorf("checking server name '%v'
existence: %v", serverName, err), http.StatusInternalServerError
- } else if !ok {
- return "", errors.New("server not found"), nil,
http.StatusNotFound
- }
- return serverName, nil, nil, http.StatusOK
-}
-
func HeaderComment(tx *sql.Tx, name string) (string, error) {
nameVersionStr, err := GetNameVersionString(tx)
if err != nil {
diff --git a/traffic_ops/traffic_ops_golang/ats/db.go
b/traffic_ops/traffic_ops_golang/ats/db.go
index 90d3b87..69e5d56 100644
--- a/traffic_ops/traffic_ops_golang/ats/db.go
+++ b/traffic_ops/traffic_ops_golang/ats/db.go
@@ -22,12 +22,79 @@ package ats
import (
"database/sql"
"errors"
+ "fmt"
+ "net/http"
+ "strconv"
"github.com/apache/trafficcontrol/lib/go-atscfg"
"github.com/apache/trafficcontrol/lib/go-log"
"github.com/apache/trafficcontrol/lib/go-tc"
+
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
+
+ "github.com/lib/pq"
)
+// RemapDotConfigIncludeInactiveDeliveryServices is whether delivery services
with 'active' false are included in the remap.config.
+const RemapDotConfigIncludeInactiveDeliveryServices = true
+
+// getProfileData returns the necessary info about the profile, whether it
exists, and any error.
+func getProfileData(tx *sql.Tx, id int) (ProfileData, bool, error) {
+ qry := `
+SELECT
+ p.name
+FROM
+ profile p
+WHERE
+ p.id = $1
+`
+ v := ProfileData{ID: id}
+ if err := tx.QueryRow(qry, id).Scan(&v.Name); err != nil {
+ if err == sql.ErrNoRows {
+ return ProfileData{}, false, nil
+ }
+ return ProfileData{}, false, errors.New("querying: " +
err.Error())
+ }
+ return v, true, nil
+}
+
+// GetProfilesParamData returns a map[profileID][paramName]paramVal
+func GetProfilesParamData(tx *sql.Tx, profileIDs []int, configFile string)
(map[int]map[string]string, error) {
+ qry := `
+SELECT
+ pr.id,
+ p.name,
+ p.value
+FROM
+ profile pr
+ JOIN profile_parameter pp on pr.id = pp.profile
+ JOIN parameter p on p.id = pp.parameter
+WHERE
+ pr.id = ANY($1)
+ AND p.config_file = $2
+`
+
+ rows, err := tx.Query(qry, pq.Array(profileIDs), configFile)
+ if err != nil {
+ return nil, errors.New("querying: " + err.Error())
+ }
+ defer rows.Close()
+
+ profilesParams := map[int]map[string]string{}
+ for rows.Next() {
+ profileID := 0
+ name := ""
+ val := ""
+ if err := rows.Scan(&profileID, &name, &val); err != nil {
+ return nil, errors.New("scanning: " + err.Error())
+ }
+ if _, ok := profilesParams[profileID]; !ok {
+ profilesParams[profileID] = map[string]string{}
+ }
+ profilesParams[profileID][name] = val
+ }
+ return profilesParams, nil
+}
+
func GetProfileParamData(tx *sql.Tx, profileID int, configFile string)
(map[string]string, error) {
qry := `
SELECT
@@ -67,6 +134,64 @@ WHERE
return params, nil
}
+func GetServerProfileParamData(tx *sql.Tx, serverName tc.CacheName, configFile
string) (map[string]string, error) {
+ qry := `
+SELECT
+ p.name,
+ p.value
+FROM
+ parameter p
+ JOIN profile_parameter pp on p.id = pp.parameter
+ JOIN server s on s.profile = pp.profile
+WHERE
+ s.host_name = $1
+ AND p.config_file = $2
+`
+ rows, err := tx.Query(qry, serverName, configFile)
+ if err != nil {
+ return nil, errors.New("querying: " + err.Error())
+ }
+ defer rows.Close()
+
+ params := map[string]string{}
+ for rows.Next() {
+ name := ""
+ val := ""
+ if err := rows.Scan(&name, &val); err != nil {
+ return nil, errors.New("scanning: " + err.Error())
+ }
+ if name == "location" {
+ continue
+ }
+
+ if _, ok := params[name]; ok {
+ log.Warnf("Server %v profile has multiple parameters
'%v' assigned! ATS config generation ignoring value '%v'!", serverName, name,
params[name])
+ }
+
+ params[name] = val
+ }
+ return params, nil
+}
+
+// GetATSMajorVersion returns the major version of the given profile's package
trafficserver parameter.
+// If no parameter exists, this does not return an error, but rather logs a
warning and uses DefaultATSVersion.
+func GetATSMajorVersionFromServerName(tx *sql.Tx, serverName tc.CacheName)
(int, error) {
+ atsVersion, _, err := GetServerProfileParamValue(tx, serverName,
"package", "trafficserver")
+ if err != nil {
+ return 0, errors.New("getting profile param value: " +
err.Error())
+ }
+ if len(atsVersion) == 0 {
+ atsVersion = atscfg.DefaultATSVersion
+ log.Warnln("Parameter package.trafficserver missing for server
" + string(serverName) + " profile. Assuming version " + atsVersion)
+ }
+
+ atsMajorVer, err := atscfg.GetATSMajorVersionFromATSVersion(atsVersion)
+ if err != nil {
+ return 0, errors.New("ats version parameter '" + atsVersion +
"' on this profile is not a number (config_file 'package', name
'trafficserver')")
+ }
+ return atsMajorVer, nil
+}
+
type ProfileData struct {
ID int
Name string
@@ -152,6 +277,31 @@ WHERE
return val, true, nil
}
+// GetServerProfileParamValue gets the value of a parameter assigned to a
server's Profile, by name and config file.
+// Returns the parameter, whether it existed, and any error.
+func GetServerProfileParamValue(tx *sql.Tx, serverName tc.CacheName,
configFile string, name string) (string, bool, error) {
+ qry := `
+SELECT
+ p.value
+FROM
+ parameter p
+ JOIN profile_parameter pp ON p.id = pp.parameter
+ JOIN server s on s.profile = pp.profile
+WHERE
+ s.host_name = $1
+ AND p.config_file = $2
+ AND p.name = $3
+`
+ val := ""
+ if err := tx.QueryRow(qry, serverName, configFile, name).Scan(&val);
err != nil {
+ if err == sql.ErrNoRows {
+ return "", false, nil
+ }
+ return "", false, errors.New("querying: " + err.Error())
+ }
+ return val, true, nil
+}
+
// GetProfileIDFromName returns the profile's ID, whether it exists, and any
error.
func GetProfileIDFromName(tx *sql.Tx, profileName string) (int, bool, error) {
qry := `SELECT id from profile where name = $1`
@@ -197,3 +347,428 @@ WHERE
}
return params, nil
}
+
+func GetServerParamData(tx *sql.Tx, profileID int, configFile string,
serverHost string, serverDomain string) (map[string]string, error) {
+ qry := `
+SELECT
+ p.id,
+ p.name,
+ p.value
+FROM
+ parameter p
+ join profile_parameter pp on p.id = pp.parameter
+ JOIN profile pr on p.id = pp.profile
+WHERE
+ pr.id = $1
+ AND p.config_file = $2
+`
+ rows, err := tx.Query(qry, profileID, configFile)
+ if err != nil {
+ return nil, errors.New("querying: " + err.Error())
+ }
+ defer rows.Close()
+
+ params := map[string]string{}
+ for rows.Next() {
+ id := 0
+ name := ""
+ val := ""
+ if err := rows.Scan(&name, &val); err != nil {
+ return nil, errors.New("scanning: " + err.Error())
+ }
+ if name == "location" {
+ continue
+ }
+
+ // some files have multiple lines with the same key... handle
that with param id.
+ key := name
+ if _, ok := params[name]; ok {
+ key += "__" + strconv.Itoa(id)
+ }
+ if val == "STRING __HOSTNAME__" {
+ val = serverHost + "." + serverDomain
+ }
+ params[key] = val
+ }
+ return params, nil
+}
+
+type DSData struct {
+ Type tc.DSType
+ OriginFQDN *string
+}
+
+func GetDSData(tx *sql.Tx, serverID int) ([]DSData, error) {
+ qry := `
+SELECT
+ dstype.name AS ds_type,
+ (SELECT o.protocol::text || \'://\' || o.fqdn || rtrim(concat(\':\',
o.port::text), \':\')
+ FROM origin o
+ WHERE o.deliveryservice = ds.id
+ AND o.is_primary) as org_server_fqdn
+FROM
+ deliveryservice ds
+ JOIN type as dstype ON ds.type = dstype.id
+WHERE
+ ds.id IN (
+ SELECT DISTINCT deliveryservice
+ FROM deliveryservice_server
+ WHERE server = $1
+ )
+`
+ rows, err := tx.Query(qry, serverID)
+ if err != nil {
+ return nil, errors.New("querying: " + err.Error())
+ }
+ defer rows.Close()
+
+ dses := []DSData{}
+ for rows.Next() {
+ d := DSData{}
+ if err := rows.Scan(&d.Type, &d.OriginFQDN); err != nil {
+ return nil, errors.New("scanning: " + err.Error())
+ }
+ d.Type = tc.DSTypeFromString(string(d.Type))
+ dses = append(dses, d)
+ }
+ return dses, nil
+}
+
+func GetRemapDSData(tx *sql.Tx, serverInfo *atscfg.ServerInfo)
([]atscfg.RemapConfigDSData, error) {
+ if tc.CacheTypeFromString(serverInfo.Type) == tc.CacheTypeMid {
+ return GetRemapDSDataForMid(tx, serverInfo)
+ } else {
+ return GetRemapDSDataForEdge(tx, serverInfo)
+ }
+}
+
+const RemapDSDataQuerySelectFrom = `
+SELECT
+ ds.xml_id,
+ ds.id AS ds_id,
+ ds.dscp,
+ ds.routing_name,
+ ds.signing_algorithm,
+ ds.qstring_ignore,
+ (SELECT o.protocol::text || '://' || o.fqdn || rtrim(concat(':',
o.port::text), ':')
+ FROM origin o
+ WHERE o.deliveryservice = ds.id
+ AND o.is_primary) as org_server_fqdn,
+ ds.multi_site_origin,
+ ds.range_request_handling,
+ ds.fq_pacing_rate,
+ ds.origin_shield,
+ r.pattern,
+ retype.name AS re_type,
+ dstype.name AS ds_type,
+ cdn.domain_name AS domain_name,
+ dsr.set_number,
+ ds.edge_header_rewrite,
+ ds.mid_header_rewrite,
+ ds.regex_remap,
+ ds.cacheurl,
+ ds.remap_text,
+ ds.protocol,
+ ds.profile,
+ ds.anonymous_blocking_enabled,
+ ds.active
+FROM
+ deliveryservice ds
+ JOIN deliveryservice_regex dsr ON dsr.deliveryservice = ds.id
+ JOIN regex r ON dsr.regex = r.id
+ JOIN type retype ON r.type = retype.id
+ JOIN type dstype ON ds.type = dstype.id
+ JOIN cdn ON cdn.id = ds.cdn_id
+`
+
+const RemapDSDataQueryWhereForMid = `
+WHERE
+ cdn.name = $1
+ AND ds.id in (SELECT dss.deliveryservice FROM deliveryservice_server dss)
+ AND ds.active = true
+`
+
+const RemapDSDataQueryWhereForEdge = `
+JOIN deliveryservice_server dss ON dss.deliveryservice = ds.id
+WHERE dss.server = $1
+`
+
+const RemapDSDataQueryOrderBy = `
+ORDER BY
+ ds_id,
+ re_type,
+ set_number
+`
+
+func GetRemapDSDataForMid(tx *sql.Tx, serverInfo *atscfg.ServerInfo)
([]atscfg.RemapConfigDSData, error) {
+ qry := RemapDSDataQuerySelectFrom + RemapDSDataQueryWhereForMid +
RemapDSDataQueryOrderBy
+ rows, err := tx.Query(qry, serverInfo.CDN)
+ if err != nil {
+ return nil, errors.New("querying: " + err.Error())
+ }
+ defer rows.Close()
+
+ dses := []atscfg.RemapConfigDSData{}
+ for rows.Next() {
+ d := atscfg.RemapConfigDSData{}
+ if err := rows.Scan(&d.Name, &d.ID, &d.DSCP, &d.RoutingName,
&d.SigningAlgorithm, &d.QStringIgnore, &d.OriginFQDN, &d.MultiSiteOrigin,
&d.RangeRequestHandling, &d.FQPacingRate, &d.OriginShield, &d.Pattern,
&d.RegexType, &d.Type, &d.Domain, &d.RegexSetNumber, &d.EdgeHeaderRewrite,
&d.MidHeaderRewrite, &d.RegexRemap, &d.CacheURL, &d.RemapText, &d.Protocol,
&d.ProfileID, &d.AnonymousBlockingEnabled, &d.Active); err != nil {
+ return nil, errors.New("scanning: " + err.Error())
+ }
+ if !RemapDotConfigIncludeInactiveDeliveryServices && !d.Active {
+ continue
+ }
+ d.Type = tc.DSTypeFromString(string(d.Type))
+ dses = append(dses, d)
+ }
+ return dses, nil
+}
+
+func GetRemapDSDataForEdge(tx *sql.Tx, server *atscfg.ServerInfo)
([]atscfg.RemapConfigDSData, error) {
+ qry := RemapDSDataQuerySelectFrom + RemapDSDataQueryWhereForEdge +
RemapDSDataQueryOrderBy
+ rows, err := tx.Query(qry, server.ID)
+ if err != nil {
+ return nil, errors.New("querying: " + err.Error())
+ }
+ defer rows.Close()
+
+ dses := []atscfg.RemapConfigDSData{}
+ for rows.Next() {
+ d := atscfg.RemapConfigDSData{}
+ if err := rows.Scan(&d.Name, &d.ID, &d.DSCP, &d.RoutingName,
&d.SigningAlgorithm, &d.QStringIgnore, &d.OriginFQDN, &d.MultiSiteOrigin,
&d.RangeRequestHandling, &d.FQPacingRate, &d.OriginShield, &d.Pattern,
&d.RegexType, &d.Type, &d.Domain, &d.RegexSetNumber, &d.EdgeHeaderRewrite,
&d.MidHeaderRewrite, &d.RegexRemap, &d.CacheURL, &d.RemapText, &d.Protocol,
&d.ProfileID, &d.AnonymousBlockingEnabled, &d.Active); err != nil {
+ return nil, errors.New("scanning: " + err.Error())
+ }
+ if !RemapDotConfigIncludeInactiveDeliveryServices && !d.Active {
+ continue
+ }
+ d.Type = tc.DSTypeFromString(string(d.Type))
+ dses = append(dses, d)
+ }
+ return dses, nil
+}
+
+func GetServerNameFromID(tx *sql.Tx, id int) (tc.CacheName, bool, error) {
+ qry := `SELECT s.host_name FROM server s WHERE s.id = $1`
+ name := tc.CacheName("")
+ if err := tx.QueryRow(qry, id).Scan(&name); err != nil {
+ if err == sql.ErrNoRows {
+ return "", false, nil
+ }
+ return "", false, errors.New("querying: " + err.Error())
+ }
+ return name, true, nil
+}
+
+// GetServerNameFromNameOrID returns the server name from a parameter which
may be the name or ID.
+// This also checks and verifies the existence of the given server, and
returns an appropriate user error if it doesn't exist.
+// Returns the name, any user error, any system error, and any error code.
+func GetServerNameFromNameOrID(tx *sql.Tx, serverNameOrID string)
(tc.CacheName, error, error, int) {
+ if serverID, err := strconv.Atoi(serverNameOrID); err == nil {
+ serverName, ok, err := dbhelpers.GetServerNameFromID(tx,
int64(serverID))
+ if err != nil {
+ return "", nil, fmt.Errorf("getting server name from id
%v: %v", serverID, err), http.StatusInternalServerError
+ } else if !ok {
+ return "", errors.New("server not found"), nil,
http.StatusNotFound
+ }
+ return tc.CacheName(serverName), nil, nil, http.StatusOK
+ }
+
+ serverName := tc.CacheName(serverNameOrID)
+ if ok, err := dbhelpers.ServerExists(string(serverName), tx); err !=
nil {
+ return "", nil, fmt.Errorf("checking server name '%v'
existence: %v", serverName, err), http.StatusInternalServerError
+ } else if !ok {
+ return "", errors.New("server not found"), nil,
http.StatusNotFound
+ }
+ return serverName, nil, nil, http.StatusOK
+}
+
+// GetServerInfoByID returns the necessary info about the server, whether the
server exists, and any error.
+func GetServerInfoByID(tx *sql.Tx, id int) (*atscfg.ServerInfo, bool, error) {
+ return getServerInfo(tx, ServerInfoQuery()+`WHERE s.id = $1`,
[]interface{}{id})
+}
+
+// GetServerInfoByHost returns the necessary info about the server, whether
the server exists, and any error.
+func GetServerInfoByHost(tx *sql.Tx, host tc.CacheName) (*atscfg.ServerInfo,
bool, error) {
+ return getServerInfo(tx, ServerInfoQuery()+` WHERE s.host_name = $1 `,
[]interface{}{host})
+}
+
+// getServerInfo returns the necessary info about the server, whether the
server exists, and any error.
+func getServerInfo(tx *sql.Tx, qry string, qryParams []interface{})
(*atscfg.ServerInfo, bool, error) {
+ s := atscfg.ServerInfo{}
+ if err := tx.QueryRow(qry, qryParams...).Scan(&s.CDN, &s.CDNID, &s.ID,
&s.HostName, &s.DomainName, &s.IP, &s.ProfileID, &s.ProfileName, &s.Port,
&s.HTTPSPort, &s.Type, &s.CacheGroupID, &s.ParentCacheGroupID,
&s.SecondaryParentCacheGroupID, &s.ParentCacheGroupType,
&s.SecondaryParentCacheGroupType); err != nil {
+ if err == sql.ErrNoRows {
+ return nil, false, nil
+ }
+ return nil, false, errors.New("querying server info: " +
err.Error())
+ }
+ return &s, true, nil
+}
+
+func ServerInfoQuery() string {
+ return `
+SELECT
+ c.name as cdn,
+ s.cdn_id,
+ s.id,
+ s.host_name,
+ c.domain_name,
+ s.ip_address,
+ s.profile AS profile_id,
+ p.name AS profile_name,
+ s.tcp_port,
+ s.https_port,
+ t.name as type,
+ s.cachegroup,
+ COALESCE(cg.parent_cachegroup_id, ` + strconv.Itoa(atscfg.InvalidID) + `) as
parent_cachegroup_id,
+ COALESCE(cg.secondary_parent_cachegroup_id, ` +
strconv.Itoa(atscfg.InvalidID) + `) as secondary_parent_cachegroup_id,
+ COALESCE(parentt.name, '') as parent_cachegroup_type,
+ COALESCE(sparentt.name, '') as secondary_parent_cachegroup_type
+FROM
+ server s
+ JOIN cdn c ON s.cdn_id = c.id
+ JOIN type t ON s.type = t.id
+ JOIN profile p ON p.id = s.profile
+ JOIN cachegroup cg on s.cachegroup = cg.id
+ LEFT JOIN type parentt on parentt.id = (select type from cachegroup where id
= cg.parent_cachegroup_id)
+ LEFT JOIN type sparentt on sparentt.id = (select type from cachegroup where
id = cg.secondary_parent_cachegroup_id)
+`
+}
+
+// GetATSMajorVersion returns the major version of the given profile's package
trafficserver parameter.
+// If no parameter exists, this does not return an error, but rather logs a
warning and uses DefaultATSVersion.
+func GetATSMajorVersion(tx *sql.Tx, serverProfileID atscfg.ProfileID) (int,
error) {
+ atsVersion, _, err := GetProfileParamValue(tx, int(serverProfileID),
"package", "trafficserver")
+ if err != nil {
+ return 0, errors.New("getting profile param value: " +
err.Error())
+ }
+ if len(atsVersion) == 0 {
+ atsVersion = atscfg.DefaultATSVersion
+ log.Warnln("Parameter package.trafficserver missing for profile
" + strconv.Itoa(int(serverProfileID)) + ". Assuming version " + atsVersion)
+ }
+ atsMajorVer, err := atscfg.GetATSMajorVersionFromATSVersion(atsVersion)
+ if err != nil {
+ return 0, errors.New("ats version parameter '" + atsVersion +
"' on this profile is not a number (config_file 'package', name
'trafficserver')")
+ }
+ return atsMajorVer, nil
+}
+
+// GetTMParams returns the global "tm.url" and "tm.rev_proxy.url" parameters,
and any error. If either param doesn't exist, an empty string is returned
without error.
+func GetTMParams(tx *sql.Tx) (TMParams, error) {
+ rows, err := tx.Query(`SELECT name, value from parameter where
config_file = 'global' AND (name = 'tm.url' OR name = 'tm.rev_proxy.url')`)
+ if err != nil {
+ return TMParams{}, errors.New("querying: " + err.Error())
+ }
+ defer rows.Close()
+
+ p := TMParams{}
+ for rows.Next() {
+ name := ""
+ val := ""
+ if err := rows.Scan(&name, &val); err != nil {
+ return TMParams{}, errors.New("scanning: " +
err.Error())
+ }
+ if name == "tm.url" {
+ p.URL = val
+ } else if name == "tm.rev_proxy.url" {
+ p.ReverseProxyURL = val
+ } else {
+ return TMParams{}, errors.New("querying got unexpected
parameter: " + name + " (value: '" + val + "')") // should never happen
+ }
+ }
+ return p, nil
+}
+
+// GetLocationParams returns a map[configFile]locationParams, and any error.
If either param doesn't exist, an empty string is returned without error.
+func GetLocationParams(tx *sql.Tx, profileID int)
(map[string]ConfigProfileParams, error) {
+ qry := `
+SELECT
+ p.name,
+ p.config_file,
+ p.value
+FROM
+ parameter p
+ JOIN profile_parameter pp ON pp.parameter = p.id
+WHERE
+ pp.profile = $1
+`
+ rows, err := tx.Query(qry, profileID)
+ if err != nil {
+ return nil, errors.New("querying: " + err.Error())
+ }
+ defer rows.Close()
+
+ params := map[string]ConfigProfileParams{}
+ for rows.Next() {
+ name := ""
+ file := ""
+ val := ""
+ if err := rows.Scan(&name, &file, &val); err != nil {
+ return nil, errors.New("scanning: " + err.Error())
+ }
+ if name == "location" {
+ p := params[file]
+ p.FileNameOnDisk = file
+ p.Location = val
+ params[file] = p
+ } else if name == "URL" {
+ p := params[file]
+ p.URL = val
+ params[file] = p
+ }
+ }
+ return params, nil
+}
+
+// GetServerURISignedDSes returns a list of delivery service names which have
the given server assigned and have URI signing enabled, and any error.
+func GetServerURISignedDSes(tx *sql.Tx, serverHostName string, serverPort int)
([]tc.DeliveryServiceName, error) {
+ qry := `
+SELECT
+ ds.xml_id
+FROM
+ deliveryservice ds
+ JOIN deliveryservice_server dss ON ds.id = dss.deliveryservice
+ JOIN server s ON s.id = dss.server
+WHERE
+ s.host_name = $1
+ AND s.tcp_port = $2
+ AND ds.signing_algorithm = 'uri_signing'
+`
+ rows, err := tx.Query(qry, serverHostName, serverPort)
+ if err != nil {
+ return nil, errors.New("querying: " + err.Error())
+ }
+ defer rows.Close()
+
+ dses := []tc.DeliveryServiceName{}
+ for rows.Next() {
+ ds := tc.DeliveryServiceName("")
+ if err := rows.Scan(&ds); err != nil {
+ return nil, errors.New("scanning: " + err.Error())
+ }
+ dses = append(dses, ds)
+ }
+ return dses, nil
+}
+
+type TMParams struct {
+ URL string
+ ReverseProxyURL string
+}
+
+type ConfigProfileParams struct {
+ FileNameOnDisk string
+ Location string
+ URL string
+ APIURI string
+}
+
+// GetFirstScopeParameter returns the value of the arbitrarily-first parameter
with the name 'scope' and the given config file, whether a parameter was found,
and any error.
+func GetFirstScopeParameter(tx *sql.Tx, cfgFile string) (string, bool, error) {
+ v := ""
+ if err := tx.QueryRow(`SELECT p.value FROM parameter p WHERE
p.config_file = $1 AND p.name = 'scope'`, cfgFile).Scan(&v); err != nil {
+ if err == sql.ErrNoRows {
+ return "", false, nil
+ }
+ return "", false, errors.New("querying first scope parameter: "
+ err.Error())
+ }
+ return v, true, nil
+}
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go
b/traffic_ops/traffic_ops_golang/routing/routes.go
index 0de74d2..9dd1403 100644
--- a/traffic_ops/traffic_ops_golang/routing/routes.go
+++ b/traffic_ops/traffic_ops_golang/routing/routes.go
@@ -38,6 +38,7 @@ import (
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/asn"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats/atsprofile"
+
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats/atsserver"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/auth"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/cachegroup"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/cachegroupparameter"
@@ -389,7 +390,6 @@ func Routes(d ServerData) ([]Route, []RawRoute,
http.Handler, error) {
{1.1, http.MethodPut, `snapshot/{cdn}/?$`,
crconfig.SnapshotHandler, auth.PrivLevelOperations, Authenticated, nil},
// ATS config files
- {1.1, http.MethodGet,
`servers/{server-name-or-id}/configfiles/ats/?(\.json)?$`,
ats.GetConfigMetaData, auth.PrivLevelOperations, Authenticated, nil},
{1.1, http.MethodGet,
`cdns/{cdn-name-or-id}/configfiles/ats/regex_revalidate.config/?(\.json)?$`,
ats.GetRegexRevalidateDotConfig, auth.PrivLevelOperations, Authenticated, nil},
{1.1, http.MethodGet,
`cdns/{cdn-name-or-id}/configfiles/ats/hdr_rw_mid_{xml-id}.config/?(\.json)?$`,
ats.GetMidHeaderRewriteDotConfig, auth.PrivLevelOperations, Authenticated, nil},
{1.1, http.MethodGet,
`cdns/{cdn-name-or-id}/configfiles/ats/hdr_rw_{xml-id}.config/?(\.json)?$`,
ats.GetEdgeHeaderRewriteDotConfig, auth.PrivLevelOperations, Authenticated,
nil},
@@ -411,8 +411,9 @@ func Routes(d ServerData) ([]Route, []RawRoute,
http.Handler, error) {
{1.1, http.MethodGet,
`profiles/{profile-name-or-id}/configfiles/ats/volume.config/?$`,
atsprofile.GetVolume, auth.PrivLevelOperations, Authenticated, nil},
{1.1, http.MethodGet,
`profiles/{profile-name-or-id}/configfiles/ats/{file}/?$`,
atsprofile.GetUnknown, auth.PrivLevelOperations, Authenticated, nil},
- // Cache Configs
- {1.1, http.MethodGet,
`servers/{id-or-host}/configfiles/ats/parent.config/?(\.json)?$`,
ats.GetParentDotConfig, auth.PrivLevelOperations, Authenticated, nil},
+ {1.1, http.MethodGet,
`servers/{server-name-or-id}/configfiles/ats/?(\.json)?$`,
atsserver.GetConfigMetaData, auth.PrivLevelOperations, Authenticated, nil},
+ {1.1, http.MethodGet,
`servers/{server-name-or-id}/configfiles/ats/parent.config/?(\.json)?$`,
atsserver.GetParentDotConfig, auth.PrivLevelOperations, Authenticated, nil},
+ {1.1, http.MethodGet,
`servers/{server-name-or-id}/configfiles/ats/remap.config/?(\.json)?$`,
atsserver.GetServerConfigRemap, auth.PrivLevelOperations, Authenticated, nil},
// Federations
{1.4, http.MethodGet, `federations/all/?(\.json)?$`,
federations.GetAll, auth.PrivLevelAdmin, Authenticated, nil},