blob: 997003e1b38e7c116fa5fe0e0113d012adc0b892 [file] [log] [blame]
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 (
"os/exec"
"strings"
"github.com/apache/trafficcontrol/lib/go-tc"
)
const RecordsSeparator = " "
const RecordsFileName = "records.config"
const ContentTypeRecordsDotConfig = ContentTypeTextASCII
const LineCommentRecordsDotConfig = LineCommentHash
type RecordsConfigOpts struct {
// ReleaseViaStr is whether or not we replace the via and server strings in ATS
// responses to be the Release value from the rpm package. This can be a user
// defined build hash (or whatever the user wants) type value to give more
// specific info as well as obfuscating the real ATS version from prying eyes
ReleaseViaStr bool
// DNSLocalBindServiceAddr is whether to set the server's service addresses
// as the records.config proxy.config.dns.local_ipv* settings.
DNSLocalBindServiceAddr bool
// HdrComment is the header comment to include at the beginning of the file.
// This should be the text desired, without comment syntax (like # or //). The file's comment syntax will be added.
// To omit the header comment, pass the empty string.
HdrComment string
// NoOutgoingIP is whether to omit adding a records.config entry for
// proxy.local.outgoing_ip_to_bind set to the server's IP addresses (V4 and V6).
// By default, this entry is added, unless it already exists in records.config
// (probably from a Parameter).
//
// The default, setting the IP to bind, is usually the right solution, unless
// the server's addresses are unusual or not public, such as NAT.
NoOutgoingIP bool
}
func MakeRecordsDotConfig(
server *Server,
serverParams []tc.Parameter,
opt *RecordsConfigOpts,
) (Cfg, error) {
if opt == nil {
opt = &RecordsConfigOpts{}
}
warnings := []string{}
if len(server.ProfileNames) == 0 {
return Cfg{}, makeErr(warnings, "server missing profiles")
}
params, paramWarns := paramsToMap(filterParams(serverParams, RecordsFileName, "", "", "location"))
warnings = append(warnings, paramWarns...)
hdr := makeHdrComment(opt.HdrComment)
txt := genericProfileConfig(params, RecordsSeparator)
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__")
txt = hdr + txt
txt, overrideWarns := addRecordsDotConfigOverrides(txt, server, opt)
warnings = append(warnings, overrideWarns...)
return Cfg{
Text: txt,
ContentType: ContentTypeRecordsDotConfig,
LineComment: LineCommentRecordsDotConfig,
Warnings: warnings,
}, nil
}
// addRecordsDotConfigOverrides modifies the records.config text and adds any overrides.
// Returns the modified text and any warnings.
func addRecordsDotConfigOverrides(txt string, server *Server, opt *RecordsConfigOpts) (string, []string) {
warnings := []string{}
if !opt.NoOutgoingIP {
ipWarns := []string{}
txt, ipWarns = addRecordsDotConfigOutgoingIP(txt, server)
warnings = append(warnings, ipWarns...)
}
if opt.ReleaseViaStr {
viaWarns := []string{}
txt, viaWarns = addRecordsDotConfigViaStr(txt)
warnings = append(warnings, viaWarns...)
}
if opt.DNSLocalBindServiceAddr {
dnsWarns := []string{}
txt, dnsWarns = addRecordsDotConfigDNSLocal(txt, server)
warnings = append(warnings, dnsWarns...)
}
return txt, warnings
}
// addRecordsDotConfigOutgoingIP returns the outgoing IP added to the config text, and any warnings.
func addRecordsDotConfigOutgoingIP(txt string, server *Server) (string, []string) {
warnings := []string{}
outgoingIPConfig := `proxy.local.outgoing_ip_to_bind`
if strings.Contains(txt, outgoingIPConfig) {
warnings = append(warnings, "records.config had a proxy.local.outgoing_ip_to_bind Parameter! Using Parameter, not setting Outgoing IP from Server")
return txt, warnings
}
v4, v6 := getServiceAddresses(server)
if v4 == nil {
warnings = append(warnings, "server had no IPv4 service address, cannot set "+outgoingIPConfig+"!")
return txt, warnings
}
txt = txt + `LOCAL ` + outgoingIPConfig + ` STRING ` + v4.String()
if v6 != nil {
txt += ` [` + v6.String() + `]`
}
txt += "\n"
return txt, warnings
}
// addRecordsDotConfigViaStr returns the request, response, and response server via strings with the current Release (a.k.a. build version and not ATS version), and any warnings.
func addRecordsDotConfigViaStr(txt string) (string, []string) {
warnings := []string{}
requestViaStr := `proxy.config.http.request_via_str`
responseViaStr := `proxy.config.http.response_via_str`
responseServerStr := `proxy.config.http.response_server_str`
cmd := "yum info installed trafficserver | grep Release"
yumOutput, err := exec.Command("sh", "-c", cmd).Output()
if err != nil {
warnings = append(warnings, "could not read trafficserver release information from yum! Not setting via strings")
return txt, warnings
}
releaseVerSlice := strings.Split(string(yumOutput), " ")
releaseVer := releaseVerSlice[len(releaseVerSlice)-1]
if strings.Contains(txt, requestViaStr) {
warnings = append(warnings, "records.config had a proxy.config.http.request_via_str Parameter! Using Parameter, not setting request via string")
} else {
txt = txt + `CONFIG ` + requestViaStr + ` STRING ` + releaseVer
txt += "\n"
}
if strings.Contains(txt, responseViaStr) {
warnings = append(warnings, "records.config had a proxy.config.http.response_via_str Parameter! Using Parameter, not setting response via string")
} else {
txt = txt + `CONFIG ` + responseViaStr + ` STRING ` + releaseVer
txt += "\n"
}
if strings.Contains(txt, responseServerStr) {
warnings = append(warnings, "records.config had a proxy.config.http.response_server_str Parameter! Using Parameter, not setting response server string")
} else {
txt = txt + `CONFIG ` + responseServerStr + ` STRING ` + releaseVer
txt += "\n"
}
return txt, warnings
}
func addRecordsDotConfigDNSLocal(txt string, server *Server) (string, []string) {
warnings := []string{}
const dnsLocalV4 = `proxy.config.dns.local_ipv4`
const dnsLocalV6 = `proxy.config.dns.local_ipv6`
v4, v6 := getServiceAddresses(server)
if v4 == nil {
warnings = append(warnings, "server had no IPv4 Service Address, not setting records.config dns v4 local bind addr!")
} else if strings.Contains(txt, dnsLocalV4) {
warnings = append(warnings, "dns local option was set, but proxy.config.dns.local_ipv4 was already in records.config, not overriding! Check the server's Parameters.")
} else {
txt += `CONFIG ` + dnsLocalV4 + ` STRING ` + v4.String() + "\n"
}
if v6 == nil {
warnings = append(warnings, "server had no IPv6 Service Address, not setting records.config dns v6 local bind addr!")
} else if strings.Contains(txt, dnsLocalV6) {
warnings = append(warnings, "dns local option was set, but proxy.config.dns.local_ipv6 was already in records.config, not overriding! Check the server's Parameters!")
} else {
txt += `CONFIG ` + dnsLocalV6 + ` STRING [` + v6.String() + `]` + "\n"
}
return txt, warnings
}
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")
}