This is an automated email from the ASF dual-hosted git repository.
rawlin 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 171fe3b Add TO Go profile ATS configs (#3593)
171fe3b is described below
commit 171fe3b60831188404e89549c8cb63be95d486de
Author: Robert Butts <[email protected]>
AuthorDate: Mon Aug 5 16:55:31 2019 -0600
Add TO Go profile ATS configs (#3593)
---
CHANGELOG.md | 1 +
traffic_ops/testing/api/v14/profileconfig_test.go | 85 ++++++++++
.../traffic_ops_golang/ats/atsprofile/astats.go | 43 ++++++
.../ats/atsprofile/atsdotrules.go | 65 ++++++++
.../traffic_ops_golang/ats/atsprofile/cache.go | 89 +++++++++++
.../ats/atsprofile/dropqstring.go | 48 ++++++
.../traffic_ops_golang/ats/atsprofile/facts.go | 37 +++++
.../traffic_ops_golang/ats/atsprofile/logging.go | 136 ++++++++++++++++
.../ats/atsprofile/loggingyaml.go | 133 ++++++++++++++++
.../traffic_ops_golang/ats/atsprofile/logsxml.go | 108 +++++++++++++
.../traffic_ops_golang/ats/atsprofile/plugin.go | 43 ++++++
.../traffic_ops_golang/ats/atsprofile/profile.go | 142 +++++++++++++++++
.../ats/atsprofile/profile_test.go | 52 +++++++
.../traffic_ops_golang/ats/atsprofile/records.go | 61 ++++++++
.../ats/atsprofile/records_test.go | 107 +++++++++++++
.../traffic_ops_golang/ats/atsprofile/storage.go | 88 +++++++++++
.../traffic_ops_golang/ats/atsprofile/sysctl.go | 46 ++++++
.../traffic_ops_golang/ats/atsprofile/unknown.go | 68 ++++++++
.../ats/atsprofile/urisigning.go | 50 ++++++
.../traffic_ops_golang/ats/atsprofile/urlsig.go | 63 ++++++++
.../traffic_ops_golang/ats/atsprofile/volume.go | 78 ++++++++++
traffic_ops/traffic_ops_golang/ats/config.go | 2 +-
traffic_ops/traffic_ops_golang/ats/db.go | 172 +++++++++++++++++++++
.../traffic_ops_golang/ats/headerrewrite.go | 4 +-
.../traffic_ops_golang/ats/parentdotconfig.go | 28 +---
.../traffic_ops_golang/ats/regexrevalidate.go | 2 +-
traffic_ops/traffic_ops_golang/riaksvc/dsutil.go | 47 ++++++
traffic_ops/traffic_ops_golang/routing/routes.go | 18 +++
28 files changed, 1786 insertions(+), 30 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index da3ad42..2a04a43 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,6 +20,7 @@ The format is based on [Keep a
Changelog](http://keepachangelog.com/en/1.0.0/).
- /api/1.1/cdns/name/:name/dnsseckeys `GET`
- /api/1.4/cdns/name/:name/dnsseckeys `GET`
- /api/1.4/user/login/oauth `POST`
+ - /api/1.1/profiles/:name/configfiles/ats/* `GET`
- 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/traffic_ops/testing/api/v14/profileconfig_test.go
b/traffic_ops/testing/api/v14/profileconfig_test.go
new file mode 100644
index 0000000..8126c74
--- /dev/null
+++ b/traffic_ops/testing/api/v14/profileconfig_test.go
@@ -0,0 +1,85 @@
+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 TestProfileDotConfig(t *testing.T) {
+ WithObjs(t, []TCObj{CDNs, Types, Tenants, Parameters, Profiles,
Statuses, Divisions, Regions, PhysLocations, CacheGroups, Servers,
DeliveryServices}, func() {
+ defer DeleteTestDeliveryServiceServersCreated(t)
+ CreateTestDeliveryServiceServers(t)
+ GetTestProfileDotConfig(t)
+ })
+}
+
+func GetTestProfileDotConfig(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, _, err :=
TOSession.GetDeliveryService(strconv.Itoa(*dsServers.Response[0].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 _, err := url.Parse(ds.OrgServerFQDN); 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)
+ }
+
+ servers, _, err := TOSession.GetServers()
+ if err != nil {
+ t.Errorf("cannot GET Servers: %v\n", err)
+ }
+
+ server := tc.Server{ID: -1}
+ for _, potentialServer := range servers {
+ if potentialServer.Type != string(tc.CacheTypeEdge) {
+ continue
+ }
+ server = potentialServer
+ }
+ if server.ID == -1 {
+ t.Errorf("GET Servers returned no edge servers, must have at
least 1 to test")
+ }
+
+ profileDotConfig, _, err :=
TOSession.GetATSProfileConfig(server.ProfileID, "cache.config")
+ if err != nil {
+ t.Fatalf("Getting server %+v config parent.config:
"+err.Error()+"\n", serverID)
+ }
+
+ if !strings.Contains(profileDotConfig, server.Profile) {
+ t.Errorf("expected: profile cache.config to contain profile
name '%+v', actual: '''%+v'''", server.Profile, profileDotConfig)
+ }
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsprofile/astats.go
b/traffic_ops/traffic_ops_golang/ats/atsprofile/astats.go
new file mode 100644
index 0000000..bbc302e
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsprofile/astats.go
@@ -0,0 +1,43 @@
+package atsprofile
+
+/*
+ * 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 (
+ "database/sql"
+ "net/http"
+
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+)
+
+const AstatsSeparator = "="
+const AstatsFileName = "astats.config"
+
+func GetAstats(w http.ResponseWriter, r *http.Request) {
+ WithProfileData(w, r, makeAstats)
+}
+
+func makeAstats(tx *sql.Tx, cfg *config.Config, profile ats.ProfileData, _
string) (string, error) {
+ txt, err := GenericProfileConfig(tx, profile, AstatsFileName,
AstatsSeparator)
+ if err == nil && txt == "" {
+ txt = "\n" // If no params exist, don't send "not found," but
an empty file. We know the profile exists.
+ }
+ return txt, err
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsprofile/atsdotrules.go
b/traffic_ops/traffic_ops_golang/ats/atsprofile/atsdotrules.go
new file mode 100644
index 0000000..45eb470
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsprofile/atsdotrules.go
@@ -0,0 +1,65 @@
+package atsprofile
+
+/*
+ * 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 (
+ "database/sql"
+ "errors"
+ "net/http"
+ "strings"
+
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+)
+
+func GetATSDotRules(w http.ResponseWriter, r *http.Request) {
+ WithProfileData(w, r, makeATSDotRules)
+}
+
+func makeATSDotRules(tx *sql.Tx, _ *config.Config, profile ats.ProfileData,
fileName string) (string, error) {
+ // TODO add more efficient db func to only get drive params?
+ paramData, err := ats.GetProfileParamData(tx, profile.ID,
"storage.config") // ats.rules is based on the storage.config params
+ if err != nil {
+ return "", errors.New("getting profile param data: " +
err.Error())
+ }
+
+ drivePrefix := strings.TrimPrefix(paramData["Drive_Prefix"], `/dev/`)
+ drivePostfix := strings.Split(paramData["Drive_Letters"], ",")
+
+ text := ""
+ for _, l := range drivePostfix {
+ l = strings.TrimSpace(l)
+ if l == "" {
+ continue
+ }
+ text += `KERNEL=="` + drivePrefix + l + `", OWNER="ats"` + "\n"
+ }
+ if ramPrefix, ok := paramData["RAM_Drive_Prefix"]; ok {
+ ramPrefix = strings.TrimPrefix(ramPrefix, `/dev/`)
+ ramPostfix := strings.Split(paramData["RAM_Drive_Letters"], ",")
+ for _, l := range ramPostfix {
+ text += `KERNEL=="` + ramPrefix + l + `", OWNER="ats"`
+ "\n"
+ }
+ }
+ if text == "" {
+ text = "\n" // prevents it being flagged as "not found"
+ }
+ return text, nil
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsprofile/cache.go
b/traffic_ops/traffic_ops_golang/ats/atsprofile/cache.go
new file mode 100644
index 0000000..affd050
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsprofile/cache.go
@@ -0,0 +1,89 @@
+package atsprofile
+
+/*
+ * 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 (
+ "database/sql"
+ "errors"
+ "net/http"
+ "strings"
+
+ "github.com/apache/trafficcontrol/lib/go-log"
+ "github.com/apache/trafficcontrol/lib/go-tc"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+)
+
+func GetCache(w http.ResponseWriter, r *http.Request) {
+ WithProfileData(w, r, makeCache)
+}
+
+func makeCache(tx *sql.Tx, _ *config.Config, profile ats.ProfileData, _
string) (string, error) {
+ lines := map[string]struct{}{} // use a "set" for lines, to avoid
duplicates, since we're looking up by profile
+ profileDSes, err := ats.GetProfileDS(tx, profile.ID)
+ if err != nil {
+ return "", errors.New("getting profile delivery services: " +
err.Error())
+ }
+
+ for _, ds := range profileDSes {
+ if ds.Type != tc.DSTypeHTTPNoCache {
+ continue
+ }
+ if ds.OriginFQDN == nil || *ds.OriginFQDN == "" {
+ log.Warnf("profileCacheDotConfig ds has no origin fqdn,
skipping!") // TODO add ds name to data loaded, to put it in the error here?
+ continue
+ }
+ originFQDN, originPort := getHostPortFromURI(*ds.OriginFQDN)
+ if originPort != "" {
+ l := "dest_domain=" + originFQDN + " port=" +
originPort + " scheme=http action=never-cache\n"
+ lines[l] = struct{}{}
+ } else {
+ l := "dest_domain=" + originFQDN + " scheme=http
action=never-cache\n"
+ lines[l] = struct{}{}
+ }
+ }
+
+ text := ""
+ for line, _ := range lines {
+ text += line
+ }
+ if text == "" {
+ text = "\n" // If no params exist, don't send "not found," but
an empty file. We know the profile exists.
+ }
+ return text, nil
+}
+
+func getHostPortFromURI(uriStr string) (string, string) {
+ originFQDN := uriStr
+ originFQDN = strings.TrimPrefix(originFQDN, "http://")
+ originFQDN = strings.TrimPrefix(originFQDN, "https://")
+
+ slashPos := strings.Index(originFQDN, "/")
+ if slashPos != -1 {
+ originFQDN = originFQDN[:slashPos]
+ }
+ portPos := strings.Index(originFQDN, ":")
+ portStr := ""
+ if portPos != -1 {
+ portStr = originFQDN[portPos+1:]
+ originFQDN = originFQDN[:portPos]
+ }
+ return originFQDN, portStr
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsprofile/dropqstring.go
b/traffic_ops/traffic_ops_golang/ats/atsprofile/dropqstring.go
new file mode 100644
index 0000000..4909766
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsprofile/dropqstring.go
@@ -0,0 +1,48 @@
+package atsprofile
+
+/*
+ * 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 (
+ "database/sql"
+ "errors"
+ "net/http"
+
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+)
+
+func GetDropQString(w http.ResponseWriter, r *http.Request) {
+ WithProfileData(w, r, makeDropQString)
+}
+
+func makeDropQString(tx *sql.Tx, _ *config.Config, profile ats.ProfileData, _
string) (string, error) {
+ dropQStringVal, hasDropQStringParam, err :=
ats.GetProfileParamValue(tx, profile.ID, "drop_qstring.config", "content")
+ if err != nil {
+ return "", errors.New("getting profile param val: " +
err.Error())
+ }
+
+ text := ""
+ if hasDropQStringParam {
+ text += dropQStringVal + "\n"
+ } else {
+ text += `/([^?]+) $s://$t/$1` + "\n"
+ }
+ return text, nil
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsprofile/facts.go
b/traffic_ops/traffic_ops_golang/ats/atsprofile/facts.go
new file mode 100644
index 0000000..6b94a55
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsprofile/facts.go
@@ -0,0 +1,37 @@
+package atsprofile
+
+/*
+ * 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 (
+ "database/sql"
+ "net/http"
+
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+)
+
+func GetFacts(w http.ResponseWriter, r *http.Request) {
+ WithProfileData(w, r, makeFacts)
+}
+
+func makeFacts(tx *sql.Tx, _ *config.Config, profile ats.ProfileData, fileName
string) (string, error) {
+ text := "profile:" + profile.Name + "\n"
+ return text, nil
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsprofile/logging.go
b/traffic_ops/traffic_ops_golang/ats/atsprofile/logging.go
new file mode 100644
index 0000000..2fe0b38
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsprofile/logging.go
@@ -0,0 +1,136 @@
+package atsprofile
+
+/*
+ * 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 (
+ "database/sql"
+ "errors"
+ "net/http"
+ "strconv"
+ "strings"
+
+ "github.com/apache/trafficcontrol/lib/go-log"
+ "github.com/apache/trafficcontrol/lib/go-tc"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+)
+
+const LoggingFileName = "logging.config"
+
+func GetLogging(w http.ResponseWriter, r *http.Request) {
+ addHdr := false
+ WithProfileDataHdr(w, r, addHdr, tc.ContentTypeTextPlain, makeLogging)
// TODO change to Content-Type text/x-lua? Perl uses text/plain.
+}
+
+func makeLogging(tx *sql.Tx, _ *config.Config, profile ats.ProfileData, _
string) (string, error) {
+ profileParamData, err := ats.GetProfileParamData(tx, profile.ID,
LoggingFileName)
+
+ if err != nil {
+ return "", errors.New("getting profile param data: " +
err.Error())
+ }
+
+ hdrComment, err := ats.HeaderComment(tx, profile.Name)
+ if err != nil {
+ return "", errors.New("getting header comment: " + err.Error())
+ }
+ // This is an LUA file, so we need to massage the header a bit for LUA
commenting.
+ hdrComment = strings.Replace(hdrComment, `# `, ``, -1)
+ hdrComment = strings.Replace(hdrComment, "\n", ``, -1)
+ text := "-- " + hdrComment + " --\n"
+
+ for i := 0; i < MaxLogObjects; i++ {
+ logFormatField := "LogFormat"
+ if i > 0 {
+ logFormatField += strconv.Itoa(i)
+ }
+ if logFormatName := profileParamData[logFormatField+".Name"];
logFormatName != "" {
+ format := profileParamData[logFormatField+".Format"]
+ if format == "" {
+ // TODO determine if the line should be
excluded. Perl includes it anyway, without checking.
+ log.Errorf("Profile '%v' has logging.config
format '%v' Name Parameter but no Format Parameter. Setting blank Format!\n",
profile.Name, logFormatField)
+ }
+ format = strings.Replace(format, `"`, `\"`, -1)
+ text += logFormatName + " = format {\n"
+ text += " Format = '" + format + " '\n"
+ text += "}\n"
+ }
+ }
+
+ for i := 0; i < MaxLogObjects; i++ {
+ logFilterField := "LogFilter"
+ if i > 0 {
+ logFilterField += strconv.Itoa(i)
+ }
+
+ if logFilterName := profileParamData[logFilterField+".Name"];
logFilterName != "" {
+ filter := profileParamData[logFilterField+".Filter"]
+ if filter == "" {
+ // TODO determine if the line should be
excluded. Perl includes it anyway, without checking.
+ log.Errorf("Profile '%v' has logging.config
format '%v' Name Parameter but no Filter Parameter. Setting blank Filter!\n",
profile.Name, logFilterField)
+ }
+
+ filter = strings.Replace(filter, `\`, `\\`, -1)
+ filter = strings.Replace(filter, `'`, `\'`, -1)
+
+ logFilterType :=
profileParamData[logFilterField+".Type"]
+ if logFilterType == "" {
+ logFilterType = "accept"
+ }
+ text += logFilterName + " = filter." + logFilterType +
"('" + filter + "')\n"
+ }
+ }
+
+ for i := 0; i < MaxLogObjects; i++ {
+ logObjectField := "LogObject"
+ if i > 0 {
+ logObjectField += strconv.Itoa(i)
+ }
+
+ if logObjectFilename :=
profileParamData[logObjectField+".Filename"]; logObjectFilename != "" {
+ logObjectType :=
profileParamData[logObjectField+".Type"]
+ if logObjectType == "" {
+ logObjectType = "ascii"
+ }
+ logObjectFormat :=
profileParamData[logObjectField+".Format"]
+ logObjectRollingEnabled :=
profileParamData[logObjectField+".RollingEnabled"]
+ logObjectRollingIntervalSec :=
profileParamData[logObjectField+".RollingIntervalSec"]
+ logObjectRollingOffsetHr :=
profileParamData[logObjectField+".RollingOffsetHr"]
+ logObjectRollingSizeMb :=
profileParamData[logObjectField+".RollingSizeMb"]
+ logObjectFilters :=
profileParamData[logObjectField+".Filters"]
+
+ text += "\nlog." + logObjectType + " {\n"
+ text += " Format = " + logObjectFormat + ",\n"
+ text += " Filename = '" + logObjectFilename + "'"
+ if logObjectType != "pipe" {
+ text += ",\n"
+ text += " RollingEnabled = " +
logObjectRollingEnabled + ",\n"
+ text += " RollingIntervalSec = " +
logObjectRollingIntervalSec + ",\n"
+ text += " RollingOffsetHr = " +
logObjectRollingOffsetHr + ",\n"
+ text += " RollingSizeMb = " +
logObjectRollingSizeMb
+ }
+ if logObjectFilters != "" {
+ text += ",\n Filters = { " + logObjectFilters
+ " }"
+ }
+ text += "\n}\n"
+ }
+ }
+
+ return text, nil
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsprofile/loggingyaml.go
b/traffic_ops/traffic_ops_golang/ats/atsprofile/loggingyaml.go
new file mode 100644
index 0000000..fe302f0
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsprofile/loggingyaml.go
@@ -0,0 +1,133 @@
+package atsprofile
+
+/*
+ * 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 (
+ "database/sql"
+ "errors"
+ "net/http"
+ "strconv"
+ "strings"
+
+ "github.com/apache/trafficcontrol/lib/go-log"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+)
+
+const LoggingYAMLFileName = "logging.yaml"
+
+func GetLoggingYAML(w http.ResponseWriter, r *http.Request) {
+ WithProfileData(w, r, makeLoggingYAML) // TODO change to Content-Type
text/yaml? Perl uses text/plain.
+}
+
+func makeLoggingYAML(tx *sql.Tx, _ *config.Config, profile ats.ProfileData, _
string) (string, error) {
+ profileParamData, err := ats.GetProfileParamData(tx, profile.ID,
LoggingYAMLFileName)
+ if err != nil {
+ return "", errors.New("getting profile param data: " +
err.Error())
+ }
+
+ // note we use the same const as logs.xml - this isn't necessarily a
requirement, and we may want to make separate variables in the future.
+ maxLogObjects := MaxLogObjects
+
+ text := "\nformats: \n"
+ for i := 0; i < maxLogObjects; i++ {
+ logFormatField := "LogFormat"
+ if i > 0 {
+ logFormatField += strconv.Itoa(i)
+ }
+ logFormatName := profileParamData[logFormatField+".Name"]
+ if logFormatName != "" {
+ format := profileParamData[logFormatField+".Format"]
+ if format == "" {
+ // TODO determine if the line should be
excluded. Perl includes it anyway, without checking.
+ log.Errorf("Profile '%v' has logging.yaml
format '%v' Name Parameter but no Format Parameter. Setting blank Format!\n",
profile.Name, logFormatField)
+ }
+ text += " - name: " + logFormatName + " \n"
+ text += " format: '" + format + "'\n"
+ }
+ }
+
+ text += "filters:\n"
+ for i := 0; i < maxLogObjects; i++ {
+ logFilterField := "LogFilter"
+ if i > 0 {
+ logFilterField += strconv.Itoa(i)
+ }
+ if logFilterName := profileParamData[logFilterField+".Name"];
logFilterName != "" {
+ filter := profileParamData[logFilterField+".Filter"]
+ if filter == "" {
+ // TODO determine if the line should be
excluded. Perl includes it anyway, without checking.
+ log.Errorf("Profile '%v' has logging.yaml
filter '%v' Name Parameter but no Filter Parameter. Setting blank Filter!\n",
profile.Name, logFilterField)
+ }
+ logFilterType :=
profileParamData[logFilterField+".Type"]
+ if logFilterType == "" {
+ logFilterType = "accept"
+ }
+ text += "- name: " + logFilterName + "\n"
+ text += " action: " + logFilterType + "\n"
+ text += " condition: " + filter + "\n"
+ }
+ }
+
+ for i := 0; i < maxLogObjects; i++ {
+ logObjectField := "LogObject"
+ if i > 0 {
+ logObjectField += strconv.Itoa(i)
+ }
+
+ if logObjectFilename :=
profileParamData[logObjectField+".Filename"]; logObjectFilename != "" {
+ logObjectType :=
profileParamData[logObjectField+".Type"]
+ if logObjectType == "" {
+ logObjectType = "ascii"
+ }
+ logObjectFormat :=
profileParamData[logObjectField+".Format"]
+ logObjectRollingEnabled :=
profileParamData[logObjectField+".RollingEnabled"]
+ logObjectRollingIntervalSec :=
profileParamData[logObjectField+".RollingIntervalSec"]
+ logObjectRollingOffsetHr :=
profileParamData[logObjectField+".RollingOffsetHr"]
+ logObjectRollingSizeMb :=
profileParamData[logObjectField+".RollingSizeMb"]
+ logObjectFilters :=
profileParamData[logObjectField+".Filters"]
+
+ text += "\nlogs:\n"
+ text += "- mode: " + logObjectType + "\n"
+ text += " filename: " + logObjectFilename + "\n"
+ text += " format: " + logObjectFormat + "\n"
+
+ if logObjectType != "pipe" {
+ if logObjectRollingEnabled != "" {
+ text += " rolling_enabled: " +
logObjectRollingEnabled + "\n"
+ }
+ if logObjectRollingIntervalSec != "" {
+ text += " rolling_interval_sec: " +
logObjectRollingIntervalSec + "\n"
+ }
+ if logObjectRollingOffsetHr != "" {
+ text += " rolling_offset_hr: " +
logObjectRollingOffsetHr + "\n"
+ }
+ if logObjectRollingSizeMb != "" {
+ text += " rolling_size_mb: " +
logObjectRollingSizeMb + "\n"
+ }
+ }
+ if logObjectFilters != "" {
+ logObjectFilters =
strings.Replace(logObjectFilters, "\v", "", -1)
+ text += " filters: [" + logObjectFilters + "]"
+ }
+ }
+ }
+ return text, nil
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsprofile/logsxml.go
b/traffic_ops/traffic_ops_golang/ats/atsprofile/logsxml.go
new file mode 100644
index 0000000..8d033ce
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsprofile/logsxml.go
@@ -0,0 +1,108 @@
+package atsprofile
+
+/*
+ * 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 (
+ "database/sql"
+ "errors"
+ "net/http"
+ "strconv"
+ "strings"
+
+ "github.com/apache/trafficcontrol/lib/go-tc"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+)
+
+const LogsXMLFileName = "logs_xml.config"
+
+const MaxLogObjects = 10
+
+func GetLogsXML(w http.ResponseWriter, r *http.Request) {
+ addHdr := false
+ WithProfileDataHdr(w, r, addHdr, tc.ContentTypeTextPlain, makeLogsXML)
// TODO change to Content-Type text/xml? Perl uses text/plain.
+}
+
+func makeLogsXML(tx *sql.Tx, _ *config.Config, profile ats.ProfileData, _
string) (string, error) {
+ profileParamData, err := ats.GetProfileParamData(tx, profile.ID,
LogsXMLFileName)
+ if err != nil {
+ return "", errors.New("getting profile param data: " +
err.Error())
+ }
+
+ hdrComment, err := ats.HeaderComment(tx, profile.Name)
+ if err != nil {
+ return "", errors.New("getting header comment: " + err.Error())
+ }
+ hdrComment = strings.Replace(hdrComment, `# `, ``, -1)
+ hdrComment = strings.Replace(hdrComment, "\n", ``, -1)
+ text := "<!-- " + hdrComment + " -->\n"
+
+ for i := 0; i < MaxLogObjects; i++ {
+ logFormatField := "LogFormat"
+ logObjectField := "LogObject"
+ if i > 0 {
+ iStr := strconv.Itoa(i)
+ logFormatField += iStr
+ logObjectField += iStr
+ }
+
+ logFormatName := profileParamData[logFormatField+".Name"]
+ if logFormatName != "" {
+ format := profileParamData[logFormatField+".Format"]
+ format = strings.Replace(format, `"`, `\"`, -1)
+
+ text += `<LogFormat>
+ <Name = "` + logFormatName + `"/>
+ <Format = "` + format + `"/>
+</LogFormat>
+`
+ }
+
+ logObjectFileName :=
profileParamData[logObjectField+".Filename"]
+ if logObjectFileName != "" {
+ logObjectFormat :=
profileParamData[logObjectField+".Format"]
+ logObjectRollingEnabled :=
profileParamData[logObjectField+".RollingEnabled"]
+ logObjectRollingIntervalSec :=
profileParamData[logObjectField+".RollingIntervalSec"]
+ logObjectRollingOffsetHr :=
profileParamData[logObjectField+".RollingOffsetHr"]
+ logObjectRollingSizeMb :=
profileParamData[logObjectField+".RollingSizeMb"]
+ logObjectHeader :=
profileParamData[logObjectField+".Header"]
+
+ text += `<LogObject>
+ <Format = "` + logObjectFormat + `"/>
+ <Filename = "` + logObjectFileName + `"/>
+`
+ if logObjectRollingEnabled != "" {
+ text += ` <RollingEnabled = ` +
logObjectRollingEnabled + `/>
+`
+ }
+ text += ` <RollingIntervalSec = ` +
logObjectRollingIntervalSec + `/>
+ <RollingOffsetHr = ` + logObjectRollingOffsetHr + `/>
+ <RollingSizeMb = ` + logObjectRollingSizeMb + `/>
+`
+ if logObjectHeader != "" {
+ text += ` <Header = "` + logObjectHeader + `"/>
+`
+ }
+ text += `</LogObject>
+`
+ }
+ }
+ return text, nil
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsprofile/plugin.go
b/traffic_ops/traffic_ops_golang/ats/atsprofile/plugin.go
new file mode 100644
index 0000000..8731508
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsprofile/plugin.go
@@ -0,0 +1,43 @@
+package atsprofile
+
+/*
+ * 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 (
+ "database/sql"
+ "net/http"
+
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+)
+
+const PluginSeparator = " "
+const PluginFileName = "plugin.config"
+
+func GetPlugin(w http.ResponseWriter, r *http.Request) {
+ WithProfileData(w, r, makePlugin)
+}
+
+func makePlugin(tx *sql.Tx, _ *config.Config, profile ats.ProfileData, _
string) (string, error) {
+ txt, err := GenericProfileConfig(tx, profile, PluginFileName,
PluginSeparator)
+ if err == nil && txt == "" {
+ txt = "\n" // If no params exist, don't send "not found," but
an empty file. We know the profile exists.
+ }
+ return txt, err
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsprofile/profile.go
b/traffic_ops/traffic_ops_golang/ats/atsprofile/profile.go
new file mode 100644
index 0000000..d3d4ed7
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsprofile/profile.go
@@ -0,0 +1,142 @@
+package atsprofile
+
+/*
+ * 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 (
+ "database/sql"
+ "errors"
+ "net/http"
+ "strconv"
+ "strings"
+
+ "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/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+)
+
+// GenericProfileConfig generates a generic profile config text, from the
profile's parameters with the given config file name.
+// This does not include a header comment, because a generic config may not
use a number sign as a comment.
+// If you need a header comment, it can be added manually via
ats.HeaderComment, or automatically with WithProfileDataHdr.
+func GenericProfileConfig(tx *sql.Tx, profile ats.ProfileData, fileName
string, separator string) (string, error) {
+ profileParamData, err := ats.GetProfileParamData(tx, profile.ID,
fileName)
+ if err != nil {
+ return "", errors.New("getting profile param data: " +
err.Error())
+ }
+ text := ""
+ for name, val := range profileParamData {
+ name = trimParamUnderscoreNumSuffix(name)
+ text += name + separator + val + "\n"
+ }
+ return text, nil
+}
+
+// trimParamUnderscoreNumSuffix removes any trailing "__[0-9]+" and returns
the trimmed string.
+func trimParamUnderscoreNumSuffix(paramName string) string {
+ underscorePos := strings.LastIndex(paramName, `__`)
+ if underscorePos == -1 {
+ return paramName
+ }
+ if _, err := strconv.ParseFloat(paramName[underscorePos+2:], 64); err
!= nil {
+ return paramName
+ }
+ return paramName[:underscorePos]
+}
+
+type MakeCfgFunc func(tx *sql.Tx, cfg *config.Config, profile ats.ProfileData,
fileName string)
+
+// WithProfileData takes a makeCfg function which takes the ProfileData and
returns the config text or any error.
+//
+// Most profile config files need the same data and write the same text file,
so this can be used to reduce duplicate boilerplate code.
+//
+// This also adds HeaderComment with the profile name to the top of the config
text.
+//
+// The route must include an "id" parameter.
+//
+// The route may include a "file" parameter, and if so, it will be passed to
makeCfg as fileName. If not, fileName will be the empty string.
+//
+// If makeCfg returns a nil error and the empty string, a 404 Not Found will
be returned to the client.
+//
+// If you need to avoid adding the standard header comment, or use a
Content-Type other than text/plain, use WithProfileDataHdr.
+//
+func WithProfileData(w http.ResponseWriter, r *http.Request, makeCfg func(tx
*sql.Tx, cfg *config.Config, profile ats.ProfileData, fileName string) (string,
error)) {
+ addHdr := true
+ WithProfileDataHdr(w, r, addHdr, tc.ContentTypeTextPlain, makeCfg)
+}
+
+func WithProfileDataHdr(w http.ResponseWriter, r *http.Request, addHdr bool,
contentType string, makeCfg func(tx *sql.Tx, cfg *config.Config, profile
ats.ProfileData, fileName string) (string, error)) {
+ inf, userErr, sysErr, errCode := api.NewInfo(r,
[]string{"profile-name-or-id"}, nil)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+ return
+ }
+ defer inf.Close()
+
+ profileNameOrID := inf.Params["profile-name-or-id"]
+ profileID, err := strconv.Atoi(profileNameOrID)
+ if err != nil {
+ profileName := profileNameOrID
+ ok := false
+ if profileID, ok, err = ats.GetProfileIDFromName(inf.Tx.Tx,
profileName); err != nil {
+ api.HandleErr(w, r, inf.Tx.Tx,
http.StatusInternalServerError, nil, errors.New("getting profile id from name:
"+err.Error()))
+ return
+ } else if !ok {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound,
errors.New("Resource not found."), nil)
+ return
+ }
+ }
+
+ fileName := strings.TrimSuffix(inf.Params["file"], ".json")
+
+ profileData, ok, err := ats.GetProfileData(inf.Tx.Tx, profileID)
+ if err != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("getting profile data: "+err.Error()))
+ return
+ }
+ if !ok {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound,
errors.New("not found"), nil)
+ return
+ }
+
+ hdr := ""
+ if addHdr {
+ if hdr, err = ats.HeaderComment(inf.Tx.Tx, profileData.Name);
err != nil {
+ api.HandleErr(w, r, inf.Tx.Tx,
http.StatusInternalServerError, nil, errors.New("getting file contents:
"+err.Error()))
+ return
+ }
+ }
+
+ text, err := makeCfg(inf.Tx.Tx, inf.Config, profileData, fileName)
+ if err != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("making config: "+err.Error()))
+ return
+ }
+
+ if text == "" {
+ // TODO replicates old Perl; verify required.
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound,
errors.New("not found"), nil)
+ return
+ }
+
+ if contentType != "" {
+ w.Header().Set(tc.ContentType, contentType)
+ }
+ w.Write([]byte(hdr + text))
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsprofile/profile_test.go
b/traffic_ops/traffic_ops_golang/ats/atsprofile/profile_test.go
new file mode 100644
index 0000000..5d0a50b
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsprofile/profile_test.go
@@ -0,0 +1,52 @@
+package atsprofile
+
+/*
+ * 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 "testing"
+
+func TestTrimParamUnderscoreNumSuffix(t *testing.T) {
+ inputExpected := map[string]string{
+ ``: ``,
+ `a`: `a`,
+ `_`: `_`,
+ `foo__`: `foo__`,
+ `foo__1`: `foo`,
+ `foo__1234567890`: `foo`,
+ `foo_1234567890`: `foo_1234567890`,
+ `foo__1234__1234567890`: `foo__1234`,
+ `foo__1234__1234567890_`: `foo__1234__1234567890_`,
+ `foo__1234__1234567890a`: `foo__1234__1234567890a`,
+ `foo__1234__1234567890__`: `foo__1234__1234567890__`,
+ `foo__1234__1234567890__a`: `foo__1234__1234567890__a`,
+ `__`: `__`,
+ `__9`: ``,
+ `_9`: `_9`,
+ `__35971234789124`: ``,
+ `a__35971234789124`: `a`,
+ `1234`: `1234`,
+ `foo__asdf_1234`: `foo__asdf_1234`,
+ }
+
+ for input, expected := range inputExpected {
+ if actual := trimParamUnderscoreNumSuffix(input); expected !=
actual {
+ t.Errorf("Expected '%v' Actual '%v'", expected, actual)
+ }
+ }
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsprofile/records.go
b/traffic_ops/traffic_ops_golang/ats/atsprofile/records.go
new file mode 100644
index 0000000..8e43600
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsprofile/records.go
@@ -0,0 +1,61 @@
+package atsprofile
+
+/*
+ * 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 (
+ "database/sql"
+ "net/http"
+ "strings"
+
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+)
+
+const RecordsSeparator = " "
+const RecordsFileName = "records.config"
+
+func GetRecords(w http.ResponseWriter, r *http.Request) {
+ WithProfileData(w, r, makeRecords)
+}
+
+func makeRecords(tx *sql.Tx, _ *config.Config, profile ats.ProfileData, _
string) (string, error) {
+ txt, err := GenericProfileConfig(tx, profile, RecordsFileName,
RecordsSeparator)
+ if err != nil {
+ return "", nil
+ }
+ if txt == "" {
+ txt = "\n" // If no params exist, don't send "not found," but
an empty file. We know the profile exists.
+ }
+ txt = ReplaceLineSuffixes(txt, "STRING __HOSTNAME__", "STRING
__FULL_HOSTNAME__")
+ return txt, nil
+}
+
+func ReplaceLineSuffixes(txt string, suffix string, newSuffix string) string {
+ lines := strings.Split(txt, "\n")
+ newLines := make([]string, 0, len(lines))
+ for _, line := range lines {
+ if strings.HasSuffix(line, suffix) {
+ line = line[:len(line)-len(suffix)]
+ line += newSuffix
+ }
+ newLines = append(newLines, line)
+ }
+ return strings.Join(newLines, "\n")
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsprofile/records_test.go
b/traffic_ops/traffic_ops_golang/ats/atsprofile/records_test.go
new file mode 100644
index 0000000..3e50b85
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsprofile/records_test.go
@@ -0,0 +1,107 @@
+package atsprofile
+
+/*
+ * 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 "testing"
+
+func TestReplaceLineSuffixes(t *testing.T) {
+ {
+ input := `
+foo STRING __HOSTNAME__
+bar
+baz
+`
+ expected := `
+foo STRING __FULL_HOSTNAME__
+bar
+baz
+`
+ actual := ReplaceLineSuffixes(input, "STRING __HOSTNAME__",
"STRING __FULL_HOSTNAME__")
+ if expected != actual {
+ t.Errorf("Expected '%v' Actual '%v'", expected, actual)
+ }
+ }
+ {
+ input := `STRING __HOSTNAME__`
+ expected := `STRING __FULL_HOSTNAME__`
+ actual := ReplaceLineSuffixes(input, "STRING __HOSTNAME__",
"STRING __FULL_HOSTNAME__")
+ if expected != actual {
+ t.Errorf("Expected '%v' Actual '%v'", expected, actual)
+ }
+ }
+ {
+ input := `
+STRING __HOSTNAME__
+`
+ expected := `
+STRING __FULL_HOSTNAME__
+`
+ actual := ReplaceLineSuffixes(input, "STRING __HOSTNAME__",
"STRING __FULL_HOSTNAME__")
+ if expected != actual {
+ t.Errorf("Expected '%v' Actual '%v'", expected, actual)
+ }
+ }
+ {
+ input := `
+
+STRING __HOSTNAME__
+`
+ expected := `
+
+STRING __FULL_HOSTNAME__
+`
+ actual := ReplaceLineSuffixes(input, "STRING __HOSTNAME__",
"STRING __FULL_HOSTNAME__")
+ if expected != actual {
+ t.Errorf("Expected '%v' Actual '%v'", expected, actual)
+ }
+ }
+ {
+ input := `
+STRING __HOSTNAME__
+ STRING __HOSTNAME__
+`
+ expected := `
+STRING __FULL_HOSTNAME__
+ STRING __FULL_HOSTNAME__
+`
+ actual := ReplaceLineSuffixes(input, "STRING __HOSTNAME__",
"STRING __FULL_HOSTNAME__")
+ if expected != actual {
+ t.Errorf("Expected '%v' Actual '%v'", expected, actual)
+ }
+ }
+ {
+ input := `
+`
+ expected := `
+`
+ actual := ReplaceLineSuffixes(input, "STRING __HOSTNAME__",
"STRING __FULL_HOSTNAME__")
+ if expected != actual {
+ t.Errorf("Expected '%v' Actual '%v'", expected, actual)
+ }
+ }
+ {
+ input := ``
+ expected := ``
+ actual := ReplaceLineSuffixes(input, "STRING __HOSTNAME__",
"STRING __FULL_HOSTNAME__")
+ if expected != actual {
+ t.Errorf("Expected '%v' Actual '%v'", expected, actual)
+ }
+ }
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsprofile/storage.go
b/traffic_ops/traffic_ops_golang/ats/atsprofile/storage.go
new file mode 100644
index 0000000..1a3d687
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsprofile/storage.go
@@ -0,0 +1,88 @@
+package atsprofile
+
+/*
+ * 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 (
+ "database/sql"
+ "errors"
+ "net/http"
+ "strconv"
+ "strings"
+
+ "github.com/apache/trafficcontrol/lib/go-log"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+)
+
+const StorageFileName = "storage.config"
+
+func GetStorage(w http.ResponseWriter, r *http.Request) {
+ WithProfileData(w, r, makeStorage)
+}
+
+func makeStorage(tx *sql.Tx, _ *config.Config, profile ats.ProfileData, _
string) (string, error) {
+ text := ""
+
+ paramData, err := ats.GetProfileParamData(tx, profile.ID,
StorageFileName) // ats.rules is based on the storage.config params
+ if err != nil {
+ return "", errors.New("profile param data: " + err.Error())
+ }
+
+ nextVolume := 1
+ if drivePrefix := paramData["Drive_Prefix"]; drivePrefix != "" {
+ driveLetters := strings.TrimSpace(paramData["Drive_Letters"])
+ if driveLetters == "" {
+ log.Warnf("Creating storage.config: profile %+v has
Drive_Prefix parameter, but no Drive_Letters; creating anyway", profile.Name)
+ }
+ text += makeStorageVolumeText(drivePrefix, driveLetters,
nextVolume)
+ nextVolume++
+ }
+
+ if ramDrivePrefix := paramData["RAM_Drive_Prefix"]; ramDrivePrefix !=
"" {
+ ramDriveLetters :=
strings.TrimSpace(paramData["RAM_Drive_Letters"])
+ if ramDriveLetters == "" {
+ log.Warnf("Creating storage.config: profile %+v has
RAM_Drive_Prefix parameter, but no RAM_Drive_Letters; creating anyway",
profile.Name)
+ }
+ text += makeStorageVolumeText(ramDrivePrefix, ramDriveLetters,
nextVolume)
+ nextVolume++
+ }
+
+ if ssdDrivePrefix := paramData["SSD_Drive_Prefix"]; ssdDrivePrefix !=
"" {
+ ssdDriveLetters :=
strings.TrimSpace(paramData["SSD_Drive_Letters"])
+ if ssdDriveLetters == "" {
+ log.Warnf("Creating storage.config: profile %+v has
SSD_Drive_Prefix parameter, but no SSD_Drive_Letters; creating anyway",
profile.Name)
+ }
+ text += makeStorageVolumeText(ssdDrivePrefix, ssdDriveLetters,
nextVolume)
+ nextVolume++
+ }
+
+ if text == "" {
+ text = "\n" // If no params exist, don't send "not found," but
an empty file. We know the profile exists.
+ }
+ return text, nil
+}
+
+func makeStorageVolumeText(prefix string, letters string, volume int) string {
+ text := ""
+ for _, letter := range strings.Split(letters, ",") {
+ text += prefix + letter + " volume=" + strconv.Itoa(volume) +
"\n"
+ }
+ return text
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsprofile/sysctl.go
b/traffic_ops/traffic_ops_golang/ats/atsprofile/sysctl.go
new file mode 100644
index 0000000..16e5267
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsprofile/sysctl.go
@@ -0,0 +1,46 @@
+package atsprofile
+
+/*
+ * 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 (
+ "database/sql"
+ "net/http"
+
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+)
+
+const SysctlSeparator = " = "
+const SysctlFileName = "sysctl.conf"
+
+func GetSysctl(w http.ResponseWriter, r *http.Request) {
+ WithProfileData(w, r, makeSysctl)
+}
+
+func makeSysctl(tx *sql.Tx, _ *config.Config, profile ats.ProfileData, _
string) (string, error) {
+ txt, err := GenericProfileConfig(tx, profile, SysctlFileName,
SysctlSeparator)
+ if err != nil {
+ return "", err
+ }
+ if txt == "" {
+ txt = "\n" // If no params exist, don't send "not found," but
an empty file. We know the profile exists.
+ }
+ return txt, nil
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsprofile/unknown.go
b/traffic_ops/traffic_ops_golang/ats/atsprofile/unknown.go
new file mode 100644
index 0000000..fc7d12d
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsprofile/unknown.go
@@ -0,0 +1,68 @@
+package atsprofile
+
+/*
+ * 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 (
+ "database/sql"
+ "errors"
+ "net/http"
+ "strings"
+
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+)
+
+func GetUnknown(w http.ResponseWriter, r *http.Request) {
+ addHdr := false
+ WithProfileDataHdr(w, r, addHdr, "text/plain", makeUnknown)
+}
+
+func makeUnknown(tx *sql.Tx, _ *config.Config, profile ats.ProfileData,
fileName string) (string, error) {
+ params, err := ats.GetProfileParamData(tx, profile.ID, fileName)
+ if err != nil {
+ return "", errors.New("getting profile param data: " +
err.Error())
+ }
+ fileContents, err := takeAndBakeProfile(tx, profile.Name, params)
+ if err != nil {
+ return "", errors.New("GetProfileConfig: takeAndBakeProfile '"
+ fileName + "': " + err.Error())
+ }
+ return fileContents, nil
+}
+
+func takeAndBakeProfile(tx *sql.Tx, profileName string, params
map[string]string) (string, error) {
+ hdr, err := ats.HeaderComment(tx, profileName)
+ if err != nil {
+ return "", errors.New("getting header comment: " + err.Error())
+ }
+ text := ""
+ for paramName, paramVal := range params {
+ if paramName == "header" {
+ if paramVal == "none" {
+ hdr = ""
+ } else {
+ hdr = paramVal + "\n"
+ }
+ } else {
+ text += paramVal + "\n"
+ }
+ }
+ text = strings.Replace(text, "__RETURN__", "\n", -1)
+ return hdr + text, nil
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsprofile/urisigning.go
b/traffic_ops/traffic_ops_golang/ats/atsprofile/urisigning.go
new file mode 100644
index 0000000..18a628d
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsprofile/urisigning.go
@@ -0,0 +1,50 @@
+package atsprofile
+
+/*
+ * 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 (
+ "database/sql"
+ "errors"
+ "net/http"
+ "strings"
+
+ "github.com/apache/trafficcontrol/lib/go-tc"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/riaksvc"
+)
+
+func GetURISigning(w http.ResponseWriter, r *http.Request) {
+ addHdr := false
+ contentType := tc.ApplicationJson
+ WithProfileDataHdr(w, r, addHdr, contentType, uriSigningDotConfig)
+}
+
+func uriSigningDotConfig(tx *sql.Tx, cfg *config.Config, _ ats.ProfileData,
fileName string) (string, error) {
+ riakKey := strings.TrimSuffix(strings.TrimPrefix(fileName,
"uri_signing_"), ".config")
+ keys, hasKeys, err := riaksvc.GetURISigningKeysRaw(tx,
cfg.RiakAuthOptions, cfg.RiakPort, riakKey)
+ if err != nil {
+ return "", errors.New("getting uri signing keys from Riak: " +
err.Error())
+ }
+ if !hasKeys {
+ return "", nil // TODO verify? Perl seems to return without
returning its $text
+ }
+ return string(keys), nil
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsprofile/urlsig.go
b/traffic_ops/traffic_ops_golang/ats/atsprofile/urlsig.go
new file mode 100644
index 0000000..09d0278
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsprofile/urlsig.go
@@ -0,0 +1,63 @@
+package atsprofile
+
+/*
+ * 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 (
+ "database/sql"
+ "errors"
+ "net/http"
+ "strings"
+
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/riaksvc"
+)
+
+func GetURLSig(w http.ResponseWriter, r *http.Request) {
+ WithProfileData(w, r, urlSigDotConfig)
+}
+
+func urlSigDotConfig(tx *sql.Tx, cfg *config.Config, profile ats.ProfileData,
fileName string) (string, error) {
+ fileName = "url_sig_" + fileName + ".config" // the fileName from the
http router is just the DS, missing "url_sig_" and ".config" - add them back now
+
+ sep := " = "
+
+ urlSigKeys, _, err := riaksvc.GetURLSigKeysFromConfigFileKey(tx,
cfg.RiakAuthOptions, cfg.RiakPort, fileName)
+ if err != nil {
+ return "", errors.New("getting url sig keys from Riak: " +
err.Error())
+ }
+
+ params, err := ats.GetProfileParamData(tx, profile.ID, fileName)
+ if err != nil {
+ return "", errors.New("getting profile param data: " +
err.Error())
+ }
+
+ text := ""
+ for paramName, paramVal := range params {
+ if len(urlSigKeys) == 0 || !strings.HasPrefix(paramName, "key")
{
+ text += paramName + sep + paramVal + "\n"
+ }
+ }
+
+ for key, val := range urlSigKeys {
+ text += key + sep + val + "\n"
+ }
+ return text, nil
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/atsprofile/volume.go
b/traffic_ops/traffic_ops_golang/ats/atsprofile/volume.go
new file mode 100644
index 0000000..bb177d5
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/atsprofile/volume.go
@@ -0,0 +1,78 @@
+package atsprofile
+
+/*
+ * 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 (
+ "database/sql"
+ "errors"
+ "net/http"
+ "strconv"
+
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/ats"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+)
+
+func GetVolume(w http.ResponseWriter, r *http.Request) {
+ WithProfileData(w, r, makeVolume)
+}
+
+func makeVolume(tx *sql.Tx, _ *config.Config, profile ats.ProfileData, _
string) (string, error) {
+ paramData, err := ats.GetProfileParamData(tx, profile.ID,
StorageFileName) // volume.config is based on the storage.config params
+ if err != nil {
+ return "", errors.New("getting profile param data: " +
err.Error())
+ }
+
+ numVolumes := getNumVolumes(paramData)
+
+ text := "# TRAFFIC OPS NOTE: This is running with forced volumes - the
size is irrelevant\n"
+ nextVolume := 1
+ if drivePrefix := paramData["Drive_Prefix"]; drivePrefix != "" {
+ text += volumeText(strconv.Itoa(nextVolume), numVolumes)
+ nextVolume++
+ }
+ if ramDrivePrefix := paramData["RAM_Drive_Prefix"]; ramDrivePrefix !=
"" {
+ text += volumeText(strconv.Itoa(nextVolume), numVolumes)
+ nextVolume++
+ }
+ if ssdDrivePrefix := paramData["SSD_Drive_Prefix"]; ssdDrivePrefix !=
"" {
+ text += volumeText(strconv.Itoa(nextVolume), numVolumes)
+ nextVolume++
+ }
+
+ if text == "" {
+ text = "\n" // If no params exist, don't send "not found," but
an empty file. We know the profile exists.
+ }
+ return text, nil
+}
+
+func volumeText(volume string, numVolumes int) string {
+ return "volume=" + volume + " scheme=http size=" +
strconv.Itoa(100/numVolumes) + "%\n"
+}
+
+func getNumVolumes(paramData map[string]string) int {
+ num := 0
+ drivePrefixes := []string{"Drive_Prefix", "SSD_Drive_Prefix",
"RAM_Drive_Prefix"}
+ for _, pre := range drivePrefixes {
+ if _, ok := paramData[pre]; ok {
+ num++
+ }
+ }
+ return num
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/config.go
b/traffic_ops/traffic_ops_golang/ats/config.go
index 8e9c1d8..d722fe3 100644
--- a/traffic_ops/traffic_ops_golang/ats/config.go
+++ b/traffic_ops/traffic_ops_golang/ats/config.go
@@ -122,7 +122,7 @@ func getServerNameFromNameOrID(tx *sql.Tx, serverNameOrID
string) (string, error
return serverName, nil, nil, http.StatusOK
}
-func headerComment(tx *sql.Tx, name string) (string, error) {
+func HeaderComment(tx *sql.Tx, name string) (string, error) {
nameVersionStr, err := GetNameVersionString(tx)
if err != nil {
return "", errors.New("getting name version string: " +
err.Error())
diff --git a/traffic_ops/traffic_ops_golang/ats/db.go
b/traffic_ops/traffic_ops_golang/ats/db.go
new file mode 100644
index 0000000..1dbbcf4
--- /dev/null
+++ b/traffic_ops/traffic_ops_golang/ats/db.go
@@ -0,0 +1,172 @@
+package ats
+
+/*
+ * 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 (
+ "database/sql"
+ "errors"
+
+ "github.com/apache/trafficcontrol/lib/go-log"
+ "github.com/apache/trafficcontrol/lib/go-tc"
+)
+
+func GetProfileParamData(tx *sql.Tx, profileID int, configFile string)
(map[string]string, error) {
+ // TODO add another func to return a slice, for things that don't need
a map, for performance? Does it make a difference?
+ qry := `
+SELECT
+ p.name,
+ p.value
+FROM
+ parameter p
+ join profile_parameter pp on p.id = pp.parameter
+ JOIN profile pr on pr.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() {
+ 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("Profile %v has multiple parameters '%v'
assigned! ATS config generation ignoring value '%v'!", profileID, name,
params[name])
+ }
+
+ params[name] = val
+ }
+ return params, nil
+}
+
+type ProfileData struct {
+ ID int
+ Name string
+}
+
+// GetProfileData returns the necessary info about the profile, whether it
exists, and any error.
+func GetProfileData(tx *sql.Tx, id int) (ProfileData, bool, error) {
+ // TODO implement, determine what fields are necessary
+ 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
+}
+
+type ProfileDS struct {
+ Type tc.DSType
+ OriginFQDN *string
+}
+
+func GetProfileDS(tx *sql.Tx, profileID int) ([]ProfileDS, 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 IN (SELECT id FROM server WHERE profile = $1)
+ )
+`
+ rows, err := tx.Query(qry, profileID)
+ if err != nil {
+ return nil, errors.New("querying: " + err.Error())
+ }
+ defer rows.Close()
+
+ dses := []ProfileDS{}
+ for rows.Next() {
+ d := ProfileDS{}
+ 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
+}
+
+// GetProfileParamValue gets the value of a parameter assigned to a profile,
by name and config file.
+// Returns the parameter, whether it existed, and any error.
+func GetProfileParamValue(tx *sql.Tx, profileID int, configFile string, name
string) (string, bool, error) {
+ qry := `
+SELECT
+ p.value
+FROM
+ parameter p
+ JOIN profile_parameter pp ON p.id = pp.parameter
+WHERE
+ pp.profile = $1
+ AND p.config_file = $2
+ AND p.name = $3
+`
+ val := ""
+ if err := tx.QueryRow(qry, profileID, 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`
+ id := 0
+ if err := tx.QueryRow(qry, profileName).Scan(&id); err != nil {
+ if err == sql.ErrNoRows {
+ return 0, false, nil
+ }
+ return 0, false, errors.New("querying: " + err.Error())
+ }
+ return id, true, nil
+}
diff --git a/traffic_ops/traffic_ops_golang/ats/headerrewrite.go
b/traffic_ops/traffic_ops_golang/ats/headerrewrite.go
index 221c9ef..fb4b492 100644
--- a/traffic_ops/traffic_ops_golang/ats/headerrewrite.go
+++ b/traffic_ops/traffic_ops_golang/ats/headerrewrite.go
@@ -46,7 +46,7 @@ func GetEdgeHeaderRewriteDotConfig(w http.ResponseWriter, r
*http.Request) {
return
}
- text, err := headerComment(inf.Tx.Tx, "CDN "+cdnName)
+ text, err := HeaderComment(inf.Tx.Tx, "CDN "+cdnName)
if err != nil {
api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("getting hdr_rw_xml-id.config text: "+err.Error()))
return
@@ -111,7 +111,7 @@ func GetMidHeaderRewriteDotConfig(w http.ResponseWriter, r
*http.Request) {
return
}
- text, err := headerComment(inf.Tx.Tx, "CDN "+cdnName)
+ text, err := HeaderComment(inf.Tx.Tx, "CDN "+cdnName)
if err != nil {
api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("getting hdr_rw_mid_xml-id.config text: "+err.Error()))
return
diff --git a/traffic_ops/traffic_ops_golang/ats/parentdotconfig.go
b/traffic_ops/traffic_ops_golang/ats/parentdotconfig.go
index 982081d..0ab740b 100644
--- a/traffic_ops/traffic_ops_golang/ats/parentdotconfig.go
+++ b/traffic_ops/traffic_ops_golang/ats/parentdotconfig.go
@@ -79,7 +79,7 @@ func GetParentDotConfig(w http.ResponseWriter, r
*http.Request) {
return
}
- hdr, err := headerComment(inf.Tx.Tx, serverInfo.HostName)
+ hdr, err := HeaderComment(inf.Tx.Tx, serverInfo.HostName)
if err != nil {
api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("Getting header comment: "+err.Error()))
return
@@ -480,7 +480,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 ProfileID) (int, error) {
- atsVersion, _, err := GetProfileParamValue(tx, serverProfileID,
"package", "trafficserver")
+ atsVersion, _, err := GetProfileParamValue(tx, int(serverProfileID),
"package", "trafficserver")
if err != nil {
return 0, errors.New("getting profile param value: " +
err.Error())
}
@@ -495,30 +495,6 @@ func GetATSMajorVersion(tx *sql.Tx, serverProfileID
ProfileID) (int, error) {
return atsMajorVer, nil
}
-// GetProfileParamValue gets the value of a parameter assigned to a profile,
by name and config file.
-// Returns the parameter, whether it existed, and any error.
-func GetProfileParamValue(tx *sql.Tx, profileID ProfileID, configFile string,
name string) (string, bool, error) {
- qry := `
-SELECT
- p.value
-FROM
- parameter p
- JOIN profile_parameter pp ON p.id = pp.parameter
-WHERE
- pp.profile = $1
- AND p.config_file = $2
- AND p.name = $3
-`
- val := ""
- if err := tx.QueryRow(qry, profileID, configFile, name).Scan(&val); err
!= nil {
- if err == sql.ErrNoRows {
- return "", false, nil
- }
- return "", false, errors.New("querying: " + err.Error())
- }
- return val, true, nil
-}
-
type ParentConfigDS struct {
Name tc.DeliveryServiceName
QStringIgnore tc.QStringIgnore
diff --git a/traffic_ops/traffic_ops_golang/ats/regexrevalidate.go
b/traffic_ops/traffic_ops_golang/ats/regexrevalidate.go
index 1dc424d..2865f63 100644
--- a/traffic_ops/traffic_ops_golang/ats/regexrevalidate.go
+++ b/traffic_ops/traffic_ops_golang/ats/regexrevalidate.go
@@ -75,7 +75,7 @@ func getRegexRevalidate(tx *sql.Tx, cdnName string) (string,
error) {
return "", errors.New("getting jobs: " + err.Error())
}
- text, err := headerComment(tx, "CDN "+cdnName)
+ text, err := HeaderComment(tx, "CDN "+cdnName)
if err != nil {
return "", errors.New("getting header comment: " + err.Error())
}
diff --git a/traffic_ops/traffic_ops_golang/riaksvc/dsutil.go
b/traffic_ops/traffic_ops_golang/riaksvc/dsutil.go
index b99f0c8..0d4c8bb 100644
--- a/traffic_ops/traffic_ops_golang/riaksvc/dsutil.go
+++ b/traffic_ops/traffic_ops_golang/riaksvc/dsutil.go
@@ -35,6 +35,7 @@ const DNSSECKeysBucket = "dnssec"
const DSSSLKeyVersionLatest = "latest"
const DefaultDSSSLKeyVersion = DSSSLKeyVersionLatest
const URLSigKeysBucket = "url_sig_keys"
+const URISigningKeysBucket = "cdn_uri_sig_keys"
func MakeDSSSLKeyKey(dsName, version string) string {
if version == "" {
@@ -349,3 +350,49 @@ func GetCDNSSLKeysDSNames(tx *sql.Tx, authOpts
*riak.AuthOptions, riakPort *uint
}
return dsVersions, nil
}
+
+// GetURISigningKeysRaw gets the URL Sig keys for the given delivery service,
as the raw bytes stored in Riak.
+func GetURISigningKeysRaw(tx *sql.Tx, authOpts *riak.AuthOptions, riakPort
*uint, key string) ([]byte, bool, error) {
+ val := []byte(nil)
+ found := false
+ err := WithCluster(tx, authOpts, riakPort, func(cluster StorageCluster)
error {
+ ro, err := FetchObjectValues(key, URISigningKeysBucket, cluster)
+ if err != nil {
+ return err
+ }
+ if len(ro) == 0 {
+ return nil // not found
+ }
+ val = ro[0].Value
+ found = true
+ return nil
+ })
+ if err != nil {
+ return nil, false, err
+ }
+ return val, found, nil
+}
+
+// GetURLSigKeysFromKey gets the URL Sig keys from the raw Riak key, which is
the ATS config file name.
+func GetURLSigKeysFromConfigFileKey(tx *sql.Tx, authOpts *riak.AuthOptions,
riakPort *uint, configFileKey string) (tc.URLSigKeys, bool, error) {
+ val := tc.URLSigKeys{}
+ found := false
+ err := WithCluster(tx, authOpts, riakPort, func(cluster StorageCluster)
error {
+ ro, err := FetchObjectValues(configFileKey, URLSigKeysBucket,
cluster)
+ if err != nil {
+ return err
+ }
+ if len(ro) == 0 {
+ return nil // not found
+ }
+ if err := json.Unmarshal(ro[0].Value, &val); err != nil {
+ return errors.New("unmarshalling Riak response: " +
err.Error())
+ }
+ found = true
+ return nil
+ })
+ if err != nil {
+ return val, false, err
+ }
+ return val, found, nil
+}
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go
b/traffic_ops/traffic_ops_golang/routing/routes.go
index 5feac7b..50c19c8 100644
--- a/traffic_ops/traffic_ops_golang/routing/routes.go
+++ b/traffic_ops/traffic_ops_golang/routing/routes.go
@@ -37,6 +37,7 @@ import (
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/apitenant"
"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/auth"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/cachegroup"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/cachesstats"
@@ -379,6 +380,23 @@ func Routes(d ServerData) ([]Route, []RawRoute,
http.Handler, error) {
{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},
+ {1.1, http.MethodGet,
`profiles/{profile-name-or-id}/configfiles/ats/12M_facts/?$`,
atsprofile.GetFacts, auth.PrivLevelOperations, Authenticated, nil},
+ {1.1, http.MethodGet,
`profiles/{profile-name-or-id}/configfiles/ats/50-ats.rules/?$`,
atsprofile.GetATSDotRules, auth.PrivLevelOperations, Authenticated, nil},
+ {1.1, http.MethodGet,
`profiles/{profile-name-or-id}/configfiles/ats/astats.config/?$`,
atsprofile.GetAstats, auth.PrivLevelOperations, Authenticated, nil},
+ {1.1, http.MethodGet,
`profiles/{profile-name-or-id}/configfiles/ats/cache.config/?$`,
atsprofile.GetCache, auth.PrivLevelOperations, Authenticated, nil},
+ {1.1, http.MethodGet,
`profiles/{profile-name-or-id}/configfiles/ats/drop_qstring.config/?$`,
atsprofile.GetDropQString, auth.PrivLevelOperations, Authenticated, nil},
+ {1.1, http.MethodGet,
`profiles/{profile-name-or-id}/configfiles/ats/logging.config/?$`,
atsprofile.GetLogging, auth.PrivLevelOperations, Authenticated, nil},
+ {1.1, http.MethodGet,
`profiles/{profile-name-or-id}/configfiles/ats/logging.yaml/?$`,
atsprofile.GetLoggingYAML, auth.PrivLevelOperations, Authenticated, nil},
+ {1.1, http.MethodGet,
`profiles/{profile-name-or-id}/configfiles/ats/logs_xml.config/?$`,
atsprofile.GetLogsXML, auth.PrivLevelOperations, Authenticated, nil},
+ {1.1, http.MethodGet,
`profiles/{profile-name-or-id}/configfiles/ats/plugin.config/?$`,
atsprofile.GetPlugin, auth.PrivLevelOperations, Authenticated, nil},
+ {1.1, http.MethodGet,
`profiles/{profile-name-or-id}/configfiles/ats/records.config/?$`,
atsprofile.GetRecords, auth.PrivLevelOperations, Authenticated, nil},
+ {1.1, http.MethodGet,
`profiles/{profile-name-or-id}/configfiles/ats/storage.config/?$`,
atsprofile.GetStorage, auth.PrivLevelOperations, Authenticated, nil},
+ {1.1, http.MethodGet,
`profiles/{profile-name-or-id}/configfiles/ats/sysctl.conf/?$`,
atsprofile.GetSysctl, auth.PrivLevelOperations, Authenticated, nil},
+ {1.1, http.MethodGet,
`profiles/{profile-name-or-id}/configfiles/ats/url_sig_{file}.config/?$`,
atsprofile.GetURLSig, auth.PrivLevelOperations, Authenticated, nil},
+ {1.1, http.MethodGet,
`profiles/{profile-name-or-id}/configfiles/ats/uri_signing_{file}.config/?$`,
atsprofile.GetURISigning, auth.PrivLevelOperations, Authenticated, nil},
+ {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},