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 fe12a38608 Add parents to varnish cache (#7669)
fe12a38608 is described below
commit fe12a386082d13d78015b0bbd0558b9414c1b304
Author: AbdelrahmanElawady
<[email protected]>
AuthorDate: Mon Aug 14 20:42:32 2023 +0300
Add parents to varnish cache (#7669)
* Add varnishcfg package and parent configuration
* Add Varnish Dockerfile to be used in CIAB
* Add license to Varnsih Dockerfile
* Add systemctl.sh to handle Varnish service and integrate Varnish with
t3c-apply
* Move host changes to BE fetch, change varnish dir and make test more
readable
* Remove Varnish package release and arch, add GoDoc and move licenses
* Move license text
---
cache-config/t3c-apply/config/config.go | 6 +
cache-config/t3c-apply/t3c-apply.go | 8 +-
cache-config/t3c-apply/torequest/cmd.go | 1 +
cache-config/t3c-apply/torequest/torequest.go | 30 +-
cache-config/t3c-generate/cfgfile/varnish.go | 46 +++
cache-config/t3c-generate/config/config.go | 3 +
cache-config/t3c-generate/t3c-generate.go | 14 +
infrastructure/cdn-in-a-box/enroller/Dockerfile | 3 +
infrastructure/cdn-in-a-box/varnish/Dockerfile | 63 ++++
infrastructure/cdn-in-a-box/varnish/run.sh | 93 ++++++
infrastructure/cdn-in-a-box/varnish/systemctl.sh | 94 ++++++
.../cdn-in-a-box/varnish/traffic_ops_ort.crontab | 18 ++
lib/go-atscfg/parentabstraction.go | 3 +
lib/go-atscfg/parentdotconfig.go | 5 +-
lib/go-atscfg/remapdotconfig.go | 14 +-
lib/go-atscfg/remapdotconfig_test.go | 2 +-
lib/go-atscfg/sslservernamedotyaml.go | 6 +-
lib/go-atscfg/strategiesdotconfig.go | 4 +-
lib/varnishcfg/backends.go | 183 +++++++++++
lib/varnishcfg/backends_test.go | 354 +++++++++++++++++++++
lib/varnishcfg/vcl.go | 101 ++++++
lib/varnishcfg/vclbuilder.go | 75 +++++
22 files changed, 1097 insertions(+), 29 deletions(-)
diff --git a/cache-config/t3c-apply/config/config.go
b/cache-config/t3c-apply/config/config.go
index e7956e1eba..3797c80061 100644
--- a/cache-config/t3c-apply/config/config.go
+++ b/cache-config/t3c-apply/config/config.go
@@ -123,6 +123,7 @@ type Cfg struct {
Version string
GitRevision string
LocalATSVersion string
+ CacheType string
}
func (cfg Cfg) AppVersion() string { return t3cutil.VersionStr(AppName,
cfg.Version, cfg.GitRevision) }
@@ -277,6 +278,7 @@ func GetCfg(appVersion string, gitRevision string) (Cfg,
error) {
defaultClientTLSVersions :=
getopt.StringLong("default-client-tls-versions", 'V', "", "Comma-delimited list
of default TLS versions for Delivery Services with no Parameter, e.g.
--default-tls-versions='1.1,1.2,1.3'. If omitted, all versions are enabled.")
maxmindLocationPtr := getopt.StringLong("maxmind-location", 'M', "",
"URL of a maxmind gzipped database file, to be installed into the trafficserver
etc directory.")
verbosePtr := getopt.CounterLong("verbose", 'v', `Log verbosity.
Logging is output to stderr. By default, errors are logged. To log warnings,
pass '-v'. To log info, pass '-vv'. To omit error logging, see '-s'`)
+ cache := getopt.StringLong("cache", 'T', "ats", "Cache server type.
Generate configuration files for specific cache server type, e.g. 'ats',
'varnish'.")
const silentFlagName = "silent"
silentPtr := getopt.BoolLong(silentFlagName, 's', `Silent. Errors are
not logged, and the 'verbose' flag is ignored. If a fatal error occurs, the
return code will be non-zero but no text will be output to stderr`)
@@ -533,6 +535,9 @@ If any of the related flags are also set, they override the
mode's default behav
if tsHome != "" {
TSHome = tsHome
tsConfigDir = tsHome + "/etc/trafficserver"
+ if cache != nil && *cache == "varnish" {
+ tsConfigDir = tsHome + "/etc/varnish"
+ }
toInfoLog = append(toInfoLog, fmt.Sprintf("TSHome: %s,
TSConfigDir: %s\n", TSHome, tsConfigDir))
}
@@ -612,6 +617,7 @@ If any of the related flags are also set, they override the
mode's default behav
Version: appVersion,
GitRevision: gitRevision,
LocalATSVersion: atsVersionStr,
+ CacheType: *cache,
}
if err = log.InitCfg(cfg); err != nil {
diff --git a/cache-config/t3c-apply/t3c-apply.go
b/cache-config/t3c-apply/t3c-apply.go
index 32692822b6..75876f129a 100644
--- a/cache-config/t3c-apply/t3c-apply.go
+++ b/cache-config/t3c-apply/t3c-apply.go
@@ -220,7 +220,7 @@ func Main() int {
}
} else {
- syncdsUpdate, err = trops.CheckSyncDSState(metaData)
+ syncdsUpdate, err = trops.CheckSyncDSState(metaData, cfg)
if err != nil {
log.Errorln("Checking syncds state: " + err.Error())
return GitCommitAndExit(ExitCodeSyncDSError,
FailureExitMsg, cfg, metaData, oldMetaData)
@@ -241,7 +241,7 @@ func Main() int {
} else if rc == 0 {
log.Infoln("updated the remap.config
for reloading.")
}
- if err := trops.StartServices(&syncdsUpdate,
metaData); err != nil {
+ if err := trops.StartServices(&syncdsUpdate,
metaData, cfg); err != nil {
log.Errorln("failed to start services:
" + err.Error())
metaData.PartialSuccess = true
return
GitCommitAndExit(ExitCodeServicesError, PostConfigFailureExitMsg, cfg,
metaData, oldMetaData)
@@ -311,7 +311,7 @@ func Main() int {
}
}
- if err := trops.StartServices(&syncdsUpdate, metaData); err != nil {
+ if err := trops.StartServices(&syncdsUpdate, metaData, cfg); err != nil
{
log.Errorln("failed to start services: " + err.Error())
metaData.PartialSuccess = true
return GitCommitAndExit(ExitCodeServicesError,
PostConfigFailureExitMsg, cfg, metaData, oldMetaData)
@@ -377,7 +377,7 @@ func GitCommitAndExit(exitCode int, exitMsg string, cfg
config.Cfg, metaData *t3
// so add the old files to the new metadata.
// This is especially important for reval runs, which don't add all
files.
metaData.OwnedFilePaths = t3cutil.CombineOwnedFilePaths(metaData,
oldMetaData)
- if len(metaData.InstalledPackages) == 0 {
+ if len(metaData.InstalledPackages) == 0 && oldMetaData != nil {
metaData.InstalledPackages = oldMetaData.InstalledPackages
}
WriteMetaData(cfg, metaData)
diff --git a/cache-config/t3c-apply/torequest/cmd.go
b/cache-config/t3c-apply/torequest/cmd.go
index efc52a327c..597d19f358 100644
--- a/cache-config/t3c-apply/torequest/cmd.go
+++ b/cache-config/t3c-apply/torequest/cmd.go
@@ -72,6 +72,7 @@ func generate(cfg config.Cfg) ([]t3cutil.ATSConfigFile,
error) {
args := []string{
`generate`,
"--dir=" + cfg.TsConfigDir,
+ "--cache=" + cfg.CacheType,
}
if cfg.LogLocationErr == log.LogLocationNull {
diff --git a/cache-config/t3c-apply/torequest/torequest.go
b/cache-config/t3c-apply/torequest/torequest.go
index 3d6d7e34c9..990b71c852 100644
--- a/cache-config/t3c-apply/torequest/torequest.go
+++ b/cache-config/t3c-apply/torequest/torequest.go
@@ -748,7 +748,7 @@ func (r *TrafficOpsReq) CheckRevalidateState(sleepOverride
bool) (UpdateStatus,
// CheckSyncDSState retrieves and returns the DS Update status from Traffic
Ops.
// The metaData is this run's metadata. It must not be nil, and this function
may add to it.
-func (r *TrafficOpsReq) CheckSyncDSState(metaData *t3cutil.ApplyMetaData)
(UpdateStatus, error) {
+func (r *TrafficOpsReq) CheckSyncDSState(metaData *t3cutil.ApplyMetaData, cfg
config.Cfg) (UpdateStatus, error) {
updateStatus := UpdateTropsNotNeeded
randDispSec := time.Duration(0)
log.Debugln("Checking syncds state.")
@@ -785,7 +785,7 @@ func (r *TrafficOpsReq) CheckSyncDSState(metaData
*t3cutil.ApplyMetaData) (Updat
}
} else if !r.Cfg.IgnoreUpdateFlag {
log.Errorln("no queued update needs to be applied.
Running revalidation before exiting.")
- r.RevalidateWhileSleeping(metaData)
+ r.RevalidateWhileSleeping(metaData, cfg)
return UpdateTropsNotNeeded, nil
} else {
log.Errorln("Traffic Ops is signaling that no update is
waiting to be applied.")
@@ -1091,7 +1091,7 @@ func (r *TrafficOpsReq)
ProcessPackagesWithMetaData(packageMetaData []string) er
return nil
}
-func (r *TrafficOpsReq) RevalidateWhileSleeping(metaData
*t3cutil.ApplyMetaData) (UpdateStatus, error) {
+func (r *TrafficOpsReq) RevalidateWhileSleeping(metaData
*t3cutil.ApplyMetaData, cfg config.Cfg) (UpdateStatus, error) {
updateStatus, err := r.CheckRevalidateState(true)
if err != nil {
return updateStatus, err
@@ -1115,7 +1115,7 @@ func (r *TrafficOpsReq) RevalidateWhileSleeping(metaData
*t3cutil.ApplyMetaData)
t3cutil.WriteActionLog(t3cutil.ActionLogActionUpdateFilesReval,
t3cutil.ActionLogStatusSuccess, metaData)
}
- if err := r.StartServices(&updateStatus, metaData); err != nil {
+ if err := r.StartServices(&updateStatus, metaData, cfg); err !=
nil {
return updateStatus, errors.New("failed to start
services: " + err.Error())
}
@@ -1132,7 +1132,7 @@ func (r *TrafficOpsReq) RevalidateWhileSleeping(metaData
*t3cutil.ApplyMetaData)
// StartServices reloads, restarts, or starts ATS as necessary,
// according to the changed config files and run mode.
// Returns nil on success or any error.
-func (r *TrafficOpsReq) StartServices(syncdsUpdate *UpdateStatus, metaData
*t3cutil.ApplyMetaData) error {
+func (r *TrafficOpsReq) StartServices(syncdsUpdate *UpdateStatus, metaData
*t3cutil.ApplyMetaData, cfg config.Cfg) error {
serviceNeeds := t3cutil.ServiceNeedsNothing
if r.Cfg.ServiceAction == t3cutil.ApplyServiceActionFlagRestart {
serviceNeeds = t3cutil.ServiceNeedsRestart
@@ -1154,13 +1154,17 @@ func (r *TrafficOpsReq) StartServices(syncdsUpdate
*UpdateStatus, metaData *t3cu
serviceNeeds = t3cutil.ServiceNeedsReload
}
}
+ packageName := "trafficserver"
+ if cfg.CacheType == "varnish" {
+ packageName = "varnish"
+ }
- if (serviceNeeds == t3cutil.ServiceNeedsRestart || serviceNeeds ==
t3cutil.ServiceNeedsReload) && !r.IsPackageInstalled("trafficserver") {
+ if (serviceNeeds == t3cutil.ServiceNeedsRestart || serviceNeeds ==
t3cutil.ServiceNeedsReload) && !r.IsPackageInstalled(packageName) {
// TODO try to reload/restart anyway? To allow non-RPM installs?
- return errors.New("trafficserver needs " +
serviceNeeds.String() + " but is not installed.")
+ return errors.New(packageName + " needs " +
serviceNeeds.String() + " but is not installed.")
}
- svcStatus, _, err := util.GetServiceStatus("trafficserver")
+ svcStatus, _, err := util.GetServiceStatus(packageName)
if err != nil {
return errors.New("getting trafficserver service status: " +
err.Error())
}
@@ -1177,7 +1181,7 @@ func (r *TrafficOpsReq) StartServices(syncdsUpdate
*UpdateStatus, metaData *t3cu
if svcStatus != util.SvcRunning {
startStr = "start"
}
- if _, err := util.ServiceStart("trafficserver", startStr); err
!= nil {
+ if _, err := util.ServiceStart(packageName, startStr); err !=
nil {
t3cutil.WriteActionLog(t3cutil.ActionLogActionATSRestart,
t3cutil.ActionLogStatusFailure, metaData)
return errors.New("failed to restart trafficserver")
}
@@ -1204,7 +1208,13 @@ func (r *TrafficOpsReq) StartServices(syncdsUpdate
*UpdateStatus, metaData *t3cu
log.Errorln("ATS configuration has changed. The new
config will be picked up the next time ATS is started.")
} else if serviceNeeds == t3cutil.ServiceNeedsReload {
log.Infoln("ATS configuration has changed, Running
'traffic_ctl config reload' now.")
- if _, _, err :=
util.ExecCommand(config.TSHome+config.TrafficCtl, "config", "reload"); err !=
nil {
+ reloadCommand := config.TSHome + config.TrafficCtl
+ reloadArgs := []string{"config", "reload"}
+ if cfg.CacheType == "varnish" {
+ reloadCommand = "varnishreload"
+ reloadArgs = []string{}
+ }
+ if _, _, err := util.ExecCommand(reloadCommand,
reloadArgs...); err != nil {
t3cutil.WriteActionLog(t3cutil.ActionLogActionATSReload,
t3cutil.ActionLogStatusFailure, metaData)
if *syncdsUpdate == UpdateTropsNeeded {
diff --git a/cache-config/t3c-generate/cfgfile/varnish.go
b/cache-config/t3c-generate/cfgfile/varnish.go
new file mode 100644
index 0000000000..060445a75a
--- /dev/null
+++ b/cache-config/t3c-generate/cfgfile/varnish.go
@@ -0,0 +1,46 @@
+package cfgfile
+
+/*
+ * 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 (
+ "github.com/apache/trafficcontrol/cache-config/t3c-generate/config"
+ "github.com/apache/trafficcontrol/cache-config/t3cutil"
+ "github.com/apache/trafficcontrol/lib/varnishcfg"
+)
+
+// GetVarnishConfigs returns varnish configuration files
+// TODO: add varnishncsa and hitch configs
+func GetVarnishConfigs(toData *t3cutil.ConfigData, cfg config.Cfg)
([]t3cutil.ATSConfigFile, error) {
+ vclBuilder := varnishcfg.NewVCLBuilder(toData)
+ vcl, warnings, err := vclBuilder.BuildVCLFile()
+ logWarnings("Generating varnish configuration files: ", warnings)
+
+ configs := make([]t3cutil.ATSConfigFile, 0)
+ // TODO: should be parameterized and generated from varnishcfg
+ configs = append(configs, t3cutil.ATSConfigFile{
+ Name: "default.vcl",
+ Text: vcl,
+ Path: cfg.Dir,
+ ContentType: "text/plain; charset=us-ascii",
+ LineComment: "//",
+ Secure: false,
+ })
+ return configs, err
+}
diff --git a/cache-config/t3c-generate/config/config.go
b/cache-config/t3c-generate/config/config.go
index 77a01df771..47814756eb 100644
--- a/cache-config/t3c-generate/config/config.go
+++ b/cache-config/t3c-generate/config/config.go
@@ -62,6 +62,7 @@ type Cfg struct {
DefaultTLSVersions []atscfg.TLSVersion
Version string
GitRevision string
+ Cache string
}
func (cfg Cfg) ErrorLog() log.LogLocation { return
log.LogLocation(cfg.LogLocationErr) }
@@ -88,6 +89,7 @@ func GetCfg(appVersion string, gitRevision string) (Cfg,
error) {
atsVersion := getopt.StringLong("ats-version", 'a', "", "The ATS
version, e.g. 9.1.2-42.abc123.el7.x86_64. If omitted, generation will attempt
to get the ATS version from the Server Parameters, and fall back to
lib/go-atscfg.DefaultATSVersion")
verbosePtr := getopt.CounterLong("verbose", 'v', `Log verbosity.
Logging is output to stderr. By default, errors are logged. To log warnings,
pass '-v'. To log info, pass '-vv'. To omit error logging, see '-s'`)
silentPtr := getopt.BoolLong("silent", 's', `Silent. Errors are not
logged, and the 'verbose' flag is ignored. If a fatal error occurs, the return
code will be non-zero but no text will be output to stderr`)
+ cache := getopt.StringLong("cache", 'C', "ats", "Cache server type.
Generate configuration files for specific cache server type, e.g. 'ats',
'varnish'.")
const useStrategiesFlagName = "use-strategies"
const defaultUseStrategies = t3cutil.UseStrategiesFlagFalse
@@ -185,6 +187,7 @@ func GetCfg(appVersion string, gitRevision string) (Cfg,
error) {
GitRevision: gitRevision,
UseStrategies:
t3cutil.UseStrategiesFlag(*useStrategiesPtr),
GoDirect: *goDirectPtr,
+ Cache: *cache,
}
if err := log.InitCfg(cfg); err != nil {
return Cfg{}, errors.New("Initializing loggers: " + err.Error()
+ "\n")
diff --git a/cache-config/t3c-generate/t3c-generate.go
b/cache-config/t3c-generate/t3c-generate.go
index 9211c8ade4..f73646df6c 100644
--- a/cache-config/t3c-generate/t3c-generate.go
+++ b/cache-config/t3c-generate/t3c-generate.go
@@ -85,6 +85,20 @@ func main() {
os.Exit(config.ExitCodeErrGeneric)
}
+ if cfg.Cache == "varnish" {
+ configs, err := cfgfile.GetVarnishConfigs(toData, cfg)
+ if err != nil {
+ log.Errorln("Generating varnish config for'" +
*toData.Server.HostName + "': " + err.Error())
+ os.Exit(config.ExitCodeErrGeneric)
+ }
+ err = cfgfile.WriteConfigs(configs, os.Stdout)
+ if err != nil {
+ log.Errorln("Writing configs for '" +
*toData.Server.HostName + "': " + err.Error())
+ os.Exit(config.ExitCodeErrGeneric)
+ }
+ os.Exit(config.ExitCodeSuccess)
+ }
+
configs, err := cfgfile.GetAllConfigs(toData, cfg)
if err != nil {
log.Errorln("Getting config for'" + *toData.Server.HostName +
"': " + err.Error())
diff --git a/infrastructure/cdn-in-a-box/enroller/Dockerfile
b/infrastructure/cdn-in-a-box/enroller/Dockerfile
index 909a1fb6b7..9c431ae837 100644
--- a/infrastructure/cdn-in-a-box/enroller/Dockerfile
+++ b/infrastructure/cdn-in-a-box/enroller/Dockerfile
@@ -42,6 +42,9 @@ COPY ./traffic_ops/toclientlib/
/go/src/github.com/apache/trafficcontrol/traffic
COPY ./traffic_ops/v4-client/
/go/src/github.com/apache/trafficcontrol/traffic_ops/v4-client/
COPY ./infrastructure/cdn-in-a-box/
/go/src/github.com/apache/trafficcontrol/infrastructure/cdn-in-a-box/
+# varnishcfg requires t3c for ToData struct and not needed for enroller
+RUN rm -rf /go/src/github.com/apache/trafficcontrol/lib/varnishcfg
+
WORKDIR
/go/src/github.com/apache/trafficcontrol/infrastructure/cdn-in-a-box/enroller
RUN set -o errexit -o nounset; \
go clean; \
diff --git a/infrastructure/cdn-in-a-box/varnish/Dockerfile
b/infrastructure/cdn-in-a-box/varnish/Dockerfile
new file mode 100644
index 0000000000..c31e54c47b
--- /dev/null
+++ b/infrastructure/cdn-in-a-box/varnish/Dockerfile
@@ -0,0 +1,63 @@
+# 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.
+
+ARG BASE_IMAGE=rockylinux \
+ RHEL_VERSION=8
+FROM ${BASE_IMAGE}:${RHEL_VERSION} AS common-varnish-cache-config-layers
+ARG RHEL_VERSION=8
+# Makes RHEL_VERSION available at runtime
+ENV RHEL_VERSION="$RHEL_VERSION"
+
+RUN dnf module disable varnish -y && yum install -y epel-release
+
+RUN curl -s
https://packagecloud.io/install/repositories/varnishcache/varnish73/script.rpm.sh
| bash
+
+RUN yum install varnish-7.3.0 -y
+
+RUN dnf install -y bind-utils kyotocabinet-libs initscripts iproute net-tools
nmap-ncat gettext autoconf automake libtool gcc-c++ cronie glibc-devel
openssl-devel git perl && \
+ dnf install -y jq logrotate findutils && \
+ dnf clean all
+
+
+COPY infrastructure/cdn-in-a-box/varnish/run.sh
infrastructure/cdn-in-a-box/traffic_ops/to-access.sh
infrastructure/cdn-in-a-box/enroller/server_template.json /
+
+COPY infrastructure/cdn-in-a-box/dns/set-dns.sh \
+ infrastructure/cdn-in-a-box/dns/insert-self-into-dns.sh \
+ /usr/local/sbin/
+
+
+COPY infrastructure/cdn-in-a-box/varnish/systemctl.sh /usr/bin/systemctl
+
+ARG ORT_RPM=infrastructure/cdn-in-a-box/cache/trafficcontrol-cache-config.rpm
+COPY $ORT_RPM /
+RUN rpm -Uvh /$(basename $ORT_RPM) &&\
+ rm /$(basename $ORT_RPM)
+
+COPY infrastructure/cdn-in-a-box/varnish/traffic_ops_ort.crontab
/etc/cron.d/traffic_ops_ort-cron-template
+
+
+CMD /run.sh
+
+FROM common-varnish-cache-config-layers AS mid
+ENV CACHE_TYPE=mid
+COPY infrastructure/cdn-in-a-box/mid/init.d/ /opt/init.d/
+
+FROM common-varnish-cache-config-layers AS edge
+ENV CACHE_TYPE=edge
+COPY infrastructure/cdn-in-a-box/edge/init.d/ /opt/init.d/
+
+
diff --git a/infrastructure/cdn-in-a-box/varnish/run.sh
b/infrastructure/cdn-in-a-box/varnish/run.sh
new file mode 100755
index 0000000000..eb9ccd4f62
--- /dev/null
+++ b/infrastructure/cdn-in-a-box/varnish/run.sh
@@ -0,0 +1,93 @@
+#!/usr/bin/env bash
+
+# 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.
+
+trap 'echo "Error on line ${LINENO} of ${0}"; exit 1' ERR
+set -o errexit -o nounset -o pipefail -o xtrace -o monitor
+env > /ciab.env
+
+mkdir /tmp/ort
+
+set-dns.sh
+insert-self-into-dns.sh
+
+source /to-access.sh
+
+# Wait on SSL certificate generation
+until [[ -f "$X509_CA_ENV_FILE" ]]
+do
+ echo "Waiting on Shared SSL certificate generation"
+ sleep 3
+done
+
+# Source the CIAB-CA shared SSL environment
+until [[ -v X509_GENERATION_COMPLETE && -n "$X509_GENERATION_COMPLETE" ]]
+do
+ echo "Waiting on X509 vars to be defined"
+ sleep 1
+ source "$X509_CA_ENV_FILE"
+done
+
+# Trust the CIAB-CA at the System level
+cp "$X509_CA_CERT_FULL_CHAIN_FILE" /etc/pki/ca-trust/source/anchors
+update-ca-trust extract
+
+while ! to-ping 2>/dev/null; do
+ echo "waiting for Traffic Ops"
+ sleep 5
+done
+
+export TO_USER=$TO_ADMIN_USER
+export TO_PASSWORD=$TO_ADMIN_PASSWORD
+
+# wait until the CDN has been registered
+found=
+while [[ -z $found ]]; do
+ echo 'waiting for enroller setup'
+ sleep 3
+ found=$(to-get api/4.0/cdns?name="$CDN_NAME" | jq -r '.response[].name')
+done
+
+for f in /opt/init.d/*; do
+ echo "$f"
+ source "$f"
+done
+
+# Wait for SSL keys to exist
+until [[ $(to-get "api/4.0/cdns/name/$CDN_NAME/sslkeys" | jq '.response |
length') -ge 2 ]]; do
+ echo 'waiting for SSL keys to exist'
+ sleep 3
+done
+mkdir -p /tmp/trafficcontrol-cache-config
+mkdir -p /opt/cache/etc/varnish
+
+# hostname is already defined in /etc/init.d/99-run.sh
+hostname="${hostname//-/_}" # replace - with _
+hostname="${hostname^^}" # uppercase
+debug_variable_name="T3C_DEBUG_COMPONENT_${hostname}"
+debug_binary="${!debug_variable_name}"
+if ! type -p "$debug_binary"; then
+ t3c apply --cache=varnish --trafficserver-home=/opt/cache
--run-mode=badass --traffic-ops-url="$TO_URL" --traffic-ops-user="$TO_USER"
--traffic-ops-password="$TO_PASSWORD" --git=yes -vv || { echo "Failed"; }
+fi
+
+envsubst < "/etc/cron.d/traffic_ops_ort-cron-template" >
"/etc/cron.d/traffic_ops_ort-cron" && rm -f
"/etc/cron.d/traffic_ops_ort-cron-template"
+chmod "0644" "/etc/cron.d/traffic_ops_ort-cron" && crontab
"/etc/cron.d/traffic_ops_ort-cron"
+
+crond -im off
+
+varnishlog
diff --git a/infrastructure/cdn-in-a-box/varnish/systemctl.sh
b/infrastructure/cdn-in-a-box/varnish/systemctl.sh
new file mode 100755
index 0000000000..cfe2a55c24
--- /dev/null
+++ b/infrastructure/cdn-in-a-box/varnish/systemctl.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+
+# 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.
+
+VARNISHD_EXECUTABLE="/usr/sbin/varnishd"
+
+is_varnishd_running() {
+ pgrep -x "$(basename "$VARNISHD_EXECUTABLE")" >/dev/null
+}
+
+start_varnishd() {
+ if is_varnishd_running; then
+ echo "varnishd is already running."
+ else
+ echo "Starting varnishd..."
+ "$VARNISHD_EXECUTABLE" -f /opt/cache/etc/varnish/default.vcl
+ echo "varnishd is now running."
+ fi
+}
+
+stop_varnishd() {
+ if is_varnishd_running; then
+ echo "Stopping varnishd..."
+
+ # Send SIGTERM signal to varnishd to terminate gracefully
+ pkill -x "$(basename "$VARNISHD_EXECUTABLE")"
+
+ # Wait for varnishd to stop, giving it a timeout of 10 seconds
+ timeout=10
+ while is_varnishd_running; do
+ if ((timeout-- == 0)); then
+ echo "Timed out waiting for varnishd to stop. Sending SIGKILL..."
+ pkill -9 -x "$(basename "$VARNISHD_EXECUTABLE")"
+ break
+ fi
+ sleep 1
+ done
+
+ if is_varnishd_running; then
+ echo "Failed to stop varnishd."
+ else
+ echo "varnishd is stopped."
+ fi
+ else
+ echo "varnishd is not running."
+ fi
+}
+
+restart_varnishd() {
+ echo "Restarting varnishd..."
+ stop_varnishd
+ start_varnishd
+}
+
+case "$1" in
+ enable)
+ exit 0
+ ;;
+ start)
+ start_varnishd
+ ;;
+ stop)
+ stop_varnishd
+ ;;
+ restart)
+ restart_varnishd
+ ;;
+ status)
+ if is_varnishd_running; then
+ exit 0
+ fi
+ exit 3
+ ;;
+ *)
+ echo "Usage: $0 {start|stop|restart|enable|status}"
+ exit 1
+esac
+
+exit 0
diff --git a/infrastructure/cdn-in-a-box/varnish/traffic_ops_ort.crontab
b/infrastructure/cdn-in-a-box/varnish/traffic_ops_ort.crontab
new file mode 100644
index 0000000000..d830ed0062
--- /dev/null
+++ b/infrastructure/cdn-in-a-box/varnish/traffic_ops_ort.crontab
@@ -0,0 +1,18 @@
+# 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.
+
+*/1 * * * * root t3c apply --cache=varnish --run-mode=syncds
--traffic-ops-url=$TO_URL --traffic-ops-user=$TO_USER
--traffic-ops-password=$TO_PASSWORD --git=yes -vv --cache-host-name=$(hostname
-s) >> /var/log/ort.log 2>&1
diff --git a/lib/go-atscfg/parentabstraction.go
b/lib/go-atscfg/parentabstraction.go
index b827c7ab6b..4c604e58a3 100644
--- a/lib/go-atscfg/parentabstraction.go
+++ b/lib/go-atscfg/parentabstraction.go
@@ -130,6 +130,9 @@ type ParentAbstractionService struct {
// Becomes parent.config weight directive
// Becomes strategies.yaml TODO
Weight float64
+
+ // DS is the delivery service associated with the service
+ DS DeliveryService
}
// ParentAbstractionServices implements sort.Interface
diff --git a/lib/go-atscfg/parentdotconfig.go b/lib/go-atscfg/parentdotconfig.go
index 282d9861d7..1ff5aebcc6 100644
--- a/lib/go-atscfg/parentdotconfig.go
+++ b/lib/go-atscfg/parentdotconfig.go
@@ -141,7 +141,7 @@ func MakeParentDotConfig(
warnings := []string{}
atsMajorVersion := getATSMajorVersion(opt.ATSMajorVersion,
tcServerParams, &warnings)
- parentAbstraction, dataWarns, err := makeParentDotConfigData(
+ parentAbstraction, dataWarns, err := MakeParentDotConfigData(
dses,
server,
servers,
@@ -292,7 +292,7 @@ func createTopology(server *Server, ds DeliveryService,
nameTopologies map[Topol
return topoName, topo, warns
}
-func makeParentDotConfigData(
+func MakeParentDotConfigData(
dses []DeliveryService,
server *Server,
servers []Server,
@@ -1057,6 +1057,7 @@ func getTopologyParentConfigLine(
}
pasvc := &ParentAbstractionService{}
+ pasvc.DS = *ds
pasvc.Name = *ds.XMLID
pasvc.Comment = makeParentComment(addComments, *ds.XMLID, *ds.Topology)
pasvc.DestDomain = orgURI.Hostname()
diff --git a/lib/go-atscfg/remapdotconfig.go b/lib/go-atscfg/remapdotconfig.go
index db610bfcd9..2e059fe38e 100644
--- a/lib/go-atscfg/remapdotconfig.go
+++ b/lib/go-atscfg/remapdotconfig.go
@@ -153,7 +153,7 @@ func MakeRemapDotConfig(
}
cdnDomain := cdn.DomainName
- dsRegexes := makeDSRegexMap(dsRegexArr)
+ dsRegexes := MakeDSRegexMap(dsRegexArr)
// Returned DSes are guaranteed to have a non-nil XMLID, Type, DSCP,
ID, and Active.
dses, dsWarns := remapFilterDSes(server, dss, unfilteredDSes)
warnings = append(warnings, dsWarns...)
@@ -174,7 +174,7 @@ func MakeRemapDotConfig(
}
nameTopologies := makeTopologyNameMap(topologies)
- anyCastPartners := getAnyCastPartners(server, servers)
+ anyCastPartners := GetAnyCastPartners(server, servers)
hdr := makeHdrComment(opt.HdrComment)
txt := ""
@@ -543,7 +543,7 @@ func getServerConfigRemapDotConfigForEdge(
continue
}
- requestFQDNs, err := getDSRequestFQDNs(&ds,
dsRegexes[tc.DeliveryServiceName(*ds.XMLID)], server, anyCastPartners,
cdnDomain)
+ requestFQDNs, err := GetDSRequestFQDNs(&ds,
dsRegexes[tc.DeliveryServiceName(*ds.XMLID)], server, anyCastPartners,
cdnDomain)
if err != nil {
warnings = append(warnings, "error getting ds
'"+*ds.XMLID+"' request fqdns, skipping! Error: "+err.Error())
continue
@@ -1053,7 +1053,7 @@ func (r deliveryServiceRegexesSortByTypeThenSetNum)
Less(i, j int) bool {
}
func (r deliveryServiceRegexesSortByTypeThenSetNum) Swap(i, j int) { r[i],
r[j] = r[j], r[i] }
-func makeDSRegexMap(regexes []tc.DeliveryServiceRegexes)
map[tc.DeliveryServiceName][]tc.DeliveryServiceRegex {
+func MakeDSRegexMap(regexes []tc.DeliveryServiceRegexes)
map[tc.DeliveryServiceName][]tc.DeliveryServiceRegex {
dsRegexMap := map[tc.DeliveryServiceName][]tc.DeliveryServiceRegex{}
for _, dsRegex := range regexes {
sort.Sort(deliveryServiceRegexesSortByTypeThenSetNum(dsRegex.Regexes))
@@ -1062,7 +1062,7 @@ func makeDSRegexMap(regexes []tc.DeliveryServiceRegexes)
map[tc.DeliveryServiceN
return dsRegexMap
}
-func getAnyCastPartners(server *Server, servers []Server) map[string][]string {
+func GetAnyCastPartners(server *Server, servers []Server) map[string][]string {
anyCastIPs := make(map[string][]string)
for _, int := range server.Interfaces {
if int.Name == "lo" {
@@ -1104,8 +1104,8 @@ func (ks keyVals) Less(i, j int) bool {
return ks[i].Val < ks[j].Val
}
-// getDSRequestFQDNs returns the FQDNs that clients will request from the edge.
-func getDSRequestFQDNs(ds *DeliveryService, regexes []tc.DeliveryServiceRegex,
server *Server, anyCastPartners map[string][]string, cdnDomain string)
([]string, error) {
+// GetDSRequestFQDNs returns the FQDNs that clients will request from the edge.
+func GetDSRequestFQDNs(ds *DeliveryService, regexes []tc.DeliveryServiceRegex,
server *Server, anyCastPartners map[string][]string, cdnDomain string)
([]string, error) {
if server.HostName == nil {
return nil, errors.New("server missing hostname")
}
diff --git a/lib/go-atscfg/remapdotconfig_test.go
b/lib/go-atscfg/remapdotconfig_test.go
index a4ee79e70c..22ed2eede8 100644
--- a/lib/go-atscfg/remapdotconfig_test.go
+++ b/lib/go-atscfg/remapdotconfig_test.go
@@ -110,7 +110,7 @@ func TestAnyCastRemapDotConfig(t *testing.T) {
server.Interfaces = []tc.ServerInterfaceInfoV40{}
setIPInfo(server, "lo", "192.168.2.6", "fdf8:f53b:82e4::53")
servers := makeTestAnyCastServers()
- for _, anyCsstServer := range getAnyCastPartners(server, servers) {
+ for _, anyCsstServer := range GetAnyCastPartners(server, servers) {
if len(anyCsstServer) != 2 {
t.Errorf("expected 2 edges in anycast group, actual
'%v'", len(anyCsstServer))
}
diff --git a/lib/go-atscfg/sslservernamedotyaml.go
b/lib/go-atscfg/sslservernamedotyaml.go
index 3d5e0aed05..5bc87aa03e 100644
--- a/lib/go-atscfg/sslservernamedotyaml.go
+++ b/lib/go-atscfg/sslservernamedotyaml.go
@@ -217,7 +217,7 @@ func GetServerSSLData(
return nil, warnings, errors.New("this server missing Profiles")
}
- dsRegexes := makeDSRegexMap(dsRegexArr)
+ dsRegexes := MakeDSRegexMap(dsRegexArr)
parentConfigParamsWithProfiles, err :=
tcParamsToParamsWithProfiles(tcParentConfigParams)
if err != nil {
@@ -241,7 +241,7 @@ func GetServerSSLData(
}
nameTopologies := makeTopologyNameMap(topologies)
- anyCastPartners := getAnyCastPartners(server, servers)
+ anyCastPartners := GetAnyCastPartners(server, servers)
sort.Sort(dsesSortByName(dses))
@@ -262,7 +262,7 @@ func GetServerSSLData(
dsParentConfigParams =
profileParentConfigParams[*ds.ProfileName]
}
- requestFQDNs, err := getDSRequestFQDNs(&ds,
dsRegexes[tc.DeliveryServiceName(*ds.XMLID)], server, anyCastPartners,
cdn.DomainName)
+ requestFQDNs, err := GetDSRequestFQDNs(&ds,
dsRegexes[tc.DeliveryServiceName(*ds.XMLID)], server, anyCastPartners,
cdn.DomainName)
if err != nil {
warnings = append(warnings, "error getting ds
'"+*ds.XMLID+"' request fqdns, skipping! Error: "+err.Error())
continue
diff --git a/lib/go-atscfg/strategiesdotconfig.go
b/lib/go-atscfg/strategiesdotconfig.go
index df2c88cb54..ee51d4d32c 100644
--- a/lib/go-atscfg/strategiesdotconfig.go
+++ b/lib/go-atscfg/strategiesdotconfig.go
@@ -81,7 +81,7 @@ func MakeStrategiesDotYAML(
atsMajorVersion := getATSMajorVersion(opt.ATSMajorVersion,
tcServerParams, &warnings)
- parentAbstraction, dataWarns, err := makeParentDotConfigData(
+ parentAbstraction, dataWarns, err := MakeParentDotConfigData(
dses,
server,
servers,
@@ -99,7 +99,7 @@ func MakeStrategiesDotYAML(
ATSMajorVersion: opt.ATSMajorVersion,
GoDirect: opt.GoDirect,
ParentIsProxy: opt.ParentIsProxy,
- }, // TODO change makeParentDotConfigData to its own opt?
+ }, // TODO change MakeParentDotConfigData to its own opt?
atsMajorVersion,
)
warnings = append(warnings, dataWarns...)
diff --git a/lib/varnishcfg/backends.go b/lib/varnishcfg/backends.go
new file mode 100644
index 0000000000..7ff67796eb
--- /dev/null
+++ b/lib/varnishcfg/backends.go
@@ -0,0 +1,183 @@
+package varnishcfg
+
+/*
+ * 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 (
+ "fmt"
+ "strings"
+
+ "github.com/apache/trafficcontrol/lib/go-atscfg"
+ "github.com/apache/trafficcontrol/lib/go-tc"
+)
+
+func (v *VCLBuilder) configureDirectors(vclFile *vclFile, parents
*atscfg.ParentAbstraction) ([]string, error) {
+ warnings := []string{}
+
+ vclFile.imports = append(vclFile.imports, "directors")
+ var err error
+ requestFQDNs := make([]string, 0)
+
+ for _, svc := range parents.Services {
+ addBackends(vclFile.backends, append(svc.Parents,
svc.SecondaryParents...), svc.DestDomain, svc.Port)
+ addDirectors(vclFile.subroutines, svc)
+
+ requestFQDNs = []string{svc.DestDomain}
+
+ if v.toData.Server.Type == tc.CacheTypeEdge.String() {
+ dsRegexes :=
atscfg.MakeDSRegexMap(v.toData.DeliveryServiceRegexes)
+ anyCastPartners :=
atscfg.GetAnyCastPartners(v.toData.Server, v.toData.Servers)
+ requestFQDNs, err = atscfg.GetDSRequestFQDNs(
+ &svc.DS,
+
dsRegexes[tc.DeliveryServiceName(*svc.DS.XMLID)],
+ v.toData.Server,
+ anyCastPartners,
+ v.toData.CDN.DomainName,
+ )
+ if err != nil {
+ warnings = append(warnings, "error getting ds
'"+*svc.DS.XMLID+"' request fqdns, skipping! Error: "+err.Error())
+ continue
+ }
+ }
+
+ assignBackends(vclFile.subroutines, svc, requestFQDNs)
+ }
+
+ return warnings, nil
+}
+
+func assignBackends(subroutines map[string][]string, svc
*atscfg.ParentAbstractionService, requestFQDNs []string) {
+ lines := make([]string, 0)
+ hostHeaderLines := make([]string, 0)
+
+ conditions := make([]string, 0)
+ backendConditions := make([]string, 0)
+ for _, fqdn := range requestFQDNs {
+ conditions = append(conditions, fmt.Sprintf(`req.http.host ==
"%s"`, fqdn))
+ backendConditions = append(backendConditions,
fmt.Sprintf(`bereq.http.host == "%s"`, fqdn))
+ }
+
+ lines = append(lines, fmt.Sprintf("if (%s) {", strings.Join(conditions,
" || ")))
+ lines = append(lines, fmt.Sprintf("\tset req.backend_hint =
%s.backend();", svc.Name))
+
+ // only change request host from edge servers which typically has
multiple request FQDNs or
+ // one request FQDN that is not the origin.
+ if len(requestFQDNs) > 1 || (len(requestFQDNs) == 1 && requestFQDNs[0]
!= svc.DestDomain) {
+ hostHeaderLines = append(hostHeaderLines, fmt.Sprintf("if (%s)
{", strings.Join(backendConditions, " || ")))
+ hostHeaderLines = append(hostHeaderLines, fmt.Sprintf("\tset
bereq.http.host = \"%s\";", svc.DestDomain))
+ hostHeaderLines = append(hostHeaderLines, "}")
+ }
+
+ lines = append(lines, "}")
+
+ if _, ok := subroutines["vcl_recv"]; !ok {
+ subroutines["vcl_recv"] = make([]string, 0)
+ }
+ subroutines["vcl_recv"] = append(subroutines["vcl_recv"], lines...)
+ if len(hostHeaderLines) == 0 {
+ return
+ }
+
+ if _, ok := subroutines["vcl_backend_fetch"]; !ok {
+ subroutines["vcl_backend_fetch"] = make([]string, 0)
+ }
+ subroutines["vcl_backend_fetch"] =
append(subroutines["vcl_backend_fetch"], hostHeaderLines...)
+}
+
+func addBackends(backends map[string]backend, parents
[]*atscfg.ParentAbstractionServiceParent, originDomain string, originPort int) {
+ for _, parent := range parents {
+ backendName := fmt.Sprintf("%s", getBackendName(parent.FQDN,
parent.Port))
+ if _, ok := backends[backendName]; ok {
+ continue
+ }
+ backends[backendName] = backend{
+ host: parent.FQDN,
+ port: parent.Port,
+ }
+ }
+ backendName := getBackendName(originDomain, originPort)
+ if _, ok := backends[backendName]; ok {
+ return
+ }
+ backends[backendName] = backend{
+ host: originDomain,
+ port: originPort,
+ }
+}
+
+func addDirectors(subroutines map[string][]string, svc
*atscfg.ParentAbstractionService) {
+ lines := make([]string, 0)
+ fallbackDirectorLines := make([]string, 0)
+ fallbackDirectorLines = append(fallbackDirectorLines, fmt.Sprintf("new
%s = directors.fallback();", svc.Name))
+
+ if len(svc.Parents) != 0 {
+ lines = append(lines,
addBackendsToDirector(svc.Name+"_primary", svc.RetryPolicy, svc.Parents)...)
+ fallbackDirectorLines = append(fallbackDirectorLines,
fmt.Sprintf("%s.add_backend(%s_primary.backend());", svc.Name, svc.Name))
+ }
+ if len(svc.SecondaryParents) != 0 {
+ lines = append(lines,
addBackendsToDirector(svc.Name+"_secondary", svc.RetryPolicy,
svc.SecondaryParents)...)
+ fallbackDirectorLines = append(fallbackDirectorLines,
fmt.Sprintf("%s.add_backend(%s_secondary.backend());", svc.Name, svc.Name))
+ }
+ fallbackDirectorLines = append(fallbackDirectorLines,
fmt.Sprintf("%s.add_backend(%s);", svc.Name, getBackendName(svc.DestDomain,
svc.Port)))
+
+ lines = append(lines, fallbackDirectorLines...)
+
+ if _, ok := subroutines["vcl_init"]; !ok {
+ subroutines["vcl_init"] = make([]string, 0)
+ }
+ subroutines["vcl_init"] = append(subroutines["vcl_init"], lines...)
+}
+
+func addBackendsToDirector(name string, retryPolicy
atscfg.ParentAbstractionServiceRetryPolicy, parents
[]*atscfg.ParentAbstractionServiceParent) []string {
+ lines := make([]string, 0)
+ directorType, sticky := getDirectorType(retryPolicy)
+ lines = append(lines, fmt.Sprintf("new %s = directors.%s(%s);", name,
directorType, sticky))
+ for _, parent := range parents {
+ lines = append(lines, fmt.Sprintf("%s.add_backend(%s);", name,
getBackendName(parent.FQDN, parent.Port)))
+ }
+ return lines
+}
+
+func getDirectorType(retryPolicy atscfg.ParentAbstractionServiceRetryPolicy)
(director string, sticky string) {
+ switch retryPolicy {
+ case atscfg.ParentAbstractionServiceRetryPolicyRoundRobinIP:
+ fallthrough
+ case atscfg.ParentAbstractionServiceRetryPolicyRoundRobinStrict:
+ director = "round_robin"
+ case atscfg.ParentAbstractionServiceRetryPolicyFirst:
+ director = "fallback"
+ case atscfg.ParentAbstractionServiceRetryPolicyLatched:
+ director = "fallback"
+ sticky = "1"
+ case atscfg.ParentAbstractionServiceRetryPolicyConsistentHash:
+ director = "shard"
+ default:
+ director = "shard"
+ }
+ return
+}
+
+func getBackendName(host string, port int) string {
+ // maybe a better way to ensure backend names are unique?
+
+ if port <= 0 {
+ return strings.ReplaceAll(host, ".", "_")
+ }
+ return fmt.Sprintf("%s_%d", strings.ReplaceAll(host, ".", "_"), port)
+}
diff --git a/lib/varnishcfg/backends_test.go b/lib/varnishcfg/backends_test.go
new file mode 100644
index 0000000000..276ef10f40
--- /dev/null
+++ b/lib/varnishcfg/backends_test.go
@@ -0,0 +1,354 @@
+package varnishcfg
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/apache/trafficcontrol/lib/go-atscfg"
+)
+
+func TestAddBackends(t *testing.T) {
+ testCases := []struct {
+ name string
+ backends map[string]backend
+ parents []*atscfg.ParentAbstractionServiceParent
+ originDomain string
+ originPort int
+ expectedBackends map[string]backend
+ }{
+ {
+ name: "no parents",
+ backends: make(map[string]backend),
+ parents:
[]*atscfg.ParentAbstractionServiceParent{},
+ originDomain: "origin.example.com",
+ originPort: 80,
+ expectedBackends: map[string]backend{
+ "origin_example_com_80": {
+ host: "origin.example.com",
+ port: 80,
+ },
+ },
+ },
+ {
+ name: "single parent",
+ backends: make(map[string]backend),
+ parents: []*atscfg.ParentAbstractionServiceParent{
+ {FQDN: "parent.example.com", Port: 444},
+ },
+ originDomain: "origin.example.com",
+ originPort: 80,
+ expectedBackends: map[string]backend{
+ "parent_example_com_444": {
+ host: "parent.example.com",
+ port: 444,
+ },
+ "origin_example_com_80": {
+ host: "origin.example.com",
+ port: 80,
+ },
+ },
+ },
+ {
+ name: "multiple parent",
+ backends: make(map[string]backend),
+ parents: []*atscfg.ParentAbstractionServiceParent{
+ {FQDN: "parent.example.com", Port: 444},
+ {FQDN: "parent2.example.com", Port: 80},
+ },
+ originDomain: "origin.example.com",
+ originPort: 80,
+ expectedBackends: map[string]backend{
+ "parent_example_com_444": {
+ host: "parent.example.com",
+ port: 444,
+ },
+ "parent2_example_com_80": {
+ host: "parent2.example.com",
+ port: 80,
+ },
+ "origin_example_com_80": {
+ host: "origin.example.com",
+ port: 80,
+ },
+ },
+ },
+ {
+ name: "already added parents",
+ backends: map[string]backend{
+ "parent_example_com_444": {
+ host: "parent.example.com",
+ port: 444,
+ },
+ "origin_example_com_80": {
+ host: "origin.example.com",
+ port: 80,
+ },
+ },
+ parents: []*atscfg.ParentAbstractionServiceParent{
+ {FQDN: "parent.example.com", Port: 444},
+ },
+ originDomain: "origin.example.com",
+ originPort: 80,
+ expectedBackends: map[string]backend{
+ "parent_example_com_444": {
+ host: "parent.example.com",
+ port: 444,
+ },
+ "origin_example_com_80": {
+ host: "origin.example.com",
+ port: 80,
+ },
+ },
+ },
+ }
+ for _, tC := range testCases {
+ t.Run(tC.name, func(t *testing.T) {
+ addBackends(tC.backends, tC.parents, tC.originDomain,
tC.originPort)
+ if !reflect.DeepEqual(tC.expectedBackends, tC.backends)
{
+ t.Errorf("expected %v got %v",
tC.expectedBackends, tC.backends)
+ }
+ })
+ }
+}
+
+func TestAddBackendsToDirector(t *testing.T) {
+ testCases := []struct {
+ name string
+ directorName string
+ retryPolicy atscfg.ParentAbstractionServiceRetryPolicy
+ parents []*atscfg.ParentAbstractionServiceParent
+ expectedLines []string
+ }{
+ {
+ name: "round robin",
+ directorName: "dir",
+ retryPolicy:
atscfg.ParentAbstractionServiceRetryPolicyRoundRobinStrict,
+ parents: []*atscfg.ParentAbstractionServiceParent{
+ {FQDN: "parent.example.com", Port: 80},
+ {FQDN: "parent2.example.com", Port: 80},
+ },
+ expectedLines: []string{
+ `new dir = directors.round_robin();`,
+ `dir.add_backend(parent_example_com_80);`,
+ `dir.add_backend(parent2_example_com_80);`,
+ },
+ },
+ {
+ name: "fallback",
+ directorName: "dir",
+ retryPolicy:
atscfg.ParentAbstractionServiceRetryPolicyFirst,
+ parents: []*atscfg.ParentAbstractionServiceParent{
+ {FQDN: "parent.example.com", Port: 80},
+ {FQDN: "parent2.example.com", Port: 80},
+ },
+ expectedLines: []string{
+ `new dir = directors.fallback();`,
+ `dir.add_backend(parent_example_com_80);`,
+ `dir.add_backend(parent2_example_com_80);`,
+ },
+ },
+ {
+ name: "fallback sticky",
+ directorName: "dir",
+ retryPolicy:
atscfg.ParentAbstractionServiceRetryPolicyLatched,
+ parents: []*atscfg.ParentAbstractionServiceParent{
+ {FQDN: "parent.example.com", Port: 80},
+ {FQDN: "parent2.example.com", Port: 80},
+ },
+ expectedLines: []string{
+ `new dir = directors.fallback(1);`,
+ `dir.add_backend(parent_example_com_80);`,
+ `dir.add_backend(parent2_example_com_80);`,
+ },
+ },
+ }
+ for _, tC := range testCases {
+ t.Run(tC.name, func(t *testing.T) {
+ lines := addBackendsToDirector(tC.directorName,
tC.retryPolicy, tC.parents)
+ if !reflect.DeepEqual(tC.expectedLines, lines) {
+ t.Errorf("expected %v got %v",
tC.expectedLines, lines)
+ }
+ })
+ }
+}
+
+func TestAddDirectors(t *testing.T) {
+ testCases := []struct {
+ name string
+ subroutines map[string][]string
+ svc *atscfg.ParentAbstractionService
+ expectedSubroutines map[string][]string
+ }{
+ {
+ name: "no parents",
+ subroutines: make(map[string][]string),
+ svc: &atscfg.ParentAbstractionService{
+ Name: "demo",
+ RetryPolicy:
atscfg.ParentAbstractionServiceRetryPolicyConsistentHash,
+ Parents:
[]*atscfg.ParentAbstractionServiceParent{},
+ DestDomain: "origin.example.com",
+ Port: 80,
+ },
+ expectedSubroutines: map[string][]string{
+ "vcl_init": {
+ `new demo = directors.fallback();`,
+
`demo.add_backend(origin_example_com_80);`,
+ },
+ },
+ },
+ {
+ name: "primary parents",
+ subroutines: make(map[string][]string),
+ svc: &atscfg.ParentAbstractionService{
+ Name: "demo",
+ RetryPolicy:
atscfg.ParentAbstractionServiceRetryPolicyConsistentHash,
+ Parents:
[]*atscfg.ParentAbstractionServiceParent{
+ {FQDN: "parent.example.com", Port: 80},
+ },
+ DestDomain: "origin.example.com",
+ Port: 80,
+ },
+ expectedSubroutines: map[string][]string{
+ "vcl_init": {
+ `new demo_primary = directors.shard();`,
+
`demo_primary.add_backend(parent_example_com_80);`,
+ `new demo = directors.fallback();`,
+
`demo.add_backend(demo_primary.backend());`,
+
`demo.add_backend(origin_example_com_80);`,
+ },
+ },
+ },
+ {
+ name: "primary and secondary parents",
+ subroutines: make(map[string][]string),
+ svc: &atscfg.ParentAbstractionService{
+ Name: "demo",
+ RetryPolicy:
atscfg.ParentAbstractionServiceRetryPolicyLatched,
+ Parents:
[]*atscfg.ParentAbstractionServiceParent{
+ {FQDN: "parent.example.com", Port: 80},
+ },
+ SecondaryParents:
[]*atscfg.ParentAbstractionServiceParent{
+ {FQDN: "parent2.example.com", Port: 80},
+ },
+ DestDomain: "origin.example.com",
+ Port: 80,
+ },
+ expectedSubroutines: map[string][]string{
+ "vcl_init": {
+ `new demo_primary =
directors.fallback(1);`,
+
`demo_primary.add_backend(parent_example_com_80);`,
+ `new demo_secondary =
directors.fallback(1);`,
+
`demo_secondary.add_backend(parent2_example_com_80);`,
+ `new demo = directors.fallback();`,
+
`demo.add_backend(demo_primary.backend());`,
+
`demo.add_backend(demo_secondary.backend());`,
+
`demo.add_backend(origin_example_com_80);`,
+ },
+ },
+ },
+ }
+ for _, tC := range testCases {
+ t.Run(tC.name, func(t *testing.T) {
+ addDirectors(tC.subroutines, tC.svc)
+ if !reflect.DeepEqual(tC.expectedSubroutines,
tC.subroutines) {
+ t.Errorf("expected %v got %v",
tC.expectedSubroutines, tC.subroutines)
+ }
+ })
+ }
+}
+
+func TestAssignBackends(t *testing.T) {
+ testCases := []struct {
+ name string
+ subroutines map[string][]string
+ svc *atscfg.ParentAbstractionService
+ requestFQDNs []string
+ expectedSubroutines map[string][]string
+ }{
+ {
+ name: "edge with one request FQDN",
+ subroutines: make(map[string][]string),
+ svc: &atscfg.ParentAbstractionService{
+ Name: "demo",
+ DestDomain: "origin.example.com",
+ },
+ requestFQDNs: []string{"example.com"},
+ expectedSubroutines: map[string][]string{
+ "vcl_recv": {
+ `if (req.http.host == "example.com") {`,
+ ` set req.backend_hint =
demo.backend();`,
+ `}`,
+ },
+ "vcl_backend_fetch": {
+ `if (bereq.http.host == "example.com")
{`,
+ ` set bereq.http.host =
"origin.example.com";`,
+ `}`,
+ },
+ },
+ },
+ {
+ name: "edge with multiple request FQDNs",
+ subroutines: make(map[string][]string),
+ svc: &atscfg.ParentAbstractionService{
+ Name: "demo",
+ DestDomain: "origin.example.com",
+ },
+ requestFQDNs: []string{"example.com",
"another.example.com"},
+ expectedSubroutines: map[string][]string{
+ "vcl_recv": {
+ `if (req.http.host == "example.com" ||
req.http.host == "another.example.com") {`,
+ ` set req.backend_hint =
demo.backend();`,
+ `}`,
+ },
+ "vcl_backend_fetch": {
+ `if (bereq.http.host == "example.com"
|| bereq.http.host == "another.example.com") {`,
+ ` set bereq.http.host =
"origin.example.com";`,
+ `}`,
+ },
+ },
+ },
+ {
+ name: "mid",
+ subroutines: make(map[string][]string),
+ svc: &atscfg.ParentAbstractionService{
+ Name: "demo",
+ DestDomain: "origin.example.com",
+ },
+ requestFQDNs: []string{"origin.example.com"},
+ expectedSubroutines: map[string][]string{
+ "vcl_recv": {
+ `if (req.http.host ==
"origin.example.com") {`,
+ ` set req.backend_hint =
demo.backend();`,
+ `}`,
+ },
+ },
+ },
+ }
+ for _, tC := range testCases {
+ t.Run(tC.name, func(t *testing.T) {
+ assignBackends(tC.subroutines, tC.svc, tC.requestFQDNs)
+ if !reflect.DeepEqual(tC.expectedSubroutines,
tC.subroutines) {
+ t.Errorf("expected %v got %v",
tC.expectedSubroutines, tC.subroutines)
+ }
+ })
+ }
+}
diff --git a/lib/varnishcfg/vcl.go b/lib/varnishcfg/vcl.go
new file mode 100644
index 0000000000..5608b21ff8
--- /dev/null
+++ b/lib/varnishcfg/vcl.go
@@ -0,0 +1,101 @@
+package varnishcfg
+
+/*
+ * 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 "fmt"
+
+const defaultVCLVersion = "4.1"
+
+// vclFile contains all VCL components
+type vclFile struct {
+ version string
+ imports []string
+ acls map[string][]string
+ backends map[string]backend
+ subroutines map[string][]string
+}
+
+func newVCLFile(version string) vclFile {
+ return vclFile{
+ version: version,
+ imports: make([]string, 0),
+ acls: make(map[string][]string),
+ backends: make(map[string]backend),
+ subroutines: make(map[string][]string),
+ }
+}
+
+func (v vclFile) String() string {
+ txt := fmt.Sprintf("vcl %s;\n", v.version)
+ for _, i := range v.imports {
+ txt += fmt.Sprintf("import %s;\n", i)
+ }
+
+ for name, backend := range v.backends {
+ txt += fmt.Sprintf("backend %s {\n", name)
+ txt += fmt.Sprint(backend)
+ txt += fmt.Sprint("}\n")
+ }
+ // varnishd will fail if there are no backends defined
+ if len(v.backends) == 0 {
+ txt += fmt.Sprint("backend default none;\n")
+ }
+
+ for name, acl := range v.acls {
+ txt += fmt.Sprintf("acl %s {\n", name)
+ for _, entry := range acl {
+ txt += fmt.Sprintf("\t%s\n", entry)
+ }
+ txt += fmt.Sprint("}\n")
+ }
+
+ // has to be before other subroutines for variables initialization
+ if _, ok := v.subroutines["vcl_init"]; ok {
+ txt += fmt.Sprint("sub vcl_init {\n")
+ for _, entry := range v.subroutines["vcl_init"] {
+ txt += fmt.Sprintf("\t%s\n", entry)
+ }
+ txt += fmt.Sprint("}\n")
+ }
+
+ for name, subroutine := range v.subroutines {
+ if name == "vcl_init" {
+ continue
+ }
+ txt += fmt.Sprintf("sub %s {\n", name)
+ for _, entry := range subroutine {
+ txt += fmt.Sprintf("\t%s\n", entry)
+ }
+ txt += fmt.Sprint("}\n")
+ }
+
+ return txt
+}
+
+type backend struct {
+ host string
+ port int
+}
+
+func (b backend) String() string {
+ txt := fmt.Sprintf("\t.host = \"%s\";\n", b.host)
+ txt += fmt.Sprintf("\t.port = \"%d\";\n", b.port)
+ return txt
+}
diff --git a/lib/varnishcfg/vclbuilder.go b/lib/varnishcfg/vclbuilder.go
new file mode 100644
index 0000000000..a40221c87f
--- /dev/null
+++ b/lib/varnishcfg/vclbuilder.go
@@ -0,0 +1,75 @@
+// Package varnishcfg manages generating configuration files
+// for Varnish cache and Hitch proxy using data from Traffic Ops APIs.
+package varnishcfg
+
+/*
+ * 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 (
+ "fmt"
+ "strings"
+
+ "github.com/apache/trafficcontrol/cache-config/t3cutil"
+ "github.com/apache/trafficcontrol/lib/go-atscfg"
+)
+
+// VCLBuilder builds the default VCL file using TO data.
+type VCLBuilder struct {
+ toData *t3cutil.ConfigData
+ // opts
+}
+
+// NewVCLBuilder returns a new VCLBuilder object.
+func NewVCLBuilder(toData *t3cutil.ConfigData) VCLBuilder {
+ return VCLBuilder{
+ toData: toData,
+ }
+}
+
+// BuildVCLFile builds the default VCL file.
+func (vb *VCLBuilder) BuildVCLFile() (string, []string, error) {
+ warnings := make([]string, 0)
+ v := newVCLFile(defaultVCLVersion)
+
+ atsMajorVersion := uint(9)
+
+ parents, dataWarns, err := atscfg.MakeParentDotConfigData(
+ vb.toData.DeliveryServices,
+ vb.toData.Server,
+ vb.toData.Servers,
+ vb.toData.Topologies,
+ vb.toData.ServerParams,
+ vb.toData.ParentConfigParams,
+ vb.toData.ServerCapabilities,
+ vb.toData.DSRequiredCapabilities,
+ vb.toData.CacheGroups,
+ vb.toData.DeliveryServiceServers,
+ vb.toData.CDN,
+ &atscfg.ParentConfigOpts{},
+ atsMajorVersion,
+ )
+ warnings = append(warnings, dataWarns...)
+ if err != nil {
+ return "", nil, fmt.Errorf("(warnings: %s) %w",
strings.Join(warnings, ", "), err)
+ }
+
+ dirWarnings, err := vb.configureDirectors(&v, parents)
+ warnings = append(warnings, dirWarnings...)
+ return fmt.Sprint(v), warnings, err
+}