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 79a1b24  Geo limit countries can now be parsed as an array and a 
string (#6441)
79a1b24 is described below

commit 79a1b24cb8f0c426dd2310cc9355115f09a4c4db
Author: Srijeet Chatterjee <[email protected]>
AuthorDate: Mon Jan 24 19:47:58 2022 -0700

    Geo limit countries can now be parsed as an array and a string (#6441)
    
    * Parse Geo Limit as a string as well as an array
    
    * changelog entry
    
    * change sanitize method
    
    * remove npe
    
    * adding validations and tests
    
    * formatting changes, error msgs changed
    
    * adding some more checks
    
    * code review fixes
    
    * fix test
    
    * code review changes
    
    * remove unneeded code
    
    * address code review
    
    * fix tests, go vet
    
    * go fmt
    
    * code review fixes
    
    * code review changes
---
 CHANGELOG.md                                       |  1 +
 cache-config/t3c-generate/cfgfile/cfgfile_test.go  |  2 +-
 docs/source/api/v4/deliveryservices.rst            |  6 +-
 docs/source/api/v4/deliveryservices_id.rst         |  4 +-
 docs/source/api/v4/deliveryservices_id_safe.rst    |  2 +-
 docs/source/api/v4/servers_id_deliveryservices.rst |  2 +-
 docs/source/overview/delivery_services.rst         |  2 +-
 lib/go-tc/deliveryservices.go                      | 50 ++++++++++++-
 lib/go-tc/deliveryservices_test.go                 |  9 ++-
 .../testing/api/v4/deliveryservices_test.go        | 62 ++++++++++++++++
 .../crconfig/deliveryservice_test.go               | 82 ++++++++++++++++++++--
 .../deliveryservice/deliveryservices.go            | 48 ++++++++++---
 traffic_ops/v4-client/server.go                    |  4 +-
 13 files changed, 246 insertions(+), 28 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4aa1f36..6091b7d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -53,6 +53,7 @@ The format is based on [Keep a 
Changelog](http://keepachangelog.com/en/1.0.0/).
 ### Changed
 - Updated `t3c` to request less unnecessary deliveryservice-server assignment 
and invalidation jobs data via new query params supported by Traffic Ops
 - [#6179](https://github.com/apache/trafficcontrol/issues/6179) Updated the 
Traffic Ops rpm to include the `ToDnssecRefresh` binary and make the 
`trafops_dnssec_refresh` cron job use it
+- [#6382](https://github.com/apache/trafficcontrol/issues/6382) Accept Geo 
Limit Countries as strings and arrays.
 - Traffic Portal no longer uses `ruby compass` to compile sass and now uses 
`dart-sass`.
 - Changed Invalidation Jobs throughout (TO, TP, T3C, etc.) to account for the 
ability to do both REFRESH and REFETCH requests for resources.
 - Changed the `maxConnections` value on Traffic Router, to prevent the 
thundering herd problem (TR).
diff --git a/cache-config/t3c-generate/cfgfile/cfgfile_test.go 
b/cache-config/t3c-generate/cfgfile/cfgfile_test.go
index 2864753..60817f2 100644
--- a/cache-config/t3c-generate/cfgfile/cfgfile_test.go
+++ b/cache-config/t3c-generate/cfgfile/cfgfile_test.go
@@ -214,7 +214,7 @@ func randDS() *atscfg.DeliveryService {
        ds.DSCP = randInt()
        ds.EdgeHeaderRewrite = randStr()
        ds.GeoLimit = randInt()
-       ds.GeoLimitCountries = randStr()
+       ds.GeoLimitCountries = nil
        ds.GeoLimitRedirectURL = randStr()
        ds.GeoProvider = randInt()
        ds.GlobalMaxMBPS = randInt()
diff --git a/docs/source/api/v4/deliveryservices.rst 
b/docs/source/api/v4/deliveryservices.rst
index c4bdaeb..9996bcf 100644
--- a/docs/source/api/v4/deliveryservices.rst
+++ b/docs/source/api/v4/deliveryservices.rst
@@ -104,7 +104,7 @@ Response Structure
 :firstHeaderRewrite:        A set of :ref:`ds-first-header-rw-rules`
 :fqPacingRate:              The :ref:`ds-fqpr`
 :geoLimit:                  An integer that defines the :ref:`ds-geo-limit`
-:geoLimitCountries:         A string containing a comma-separated list 
defining the :ref:`ds-geo-limit-countries`
+:geoLimitCountries:         An array of strings defining the 
:ref:`ds-geo-limit-countries`
 :geoLimitRedirectUrl:       A :ref:`ds-geo-limit-redirect-url`
 :geoProvider:               The :ref:`ds-geo-provider`
 :globalMaxMbps:             The :ref:`ds-global-max-mbps`
@@ -296,7 +296,7 @@ Request Structure
 :firstHeaderRewrite:        A set of :ref:`ds-first-header-rw-rules`
 :fqPacingRate:              The :ref:`ds-fqpr`
 :geoLimit:                  An integer that defines the :ref:`ds-geo-limit`
-:geoLimitCountries:         A string containing a comma-separated list 
defining the :ref:`ds-geo-limit-countries`\ [#geolimit]_
+:geoLimitCountries:         A string containing a comma-separated list, or an 
array of strings defining the :ref:`ds-geo-limit-countries`\ [#geolimit]_
 :geoLimitRedirectUrl:       A :ref:`ds-geo-limit-redirect-url`\ [#geolimit]_
 :geoProvider:               The :ref:`ds-geo-provider`
 :globalMaxMbps:             The :ref:`ds-global-max-mbps`
@@ -447,7 +447,7 @@ Response Structure
 :firstHeaderRewrite:        A set of :ref:`ds-first-header-rw-rules`
 :fqPacingRate:              The :ref:`ds-fqpr`
 :geoLimit:                  An integer that defines the :ref:`ds-geo-limit`
-:geoLimitCountries:         A string containing a comma-separated list 
defining the :ref:`ds-geo-limit-countries`
+:geoLimitCountries:         An array of strings defining the 
:ref:`ds-geo-limit-countries`
 :geoLimitRedirectUrl:       A :ref:`ds-geo-limit-redirect-url`
 :geoProvider:               The :ref:`ds-geo-provider`
 :globalMaxMbps:             The :ref:`ds-global-max-mbps`
diff --git a/docs/source/api/v4/deliveryservices_id.rst 
b/docs/source/api/v4/deliveryservices_id.rst
index 8223317..3ba01c0 100644
--- a/docs/source/api/v4/deliveryservices_id.rst
+++ b/docs/source/api/v4/deliveryservices_id.rst
@@ -52,7 +52,7 @@ Request Structure
 :firstHeaderRewrite:        A set of :ref:`ds-first-header-rw-rules`
 :fqPacingRate:              The :ref:`ds-fqpr`
 :geoLimit:                  An integer that defines the :ref:`ds-geo-limit`
-:geoLimitCountries:         A string containing a comma-separated list 
defining the :ref:`ds-geo-limit-countries`\ [#geolimit]_
+:geoLimitCountries:         A string containing a comma-separated list, or an 
array of strings defining the :ref:`ds-geo-limit-countries`\ [#geolimit]_
 :geoLimitRedirectUrl:       A :ref:`ds-geo-limit-redirect-url`\ [#geolimit]_
 :geoProvider:               The :ref:`ds-geo-provider`
 :globalMaxMbps:             The :ref:`ds-global-max-mbps`
@@ -204,7 +204,7 @@ Response Structure
 :firstHeaderRewrite:        A set of :ref:`ds-first-header-rw-rules`
 :fqPacingRate:              The :ref:`ds-fqpr`
 :geoLimit:                  An integer that defines the :ref:`ds-geo-limit`
-:geoLimitCountries:         A string containing a comma-separated list 
defining the :ref:`ds-geo-limit-countries`
+:geoLimitCountries:         An array of strings defining the 
:ref:`ds-geo-limit-countries`
 :geoLimitRedirectUrl:       A :ref:`ds-geo-limit-redirect-url`
 :geoProvider:               The :ref:`ds-geo-provider`
 :globalMaxMbps:             The :ref:`ds-global-max-mbps`
diff --git a/docs/source/api/v4/deliveryservices_id_safe.rst 
b/docs/source/api/v4/deliveryservices_id_safe.rst
index 0501499..c6a529b 100644
--- a/docs/source/api/v4/deliveryservices_id_safe.rst
+++ b/docs/source/api/v4/deliveryservices_id_safe.rst
@@ -83,7 +83,7 @@ Response Structure
 :firstHeaderRewrite:        A set of :ref:`ds-first-header-rw-rules`
 :fqPacingRate:              The :ref:`ds-fqpr`
 :geoLimit:                  An integer that defines the :ref:`ds-geo-limit`
-:geoLimitCountries:         A string containing a comma-separated list 
defining the :ref:`ds-geo-limit-countries`
+:geoLimitCountries:         An array of strings defining the 
:ref:`ds-geo-limit-countries`
 :geoLimitRedirectUrl:       A :ref:`ds-geo-limit-redirect-url`
 :geoProvider:               The :ref:`ds-geo-provider`
 :globalMaxMbps:             The :ref:`ds-global-max-mbps`
diff --git a/docs/source/api/v4/servers_id_deliveryservices.rst 
b/docs/source/api/v4/servers_id_deliveryservices.rst
index 69112b9..a04d7fc 100644
--- a/docs/source/api/v4/servers_id_deliveryservices.rst
+++ b/docs/source/api/v4/servers_id_deliveryservices.rst
@@ -91,7 +91,7 @@ Response Structure
 :firstHeaderRewrite:        A set of :ref:`ds-first-header-rw-rules`
 :fqPacingRate:              The :ref:`ds-fqpr`
 :geoLimit:                  An integer that defines the :ref:`ds-geo-limit`
-:geoLimitCountries:         A string containing a comma-separated list 
defining the :ref:`ds-geo-limit-countries`
+:geoLimitCountries:         An array of strings defining the 
:ref:`ds-geo-limit-countries`
 :geoLimitRedirectUrl:       A :ref:`ds-geo-limit-redirect-url`
 :geoProvider:               The :ref:`ds-geo-provider`
 :globalMaxMbps:             The :ref:`ds-global-max-mbps`
diff --git a/docs/source/overview/delivery_services.rst 
b/docs/source/overview/delivery_services.rst
index 4bc218f..389779a 100644
--- a/docs/source/overview/delivery_services.rst
+++ b/docs/source/overview/delivery_services.rst
@@ -277,7 +277,7 @@ Limits access to a Delivery Service by geographic location. 
The only practical d
 
 Geo Limit Countries
 -------------------
-When `Geo Limit`_ is being used with this Delivery Service (and is set to 
exactly ``2``), this is optionally a list of country codes to which access to 
content provided by the Delivery Service will be restricted. Normally, this is 
a comma-delimited string of said country codes. When creating a Delivery 
Service with this field or modifying the Geo Limit Countries field on an 
existing Delivery Service, any amount of whitespace between country codes is 
permissible, as it will be removed on  [...]
+When `Geo Limit`_ is being used with this Delivery Service (and is set to 
exactly ``2``), this is optionally a list of country codes to which access to 
content provided by the Delivery Service will be restricted. Normally, this is 
a comma-delimited string of said country codes, or an array of strings 
representing the country codes. When creating a Delivery Service with this 
field or modifying the Geo Limit Countries field on an existing Delivery 
Service, any amount of whitespace between  [...]
 
 .. table:: Aliases
 
diff --git a/lib/go-tc/deliveryservices.go b/lib/go-tc/deliveryservices.go
index 6e729af..4462a27 100644
--- a/lib/go-tc/deliveryservices.go
+++ b/lib/go-tc/deliveryservices.go
@@ -245,7 +245,8 @@ type DeliveryServiceV40 struct {
 
        // TLSVersions is the list of explicitly supported TLS versions for 
cache
        // servers serving the Delivery Service's content.
-       TLSVersions []string `json:"tlsVersions" db:"tls_versions"`
+       TLSVersions       []string              `json:"tlsVersions" 
db:"tls_versions"`
+       GeoLimitCountries GeoLimitCountriesType `json:"geoLimitCountries"`
 }
 
 // DeliveryServiceV4 is a Delivery Service as it appears in version 4 of the
@@ -577,6 +578,40 @@ type DeliveryServiceNullableV11 struct {
        DeliveryServiceRemovedFieldsV11
 }
 
+// GeoLimitCountriesType is the type alias that is used to represent the 
GeoLimitCountries attribute of the DeliveryService struct.
+type GeoLimitCountriesType []string
+
+// UnmarshalJSON will unmarshal a byte slice into type GeoLimitCountriesType.
+func (g *GeoLimitCountriesType) UnmarshalJSON(data []byte) error {
+       var err error
+       var initial = make([]string, 0)
+       var initialStr string
+       if err = json.Unmarshal(data, &initial); err != nil {
+               if err = json.Unmarshal(data, &initialStr); err != nil {
+                       return err
+               }
+               if strings.Contains(initialStr, ",") {
+                       initial = strings.Split(initialStr, ",")
+               } else {
+                       initial = append(initial, initialStr)
+               }
+       }
+
+       if initial == nil || len(initial) == 0 {
+               g = nil
+               return nil
+       }
+       *g = initial
+       return nil
+
+}
+
+// MarshalJSON will marshal a GeoLimitCountriesType into a byte slice.
+func (g GeoLimitCountriesType) MarshalJSON() ([]byte, error) {
+       arr := ([]string)(g)
+       return json.Marshal(arr)
+}
+
 // DeliveryServiceNullableFieldsV11 contains properties that Delivery Services
 // as they appeared in Traffic Ops API v1.1 had, AND were not removed by ANY
 // later API version.
@@ -813,6 +848,10 @@ func (ds *DeliveryServiceV4) RemoveLD1AndLD2() 
DeliveryServiceV4 {
 
 // DowngradeToV3 converts the 4.x DS to a 3.x DS.
 func (ds *DeliveryServiceV4) DowngradeToV3() DeliveryServiceNullableV30 {
+       nullableFields := ds.DeliveryServiceNullableFieldsV11
+       geoLimitCountries := ([]string)(ds.GeoLimitCountries)
+       geo := strings.Join(geoLimitCountries, ",")
+       nullableFields.GeoLimitCountries = &geo
        return DeliveryServiceNullableV30{
                DeliveryServiceV30: DeliveryServiceV30{
                        DeliveryServiceNullableV15: DeliveryServiceNullableV15{
@@ -820,7 +859,7 @@ func (ds *DeliveryServiceV4) DowngradeToV3() 
DeliveryServiceNullableV30 {
                                        DeliveryServiceNullableV13: 
DeliveryServiceNullableV13{
                                                DeliveryServiceNullableV12: 
DeliveryServiceNullableV12{
                                                        
DeliveryServiceNullableV11: DeliveryServiceNullableV11{
-                                                               
DeliveryServiceNullableFieldsV11: ds.DeliveryServiceNullableFieldsV11,
+                                                               
DeliveryServiceNullableFieldsV11: nullableFields,
                                                        },
                                                },
                                                DeliveryServiceFieldsV13: 
ds.DeliveryServiceFieldsV13,
@@ -837,6 +876,12 @@ func (ds *DeliveryServiceV4) DowngradeToV3() 
DeliveryServiceNullableV30 {
 
 // UpgradeToV4 converts the 3.x DS to a 4.x DS.
 func (ds *DeliveryServiceNullableV30) UpgradeToV4() DeliveryServiceV4 {
+       var geo GeoLimitCountriesType
+       if ds.GeoLimitCountries != nil {
+               str := *ds.GeoLimitCountries
+               geo = make([]string, 0)
+               geo = strings.Split(str, ",")
+       }
        return DeliveryServiceV4{
                DeliveryServiceFieldsV31:         ds.DeliveryServiceFieldsV31,
                DeliveryServiceFieldsV30:         ds.DeliveryServiceFieldsV30,
@@ -845,6 +890,7 @@ func (ds *DeliveryServiceNullableV30) UpgradeToV4() 
DeliveryServiceV4 {
                DeliveryServiceFieldsV13:         ds.DeliveryServiceFieldsV13,
                DeliveryServiceNullableFieldsV11: 
ds.DeliveryServiceNullableFieldsV11,
                TLSVersions:                      nil,
+               GeoLimitCountries:                geo,
        }
 }
 
diff --git a/lib/go-tc/deliveryservices_test.go 
b/lib/go-tc/deliveryservices_test.go
index 3ba0f6a..8491ed3 100644
--- a/lib/go-tc/deliveryservices_test.go
+++ b/lib/go-tc/deliveryservices_test.go
@@ -16,6 +16,7 @@ package tc
 
 import (
        "fmt"
+       "strings"
        "testing"
 )
 
@@ -412,7 +413,9 @@ func dsUpgradeAndDowngradeTestingPair() 
(DeliveryServiceNullableV30, DeliverySer
        firstHeaderRewrite := "firstHeaderRewrite"
        fqPacingRate := 1337
        geoLimit := 2
-       geoLimitCountries := "geo,Limit,Countries"
+       geoLimitCountries := []string{"geo", "Limit", "Countries"}
+       geo := (GeoLimitCountriesType)(geoLimitCountries)
+       geoStr := strings.Join(geoLimitCountries, ",")
        geoLimitRedirectURL := "wss://geoLimitRedirectURL"
        geoProvider := 1
        globalMaxMBPS := -72485
@@ -484,7 +487,7 @@ func dsUpgradeAndDowngradeTestingPair() 
(DeliveryServiceNullableV30, DeliverySer
        newDS.FirstHeaderRewrite = &firstHeaderRewrite
        newDS.FQPacingRate = &fqPacingRate
        newDS.GeoLimit = &geoLimit
-       newDS.GeoLimitCountries = &geoLimitCountries
+       newDS.GeoLimitCountries = geo
        newDS.GeoLimitRedirectURL = &geoLimitRedirectURL
        newDS.GeoProvider = &geoProvider
        newDS.GlobalMaxMBPS = &globalMaxMBPS
@@ -560,7 +563,7 @@ func dsUpgradeAndDowngradeTestingPair() 
(DeliveryServiceNullableV30, DeliverySer
                                                                        
EdgeHeaderRewrite:        &edgeHeaderRewrite,
                                                                        
ExampleURLs:              exampleURLs,
                                                                        
GeoLimit:                 &geoLimit,
-                                                                       
GeoLimitCountries:        &geoLimitCountries,
+                                                                       
GeoLimitCountries:        &geoStr,
                                                                        
GeoLimitRedirectURL:      &geoLimitRedirectURL,
                                                                        
GeoProvider:              &geoProvider,
                                                                        
GlobalMaxMBPS:            &globalMaxMBPS,
diff --git a/traffic_ops/testing/api/v4/deliveryservices_test.go 
b/traffic_ops/testing/api/v4/deliveryservices_test.go
index 6109921..25b21d9 100644
--- a/traffic_ops/testing/api/v4/deliveryservices_test.go
+++ b/traffic_ops/testing/api/v4/deliveryservices_test.go
@@ -54,6 +54,7 @@ func TestDeliveryServices(t *testing.T) {
                        t.Run("Create and retrieve SSL keys for a Delivery 
Service", DeliveryServiceSSLKeys)
                }
 
+               t.Run("Create a Delivery Service with geo limit countries", 
CreateTestDeliveryServiceWithGeoLimitCountries)
                t.Run("Create a Delivery Service with the removed Long 
Description 2 and 3 fields", CreateTestDeliveryServiceWithLongDescFields)
                t.Run("Update a Delivery Service, setting its removed Long 
Description 2 and 3 fields", UpdateTestDeliveryServiceWithLongDescFields)
                t.Run("Getting unmodified Delivery Services using the 
If-Modified-Since header", GetTestDeliveryServicesIMS)
@@ -98,6 +99,67 @@ func TestDeliveryServices(t *testing.T) {
        })
 }
 
+func CreateTestDeliveryServiceWithGeoLimitCountries(t *testing.T) {
+
+       if len(testData.DeliveryServices) == 0 {
+               t.Fatalf("no deliveryservices to run the test on, quitting")
+       }
+
+       cdn := createBlankCDN("geoLimitCDN", t)
+       opts := client.NewRequestOptions()
+       opts.QueryParameters.Set("name", "HTTP")
+       types, _, err := TOSession.GetTypes(opts)
+       if err != nil {
+               t.Fatalf("unable to get Types: %v - alerts: %+v", err, 
types.Alerts)
+       }
+       if len(types.Response) < 1 {
+               t.Fatal("expected at least one type")
+       }
+       customDS := getCustomDS(cdn.ID, types.Response[0].ID, 
"geo-limit-countries-test-ds-name", "edge", "https://test-geo-limit.com";, 
"geo-limit-countries-test-ds-xml-id")
+       customDS.Protocol = util.IntPtr(0)
+       customDS.GeoLimit = util.IntPtr(2)
+       //geoLimitCountries := []string{"US   ", "CA"}
+       customDS.GeoLimitCountries = []string{"US   ", "CA"}
+
+       resp, _, err := TOSession.CreateDeliveryService(customDS, 
client.RequestOptions{})
+       if err != nil {
+               t.Errorf("expected no error while creating a new ds, but got 
%v", err)
+       }
+       if len(resp.Response) != 1 {
+               t.Fatalf("expected 1 response in return of a create DS request, 
but got %d", len(resp.Response))
+       }
+       if resp.Response[0].GeoLimitCountries == nil {
+               t.Fatalf("got nothing in geo limit countries")
+       }
+       arr := ([]string)(resp.Response[0].GeoLimitCountries)
+       if len(arr) != 2 || arr[0] != "US" || arr[1] != "CA" {
+               t.Errorf("expected geo limit countries: US,CA; actual: %s", arr)
+       }
+       opts = client.NewRequestOptions()
+       opts.QueryParameters.Set("xmlId", *customDS.XMLID)
+       deliveryServices, _, err := TOSession.GetDeliveryServices(opts)
+       if err != nil {
+               t.Fatalf("couldn't get ds: %v", err)
+       }
+       if len(deliveryServices.Response) != 1 {
+               t.Fatal("couldn't get exactly one ds in the response, quitting")
+       }
+       dsID := deliveryServices.Response[0].ID
+       if dsID == nil {
+               t.Fatalf("got a delivery service with no ID")
+       }
+       customDS.GeoLimitCountries = []string{"US   ", "CA", "12"}
+       _, _, err = TOSession.UpdateDeliveryService(*dsID, customDS, 
client.RequestOptions{})
+       if err == nil {
+               t.Error("expected an error while updating geo limit countries 
of a ds with an invalid country code, but got nothing")
+       }
+
+       _, _, err = TOSession.DeleteDeliveryService(*dsID, 
client.NewRequestOptions())
+       if err != nil {
+               t.Errorf("expected no error while deleting ds, but got %v", err)
+       }
+}
+
 func CUDDeliveryServiceWithLocks(t *testing.T) {
        // Create a new user with operations level privileges
        user1 := tc.UserV4{
diff --git a/traffic_ops/traffic_ops_golang/crconfig/deliveryservice_test.go 
b/traffic_ops/traffic_ops_golang/crconfig/deliveryservice_test.go
index 3904834..5ff78ee 100644
--- a/traffic_ops/traffic_ops_golang/crconfig/deliveryservice_test.go
+++ b/traffic_ops/traffic_ops_golang/crconfig/deliveryservice_test.go
@@ -123,7 +123,11 @@ func ExpectedMakeDSes() 
map[string]tc.CRConfigDeliveryService {
        }
 }
 
-func MockMakeDSes(mock sqlmock.Sqlmock, expected 
map[string]tc.CRConfigDeliveryService, cdn string) {
+func MockMakeDSes(mock sqlmock.Sqlmock, expected 
map[string]tc.CRConfigDeliveryService, cdn string, geoEnabled string) {
+       geoLimit := 0
+       if len(geoEnabled) != 0 {
+               geoLimit = 2
+       }
        rows := sqlmock.NewRows([]string{
                "anonymous_blocking_enabled",
                "consistent_hash_regex",
@@ -173,8 +177,8 @@ func MockMakeDSes(mock sqlmock.Sqlmock, expected 
map[string]tc.CRConfigDeliveryS
                        *ds.TTL,
                        *ds.EcsEnabled,
                        false,
-                       0,
-                       "",
+                       geoLimit,
+                       geoEnabled,
                        "",
                        0,
                        *ds.BypassDestination["HTTP"].FQDN,
@@ -195,6 +199,76 @@ func MockMakeDSes(mock sqlmock.Sqlmock, expected 
map[string]tc.CRConfigDeliveryS
        mock.ExpectQuery("select").WithArgs(cdn).WillReturnRows(rows)
 }
 
+func TestMakeDSesGeoLimit(t *testing.T) {
+       db, mock, err := sqlmock.New()
+       if err != nil {
+               t.Fatalf("an error '%s' was not expected when opening a stub 
database connection", err)
+       }
+       defer db.Close()
+
+       cdn := "mycdn"
+       domain := "mycdn.invalid"
+
+       expected := ExpectedMakeDSes()
+       delete(expected, "ds2")
+       expectedDS := expected["ds1"]
+       geoEnabled := make([]tc.CRConfigGeoEnabled, 0)
+       geoEnabledCountry := tc.CRConfigGeoEnabled{CountryCode: "US"}
+       geoEnabled = append(geoEnabled, geoEnabledCountry)
+       geoEnabledCountry = tc.CRConfigGeoEnabled{CountryCode: "CA"}
+       geoEnabled = append(geoEnabled, geoEnabledCountry)
+       expectedDS.GeoEnabled = geoEnabled
+       expected["ds1"] = expectedDS
+
+       expectedParams := ExpectedGetServerProfileParams(expected)
+       expectedDSParams, err := getDSParams(expectedParams)
+       if err != nil {
+               t.Fatalf("getDSParams error expected: nil, actual: %v", err)
+       }
+       expectedMatchsets, expectedDomains := 
ExpectedGetDSRegexesDomains(expectedDSParams)
+       expectedStaticDNSEntries := ExpectedGetStaticDNSEntries(expected)
+
+       mock.ExpectBegin()
+       MockGetServerProfileParams(mock, expectedParams, cdn)
+       MockGetDSRegexesDomains(mock, expectedMatchsets, expectedDomains, cdn)
+       MockGetStaticDNSEntries(mock, expectedStaticDNSEntries, cdn)
+       MockMakeDSes(mock, expected, cdn, "US,CA")
+       mock.ExpectCommit()
+
+       dbCtx, cancelTx := context.WithTimeout(context.TODO(), 10*time.Second)
+       defer cancelTx()
+       tx, err := db.BeginTx(dbCtx, nil)
+       if err != nil {
+               t.Fatalf("creating transaction: %v", err)
+       }
+       defer tx.Commit()
+
+       actual, err := makeDSes(cdn, domain, tx)
+       if err != nil {
+               t.Fatalf("makeDSes expected: nil error, actual: %v", err)
+       }
+
+       if len(actual) != len(expected) {
+               t.Fatalf("makeDses len expected: %v, actual: %v", 
len(expected), len(actual))
+       }
+
+       for dsName, ds := range expected {
+               actualDS, ok := actual[dsName]
+               if !ok {
+                       t.Errorf("makeDSes expected: %v, actual: missing", 
dsName)
+                       continue
+               }
+               if len(ds.GeoEnabled) != len(actualDS.GeoEnabled) {
+                       t.Fatalf("expected DS Geoenabled length %d != actual DS 
Geoenabled length %d", len(ds.GeoEnabled), len(actualDS.GeoEnabled))
+               }
+               for i, countryCode := range ds.GeoEnabled {
+                       if countryCode != actualDS.GeoEnabled[i] {
+                               t.Errorf("mismatch in geo enabled countries of 
expected DS and actual DS, expected: %s, actual: %s", countryCode, 
actualDS.GeoEnabled[i])
+                       }
+               }
+       }
+}
+
 func TestMakeDSes(t *testing.T) {
        db, mock, err := sqlmock.New()
        if err != nil {
@@ -218,7 +292,7 @@ func TestMakeDSes(t *testing.T) {
        MockGetServerProfileParams(mock, expectedParams, cdn)
        MockGetDSRegexesDomains(mock, expectedMatchsets, expectedDomains, cdn)
        MockGetStaticDNSEntries(mock, expectedStaticDNSEntries, cdn)
-       MockMakeDSes(mock, expected, cdn)
+       MockMakeDSes(mock, expected, cdn, "")
        mock.ExpectCommit()
 
        dbCtx, cancelTx := context.WithTimeout(context.TODO(), 10*time.Second)
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go 
b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
index abbfb7d..56500c8 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
@@ -211,7 +211,6 @@ func CreateV40(w http.ResponseWriter, r *http.Request) {
                api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, 
errors.New("decoding: "+err.Error()), nil)
                return
        }
-
        res, status, userErr, sysErr := createV40(w, r, inf, ds, true)
        if userErr != nil || sysErr != nil {
                api.HandleErr(w, r, inf.Tx.Tx, status, userErr, sysErr)
@@ -303,6 +302,7 @@ func createV40(w http.ResponseWriter, r *http.Request, inf 
*api.APIInfo, dsV40 t
        tx := inf.Tx.Tx
        ds := tc.DeliveryServiceV4(dsV40)
        err := Validate(tx, &ds)
+       var geoLimitCountries string
        if err != nil {
                return nil, http.StatusBadRequest, errors.New("invalid request: 
" + err.Error()), nil
        }
@@ -327,6 +327,8 @@ func createV40(w http.ResponseWriter, r *http.Request, inf 
*api.APIInfo, dsV40 t
        if userErr != nil || sysErr != nil {
                return nil, errCode, userErr, sysErr
        }
+       geo := ([]string)(ds.GeoLimitCountries)
+       geoLimitCountries = strings.Join(geo, ",")
        var resultRows *sql.Rows
        if omitExtraLongDescFields {
                if ds.LongDesc1 != nil || ds.LongDesc2 != nil {
@@ -349,7 +351,7 @@ func createV40(w http.ResponseWriter, r *http.Request, inf 
*api.APIInfo, dsV40 t
                        &ds.EdgeHeaderRewrite,
                        &ds.GeoLimitRedirectURL,
                        &ds.GeoLimit,
-                       &ds.GeoLimitCountries,
+                       &geoLimitCountries,
                        &ds.GeoProvider,
                        &ds.GlobalMaxMBPS,
                        &ds.GlobalMaxTPS,
@@ -409,7 +411,7 @@ func createV40(w http.ResponseWriter, r *http.Request, inf 
*api.APIInfo, dsV40 t
                        &ds.EdgeHeaderRewrite,
                        &ds.GeoLimitRedirectURL,
                        &ds.GeoLimit,
-                       &ds.GeoLimitCountries,
+                       &geoLimitCountries,
                        &ds.GeoProvider,
                        &ds.GlobalMaxMBPS,
                        &ds.GlobalMaxTPS,
@@ -593,7 +595,6 @@ func (ds *TODeliveryService) Read(h http.Header, useIMS 
bool) ([]interface{}, er
 
        returnable := []interface{}{}
        dses, userErr, sysErr, errCode, maxTime := readGetDeliveryServices(h, 
ds.APIInfo().Params, ds.APIInfo().Tx, ds.APIInfo().User, useIMS)
-
        if sysErr != nil {
                sysErr = errors.New("reading dses: " + sysErr.Error())
                errCode = http.StatusInternalServerError
@@ -913,6 +914,11 @@ func updateV40(w http.ResponseWriter, r *http.Request, inf 
*api.APIInfo, dsV40 *
                }
        }
 
+       var geoLimitCountries string
+       if ds.GeoLimitCountries != nil {
+               geo := ([]string)(ds.GeoLimitCountries)
+               geoLimitCountries = strings.Join(geo, ",")
+       }
        var resultRows *sql.Rows
        if omitExtraLongDescFields {
                if ds.LongDesc1 != nil || ds.LongDesc2 != nil {
@@ -933,7 +939,7 @@ func updateV40(w http.ResponseWriter, r *http.Request, inf 
*api.APIInfo, dsV40 *
                        &ds.EdgeHeaderRewrite,
                        &ds.GeoLimitRedirectURL,
                        &ds.GeoLimit,
-                       &ds.GeoLimitCountries,
+                       &geoLimitCountries,
                        &ds.GeoProvider,
                        &ds.GlobalMaxMBPS,
                        &ds.GlobalMaxTPS,
@@ -993,7 +999,7 @@ func updateV40(w http.ResponseWriter, r *http.Request, inf 
*api.APIInfo, dsV40 *
                        &ds.EdgeHeaderRewrite,
                        &ds.GeoLimitRedirectURL,
                        &ds.GeoLimit,
-                       &ds.GeoLimitCountries,
+                       &geoLimitCountries,
                        &ds.GeoProvider,
                        &ds.GlobalMaxMBPS,
                        &ds.GlobalMaxTPS,
@@ -1372,6 +1378,9 @@ func Validate(tx *sql.Tx, ds *tc.DeliveryServiceV4) error 
{
                        },
                )),
        })
+       if err := validateGeoLimitCountries(ds); err != nil {
+               errs = append(errs, err)
+       }
        if err := validateTopologyFields(ds); err != nil {
                errs = append(errs, err)
        }
@@ -1384,6 +1393,20 @@ func Validate(tx *sql.Tx, ds *tc.DeliveryServiceV4) 
error {
        return util.JoinErrs(errs)
 }
 
+func validateGeoLimitCountries(ds *tc.DeliveryServiceV4) error {
+       var IsLetter = regexp.MustCompile(`^[A-Z]+$`).MatchString
+       if ds.GeoLimitCountries == nil {
+               return nil
+       }
+       countryCodes := ([]string)(ds.GeoLimitCountries)
+       for _, cc := range countryCodes {
+               if cc != "" && !IsLetter(cc) {
+                       return fmt.Errorf("country codes can only contain 
alphabets")
+               }
+       }
+       return nil
+}
+
 func validateTopologyFields(ds *tc.DeliveryServiceV4) error {
        if ds.Topology != nil && (ds.EdgeHeaderRewrite != nil || 
ds.MidHeaderRewrite != nil) {
                return errors.New("cannot set edgeHeaderRewrite or 
midHeaderRewrite while a Topology is assigned. Use firstHeaderRewrite, 
innerHeaderRewrite, and/or lastHeaderRewrite instead")
@@ -1652,6 +1675,7 @@ func GetDeliveryServices(query string, queryValues 
map[string]interface{}, tx *s
        // ensure json generated from this slice won't come out as `null` if 
empty
        dsQueryParams := []string{}
 
+       geoLimitCountries := util.StrPtr("")
        for rows.Next() {
                ds := tc.DeliveryServiceV4{}
                cdnDomain := ""
@@ -1674,7 +1698,7 @@ func GetDeliveryServices(query string, queryValues 
map[string]interface{}, tx *s
                        &ds.FirstHeaderRewrite,
                        &ds.GeoLimitRedirectURL,
                        &ds.GeoLimit,
-                       &ds.GeoLimitCountries,
+                       &geoLimitCountries,
                        &ds.GeoProvider,
                        &ds.GlobalMaxMBPS,
                        &ds.GlobalMaxTPS,
@@ -1730,6 +1754,10 @@ func GetDeliveryServices(query string, queryValues 
map[string]interface{}, tx *s
                        return nil, nil, fmt.Errorf("getting delivery services: 
%v", err), http.StatusInternalServerError
                }
 
+               if geoLimitCountries != nil && *geoLimitCountries != "" {
+                       geo := strings.Split(*geoLimitCountries, ",")
+                       ds.GeoLimitCountries = geo
+               }
                ds.ConsistentHashQueryParams = []string{}
                if len(dsQueryParams) >= 0 {
                        // ensure unique and in consistent order
@@ -2138,7 +2166,11 @@ func setNilIfEmpty(ptrs ...**string) {
 
 func sanitize(ds *tc.DeliveryServiceV4) {
        if ds.GeoLimitCountries != nil {
-               *ds.GeoLimitCountries = 
strings.ToUpper(strings.Replace(*ds.GeoLimitCountries, " ", "", -1))
+               geo := ([]string)(ds.GeoLimitCountries)
+               for i, _ := range geo {
+                       geo[i] = strings.ToUpper(strings.Replace(geo[i], " ", 
"", -1))
+               }
+               ds.GeoLimitCountries = geo
        }
        if ds.ProfileID != nil && *ds.ProfileID == -1 {
                ds.ProfileID = nil
diff --git a/traffic_ops/v4-client/server.go b/traffic_ops/v4-client/server.go
index 2c3299e..272f15c 100644
--- a/traffic_ops/v4-client/server.go
+++ b/traffic_ops/v4-client/server.go
@@ -176,9 +176,9 @@ func (to *Session) 
AssignDeliveryServiceIDsToServerID(server int, dsIDs []int, r
 
 // GetServerIDDeliveryServices returns all of the Delivery Services assigned 
to the server identified
 // by the integral, unique identifier 'server'.
-func (to *Session) GetServerIDDeliveryServices(server int, opts 
RequestOptions) (tc.DeliveryServicesNullableResponse, toclientlib.ReqInf, 
error) {
+func (to *Session) GetServerIDDeliveryServices(server int, opts 
RequestOptions) (tc.DeliveryServicesResponseV4, toclientlib.ReqInf, error) {
        endpoint := fmt.Sprintf(apiServerDeliveryServices, server)
-       var data tc.DeliveryServicesNullableResponse
+       var data tc.DeliveryServicesResponseV4
        reqInf, err := to.get(endpoint, opts, &data)
        return data, reqInf, err
 }

Reply via email to