This is an automated email from the ASF dual-hosted git repository.

friede 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 6f527a10b0 Configure uncacheable delivery services in Varnish (#7771)
6f527a10b0 is described below

commit 6f527a10b09d9b043c881d0998a40b8315ce83f6
Author: AbdelrahmanElawady 
<[email protected]>
AuthorDate: Tue Sep 12 19:45:12 2023 +0300

    Configure uncacheable delivery services in Varnish (#7771)
---
 lib/go-atscfg/cachedotconfig.go      | 121 ++++++++++++++++++++---------------
 lib/varnishcfg/cache_control.go      |  57 +++++++++++++++++
 lib/varnishcfg/cache_control_test.go |  90 ++++++++++++++++++++++++++
 lib/varnishcfg/vclbuilder.go         |  10 +++
 4 files changed, 227 insertions(+), 51 deletions(-)

diff --git a/lib/go-atscfg/cachedotconfig.go b/lib/go-atscfg/cachedotconfig.go
index 949bc4c27c..9418d462e3 100644
--- a/lib/go-atscfg/cachedotconfig.go
+++ b/lib/go-atscfg/cachedotconfig.go
@@ -20,6 +20,7 @@ package atscfg
  */
 
 import (
+       "fmt"
        "sort"
        "strings"
 
@@ -71,6 +72,62 @@ func makeCacheDotConfigEdge(
                return Cfg{}, makeErr(warnings, "server missing profiles")
        }
 
+       profileDSes, dsWarnings := GetProfileDSes(server, servers, 
deliveryServices, deliveryServiceServers)
+       warnings = append(warnings, dsWarnings...)
+
+       lines := map[string]struct{}{} // use a "set" for lines, to avoid 
duplicates, since we're looking up by profile
+       for _, ds := range profileDSes {
+               if ds.Type != tc.DSTypeHTTPNoCache {
+                       continue
+               }
+               if ds.OriginFQDN == "" {
+                       warnings = append(warnings, 
fmt.Sprintf("profileCacheDotConfig ds %s has no origin fqdn, skipping!", 
ds.Name))
+                       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{}{}
+               }
+       }
+
+       linesArr := []string{}
+       for line, _ := range lines {
+               linesArr = append(linesArr, line)
+       }
+       sort.Strings(linesArr)
+       text := strings.Join(linesArr, "")
+       if text == "" {
+               text = "\n" // If no params exist, don't send "not found," but 
an empty file. We know the profile exists.
+       }
+       hdr := makeHdrComment(opt.HdrComment)
+       text = hdr + text
+
+       return Cfg{
+               Text:        text,
+               ContentType: ContentTypeCacheDotConfig,
+               LineComment: LineCommentCacheDotConfig,
+               Warnings:    warnings,
+       }, nil
+}
+
+// ProfileDS struct for filtered delivery services.
+type ProfileDS struct {
+       Name       string
+       Type       tc.DSType
+       OriginFQDN string
+}
+
+// GetProfileDSes filters delivery services and return delivery services with 
valid type and non-empty FQDN.
+func GetProfileDSes(server *Server,
+       servers []Server,
+       deliveryServices []DeliveryService,
+       deliveryServiceServers []DeliveryServiceServer,
+) ([]ProfileDS, []string) {
+       warnings := make([]string, 0)
        profileServerIDsMap := map[int]struct{}{}
        for _, sv := range servers {
                if len(sv.ProfileNames) == 0 {
@@ -97,7 +154,7 @@ func makeCacheDotConfigEdge(
                dsIDs[dss.DeliveryService] = struct{}{}
        }
 
-       profileDSes := []profileDS{}
+       profileDSes := []ProfileDS{}
        for _, ds := range deliveryServices {
                if ds.ID == nil {
                        warnings = append(warnings, "deliveryservices had ds 
with nil id, skipping!")
@@ -110,6 +167,10 @@ func makeCacheDotConfigEdge(
                if ds.OrgServerFQDN == nil {
                        continue // this is normal for steering and anymap dses
                }
+               if ds.XMLID == nil || *ds.XMLID == "" {
+                       warnings = append(warnings, "got ds with missing XMLID, 
skipping!")
+                       continue
+               }
                if *ds.Type == tc.DSTypeInvalid {
                        warnings = append(warnings, "deliveryservices had ds 
with invalid type, skipping!")
                        continue
@@ -121,73 +182,31 @@ func makeCacheDotConfigEdge(
                if _, ok := dsIDs[*ds.ID]; !ok && ds.Topology == nil {
                        continue
                }
-               origin := *ds.OrgServerFQDN
-               profileDSes = append(profileDSes, profileDS{Type: *ds.Type, 
OriginFQDN: &origin})
+               profileDSes = append(profileDSes, ProfileDS{Name: *ds.XMLID, 
Type: *ds.Type, OriginFQDN: *ds.OrgServerFQDN})
        }
 
-       lines := map[string]struct{}{} // use a "set" for lines, to avoid 
duplicates, since we're looking up by profile
-       for _, ds := range profileDSes {
-               if ds.Type != tc.DSTypeHTTPNoCache {
-                       continue
-               }
-               if ds.OriginFQDN == nil || *ds.OriginFQDN == "" {
-                       warnings = append(warnings, "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{}{}
-               }
-       }
-
-       linesArr := []string{}
-       for line, _ := range lines {
-               linesArr = append(linesArr, line)
-       }
-       sort.Strings(linesArr)
-       text := strings.Join(linesArr, "")
-       if text == "" {
-               text = "\n" // If no params exist, don't send "not found," but 
an empty file. We know the profile exists.
-       }
-       hdr := makeHdrComment(opt.HdrComment)
-       text = hdr + text
-
-       return Cfg{
-               Text:        text,
-               ContentType: ContentTypeCacheDotConfig,
-               LineComment: LineCommentCacheDotConfig,
-               Warnings:    warnings,
-       }, nil
-}
-
-type profileDS struct {
-       Type       tc.DSType
-       OriginFQDN *string
+       return profileDSes, warnings
 }
 
 // dsesToProfileDSes is a helper function to convert a 
[]tc.DeliveryServiceNullable to []ProfileDS.
 // Note this does not check for nil values. If any DeliveryService's Type or 
OrgServerFQDN may be nil, the returned ProfileDS should be checked for 
DSTypeInvalid and nil, respectively.
-func dsesToProfileDSes(dses []tc.DeliveryServiceNullable) []profileDS {
-       pdses := []profileDS{}
+func dsesToProfileDSes(dses []tc.DeliveryServiceNullable) []ProfileDS {
+       pdses := []ProfileDS{}
        for _, ds := range dses {
-               pds := profileDS{}
+               pds := ProfileDS{}
                if ds.Type != nil {
                        pds.Type = *ds.Type
                }
                if ds.OrgServerFQDN != nil && *ds.OrgServerFQDN != "" {
-                       org := *ds.OrgServerFQDN
-                       pds.OriginFQDN = &org
+                       pds.OriginFQDN = *ds.OrgServerFQDN
                }
                pdses = append(pdses, pds)
        }
        return pdses
 }
 
-func getHostPortFromURI(uriStr string) (string, string) {
+// GetHostPortFromURI strips HTTP(s) scheme and path and return host with port 
(if found).
+func GetHostPortFromURI(uriStr string) (string, string) {
        originFQDN := uriStr
        originFQDN = strings.TrimPrefix(originFQDN, "http://";)
        originFQDN = strings.TrimPrefix(originFQDN, "https://";)
diff --git a/lib/varnishcfg/cache_control.go b/lib/varnishcfg/cache_control.go
new file mode 100644
index 0000000000..851c0ff919
--- /dev/null
+++ b/lib/varnishcfg/cache_control.go
@@ -0,0 +1,57 @@
+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) configureUncacheableDSes(vclFile *vclFile, profileDSes 
[]atscfg.ProfileDS) []string {
+       warnings := make([]string, 0)
+
+       uncacheableHostsConditions := make([]string, 0)
+       for _, ds := range profileDSes {
+               if ds.Type != tc.DSTypeHTTPNoCache {
+                       continue
+               }
+
+               if ds.OriginFQDN == "" {
+                       warnings = append(warnings, fmt.Sprintf("ds %s has no 
origin fqdn, skipping!", ds.Name))
+                       continue
+               }
+
+               host, _ := atscfg.GetHostPortFromURI(ds.OriginFQDN)
+               uncacheableHostsConditions = append(uncacheableHostsConditions, 
fmt.Sprintf(`bereq.http.host == "%s"`, host))
+       }
+       if len(uncacheableHostsConditions) == 0 {
+               return warnings
+       }
+       berespLines := make([]string, 0)
+       berespLines = append(berespLines, fmt.Sprintf(`if (%s) {`, 
strings.Join(uncacheableHostsConditions, " || ")))
+       berespLines = append(berespLines, fmt.Sprint(`  set beresp.uncacheable 
= true;`))
+       berespLines = append(berespLines, fmt.Sprint(`}`))
+       vclFile.subroutines["vcl_backend_response"] = 
append(vclFile.subroutines["vcl_backend_response"], berespLines...)
+
+       return warnings
+}
diff --git a/lib/varnishcfg/cache_control_test.go 
b/lib/varnishcfg/cache_control_test.go
new file mode 100644
index 0000000000..beb57d6cb4
--- /dev/null
+++ b/lib/varnishcfg/cache_control_test.go
@@ -0,0 +1,90 @@
+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/cache-config/t3cutil"
+       "github.com/apache/trafficcontrol/lib/go-atscfg"
+       "github.com/apache/trafficcontrol/lib/go-tc"
+)
+
+func TestConfigureUncacheableDSes(t *testing.T) {
+       testCases := []struct {
+               name                string
+               dses                []atscfg.ProfileDS
+               expectedSubroutines map[string][]string
+       }{
+               {
+                       name: "no uncacheable DSes",
+                       dses: []atscfg.ProfileDS{
+                               {Name: "ds1", Type: tc.DSTypeHTTP, OriginFQDN: 
"ds1.example.com"},
+                               {Name: "ds2", Type: tc.DSTypeHTTP, OriginFQDN: 
"ds2.example.com"},
+                       },
+                       expectedSubroutines: make(map[string][]string),
+               },
+               {
+                       name: "single uncacheable DS",
+                       dses: []atscfg.ProfileDS{
+                               {Name: "ds1", Type: tc.DSTypeHTTP, OriginFQDN: 
"ds1.example.com"},
+                               {Name: "ds2", Type: tc.DSTypeHTTPNoCache, 
OriginFQDN: "ds2.example.com"},
+                       },
+                       expectedSubroutines: map[string][]string{
+                               "vcl_backend_response": {
+                                       `if (bereq.http.host == 
"ds2.example.com") {`,
+                                       `       set beresp.uncacheable = true;`,
+                                       `}`,
+                               },
+                       },
+               },
+               {
+                       name: "multiple uncacheable DS",
+                       dses: []atscfg.ProfileDS{
+                               {Name: "ds1", Type: tc.DSTypeHTTP, OriginFQDN: 
"ds1.example.com"},
+                               {Name: "ds2", Type: tc.DSTypeHTTPNoCache, 
OriginFQDN: "ds2.example.com"},
+                               {Name: "ds3", Type: tc.DSTypeHTTPNoCache, 
OriginFQDN: "ds3.example.com"},
+                               {Name: "ds4", Type: tc.DSTypeHTTPNoCache, 
OriginFQDN: "ds4.example.com"},
+                               {Name: "ds5", Type: tc.DSTypeDNS, OriginFQDN: 
"ds5.example.com"},
+                       },
+                       expectedSubroutines: map[string][]string{
+                               "vcl_backend_response": {
+                                       `if (bereq.http.host == 
"ds2.example.com" || bereq.http.host == "ds3.example.com" || bereq.http.host == 
"ds4.example.com") {`,
+                                       `       set beresp.uncacheable = true;`,
+                                       `}`,
+                               },
+                       },
+               },
+       }
+       for _, tC := range testCases {
+               t.Run(tC.name, func(t *testing.T) {
+                       vb := NewVCLBuilder(&t3cutil.ConfigData{})
+                       vclFile := newVCLFile(defaultVCLVersion)
+                       warnings := vb.configureUncacheableDSes(&vclFile, 
tC.dses)
+                       if len(warnings) != 0 {
+                               t.Errorf("got unexpected warnings %v", warnings)
+                       }
+                       if !reflect.DeepEqual(vclFile.subroutines, 
tC.expectedSubroutines) {
+                               t.Errorf("got %v want %v", vclFile.subroutines, 
tC.expectedSubroutines)
+                       }
+               })
+       }
+}
diff --git a/lib/varnishcfg/vclbuilder.go b/lib/varnishcfg/vclbuilder.go
index 70c89de9c7..480aaa686e 100644
--- a/lib/varnishcfg/vclbuilder.go
+++ b/lib/varnishcfg/vclbuilder.go
@@ -75,8 +75,18 @@ func (vb *VCLBuilder) BuildVCLFile() (string, []string, 
error) {
        if err != nil {
                return "", nil, fmt.Errorf("(warnings: %s) %w", 
strings.Join(warnings, ", "), err)
        }
+       profileDSes, dsWarnings := atscfg.GetProfileDSes(
+               vb.toData.Server,
+               vb.toData.Servers,
+               vb.toData.DeliveryServices,
+               vb.toData.DeliveryServiceServers,
+       )
+       warnings = append(warnings, dsWarnings...)
+       cacheWarnings := vb.configureUncacheableDSes(&v, profileDSes)
+       warnings = append(warnings, cacheWarnings...)
 
        dirWarnings, err := vb.configureDirectors(&v, parents)
        warnings = append(warnings, dirWarnings...)
+
        return fmt.Sprint(v), warnings, err
 }

Reply via email to