dewrich closed pull request #2557: Cachegroups localization policy
URL: https://github.com/apache/trafficcontrol/pull/2557
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git a/lib/go-tc/crconfig.go b/lib/go-tc/crconfig.go
index 18133bf0c..1cf10d5de 100644
--- a/lib/go-tc/crconfig.go
+++ b/lib/go-tc/crconfig.go
@@ -151,18 +151,16 @@ type CRConfigDispersion struct {
Shuffled bool `json:"shuffled,string"`
}
-
type CRConfigBackupLocations struct {
FallbackToClosest bool `json:"fallbackToClosest,string"`
List []string `json:"list,omitempty"`
-
}
type CRConfigLatitudeLongitude struct {
- Lat float64 `json:"latitude"`
- Lon float64 `json:"longitude"`
- BackupLocations CRConfigBackupLocations
`json:"backupLocations,omitempty"`
-
+ Lat float64 `json:"latitude"`
+ Lon float64 `json:"longitude"`
+ BackupLocations CRConfigBackupLocations
`json:"backupLocations,omitempty"`
+ LocalizationMethods []LocalizationMethod `json:"localizationMethods"`
}
type CRConfigLatitudeLongitudeShort struct {
diff --git a/lib/go-tc/enum.go b/lib/go-tc/enum.go
index 7b2aee03d..4ab943f1c 100644
--- a/lib/go-tc/enum.go
+++ b/lib/go-tc/enum.go
@@ -29,8 +29,10 @@ package tc
*/
import (
+ "database/sql/driver"
"encoding/json"
"errors"
+ "fmt"
"strconv"
"strings"
)
@@ -186,6 +188,83 @@ func CacheStatusFromString(s string) CacheStatus {
}
}
+// LocalizationMethod represents an enabled localization method for a
cachegroup. The string values of this type should match the Traffic Ops values.
+type LocalizationMethod string
+
+const (
+ LocalizationMethodCZ = LocalizationMethod("CZ")
+ LocalizationMethodDeepCZ = LocalizationMethod("DEEP_CZ")
+ LocalizationMethodGeo = LocalizationMethod("GEO")
+ LocalizationMethodInvalid = LocalizationMethod("INVALID")
+)
+
+// String returns a string representation of this localization method
+func (m LocalizationMethod) String() string {
+ switch m {
+ case LocalizationMethodCZ:
+ return string(m)
+ case LocalizationMethodDeepCZ:
+ return string(m)
+ case LocalizationMethodGeo:
+ return string(m)
+ default:
+ return "INVALID"
+ }
+}
+
+func LocalizationMethodFromString(s string) LocalizationMethod {
+ switch strings.ToLower(s) {
+ case "cz":
+ return LocalizationMethodCZ
+ case "deep_cz":
+ return LocalizationMethodDeepCZ
+ case "geo":
+ return LocalizationMethodGeo
+ default:
+ return LocalizationMethodInvalid
+ }
+}
+
+func (m *LocalizationMethod) UnmarshalJSON(data []byte) error {
+ if string(data) == "null" {
+ return errors.New("LocalizationMethod cannot be null")
+ }
+ s, err := strconv.Unquote(string(data))
+ if err != nil {
+ return errors.New(string(data) + " JSON not quoted")
+ }
+ *m = LocalizationMethodFromString(s)
+ if *m == LocalizationMethodInvalid {
+ return errors.New(s + " is not a LocalizationMethod")
+ }
+ return nil
+}
+
+func (m LocalizationMethod) MarshalJSON() ([]byte, error) {
+ return json.Marshal(m.String())
+}
+
+func (m *LocalizationMethod) Scan(value interface{}) error {
+ if value == nil {
+ return errors.New("LocalizationMethod cannot be null")
+ }
+ sv, err := driver.String.ConvertValue(value)
+ if err != nil {
+ return errors.New("failed to scan LocalizationMethod: " +
err.Error())
+ }
+
+ switch v := sv.(type) {
+ case []byte:
+ *m = LocalizationMethodFromString(string(v))
+ if *m == LocalizationMethodInvalid {
+ return errors.New(string(v) + " is not a valid
LocalizationMethod")
+ }
+ return nil
+ default:
+ return fmt.Errorf("failed to scan LocalizationMethod,
unsupported input type: %T", value)
+ }
+}
+
// DeepCachingType represents a Delivery Service's deep caching type. The
string values of this type should match the Traffic Ops values.
type DeepCachingType string
diff --git a/lib/go-tc/tovalidate/rules.go b/lib/go-tc/tovalidate/rules.go
index 940424c99..f5d055b73 100644
--- a/lib/go-tc/tovalidate/rules.go
+++ b/lib/go-tc/tovalidate/rules.go
@@ -15,6 +15,7 @@ package tovalidate
import (
"errors"
"fmt"
+ "reflect"
"strings"
)
@@ -51,6 +52,52 @@ func IsOneOfStringICase(set ...string) func(string) bool {
}
}
+// IsPtrToSliceOfUniqueStringersICase returns a validator function which
returns an error if the argument is a non-nil
+// pointer to a slice of Stringers whose String() values are not in the set of
strings or there are duplicate strings
+func IsPtrToSliceOfUniqueStringersICase(set ...string) func(interface{}) error
{
+ lowcased := make(map[string]bool, len(set))
+ for _, s := range set {
+ lowcased[strings.ToLower(s)] = true
+ }
+ return func(slicePtr interface{}) error {
+
+ rv := reflect.ValueOf(slicePtr)
+ if rv.Kind() != reflect.Ptr {
+ return fmt.Errorf("%T is not a pointer", slicePtr)
+ }
+
+ if rv.IsNil() {
+ return nil
+ }
+
+ slice := rv.Elem()
+ if slice.Kind() != reflect.Slice {
+ return fmt.Errorf("%T is not a slice", slicePtr)
+ }
+
+ seen := make(map[string]bool, len(set))
+
+ l := slice.Len()
+ for i := 0; i < l; i++ {
+ if item := slice.Index(i).Interface(); item != nil {
+ s, ok := item.(fmt.Stringer)
+ if !ok {
+ return fmt.Errorf("%T is not a pointer
to a slice of Stringers", slicePtr)
+ }
+ lc := strings.ToLower(s.String())
+ if !lowcased[lc] {
+ return fmt.Errorf("'%s' is not one of
%v", lc, set)
+ }
+ if _, ok := seen[lc]; ok {
+ return fmt.Errorf("duplicate value
found: '%s'", lc)
+ }
+ seen[lc] = true
+ }
+ }
+ return nil
+ }
+}
+
func IsGreaterThanZero(value interface{}) error {
switch v := value.(type) {
case *int:
diff --git a/lib/go-tc/v13/cachegroups.go b/lib/go-tc/v13/cachegroups.go
index 6eb7c0fdf..c2fce59b2 100644
--- a/lib/go-tc/v13/cachegroups.go
+++ b/lib/go-tc/v13/cachegroups.go
@@ -41,35 +41,37 @@ type CacheGroupDetailResponse struct {
// CacheGroup contains information about a given Cachegroup in Traffic Ops.
type CacheGroup struct {
- ID int `json:"id" db:"id"`
- Name string `json:"name" db:"name"`
- ShortName string `json:"shortName"
db:"short_name"`
- Latitude float64 `json:"latitude" db:"latitude"`
- Longitude float64 `json:"longitude"
db:"longitude"`
- ParentName string `json:"parentCachegroupName"
db:"parent_cachegroup_name"`
- ParentCachegroupID int `json:"parentCachegroupId"
db:"parent_cachegroup_id"`
- SecondaryParentName string
`json:"secondaryParentCachegroupName" db:"secondary_parent_cachegroup_name"`
- SecondaryParentCachegroupID int
`json:"secondaryParentCachegroupId" db:"secondary_parent_cachegroup_id"`
- FallbackToClosest bool `json:"fallbackToClosest"
db:"fallback_to_closest"`
- Type string `json:"typeName"
db:"type_name"` // aliased to type_name to disambiguate struct scans due to
join on 'type' table
- TypeID int `json:"typeId" db:"type_id"`
// aliased to type_id to disambiguate struct scans due join on 'type' table
- LastUpdated tc.TimeNoMod `json:"lastUpdated"
db:"last_updated"`
+ ID int `json:"id" db:"id"`
+ Name string `json:"name"
db:"name"`
+ ShortName string `json:"shortName"
db:"short_name"`
+ Latitude float64 `json:"latitude"
db:"latitude"`
+ Longitude float64 `json:"longitude"
db:"longitude"`
+ ParentName string
`json:"parentCachegroupName" db:"parent_cachegroup_name"`
+ ParentCachegroupID int
`json:"parentCachegroupId" db:"parent_cachegroup_id"`
+ SecondaryParentName string
`json:"secondaryParentCachegroupName" db:"secondary_parent_cachegroup_name"`
+ SecondaryParentCachegroupID int
`json:"secondaryParentCachegroupId" db:"secondary_parent_cachegroup_id"`
+ FallbackToClosest bool
`json:"fallbackToClosest" db:"fallback_to_closest"`
+ LocalizationMethods []tc.LocalizationMethod
`json:"localizationMethods" db:"localization_methods"`
+ Type string `json:"typeName"
db:"type_name"` // aliased to type_name to disambiguate struct scans due to
join on 'type' table
+ TypeID int `json:"typeId"
db:"type_id"` // aliased to type_id to disambiguate struct scans due join
on 'type' table
+ LastUpdated tc.TimeNoMod `json:"lastUpdated"
db:"last_updated"`
}
type CacheGroupNullable struct {
- ID *int `json:"id" db:"id"`
- Name *string `json:"name" db:"name"`
- ShortName *string `json:"shortName"
db:"short_name"`
- Latitude *float64 `json:"latitude"
db:"latitude"`
- Longitude *float64
`json:"longitude"db:"longitude"`
- ParentName *string `json:"parentCachegroupName"
db:"parent_cachegroup_name"`
- ParentCachegroupID *int `json:"parentCachegroupId"
db:"parent_cachegroup_id"`
- SecondaryParentName *string
`json:"secondaryParentCachegroupName" db:"secondary_parent_cachegroup_name"`
- SecondaryParentCachegroupID *int
`json:"secondaryParentCachegroupId" db:"secondary_parent_cachegroup_id"`
- FallbackToClosest *bool `json:"fallbackToClosest"
db:"fallback_to_closest"`
- Type *string `json:"typeName"
db:"type_name"` // aliased to type_name to disambiguate struct scans due to
join on 'type' table
- TypeID *int `json:"typeId" db:"type_id"`
// aliased to type_id to disambiguate struct scans due join on 'type' table
- LastUpdated *tc.TimeNoMod `json:"lastUpdated"
db:"last_updated"`
+ ID *int `json:"id" db:"id"`
+ Name *string `json:"name"
db:"name"`
+ ShortName *string `json:"shortName"
db:"short_name"`
+ Latitude *float64 `json:"latitude"
db:"latitude"`
+ Longitude *float64
`json:"longitude"db:"longitude"`
+ ParentName *string
`json:"parentCachegroupName" db:"parent_cachegroup_name"`
+ ParentCachegroupID *int
`json:"parentCachegroupId" db:"parent_cachegroup_id"`
+ SecondaryParentName *string
`json:"secondaryParentCachegroupName" db:"secondary_parent_cachegroup_name"`
+ SecondaryParentCachegroupID *int
`json:"secondaryParentCachegroupId" db:"secondary_parent_cachegroup_id"`
+ FallbackToClosest *bool
`json:"fallbackToClosest" db:"fallback_to_closest"`
+ LocalizationMethods *[]tc.LocalizationMethod
`json:"localizationMethods" db:"localization_methods"`
+ Type *string `json:"typeName"
db:"type_name"` // aliased to type_name to disambiguate struct scans due to
join on 'type' table
+ TypeID *int `json:"typeId"
db:"type_id"` // aliased to type_id to disambiguate struct scans due join
on 'type' table
+ LastUpdated *tc.TimeNoMod
`json:"lastUpdated" db:"last_updated"`
}
type CachegroupTrimmedName struct {
diff --git
a/traffic_ops/app/db/migrations/20180806000000_add_cachegroup_localization_method.sql
b/traffic_ops/app/db/migrations/20180806000000_add_cachegroup_localization_method.sql
new file mode 100644
index 000000000..da1983223
--- /dev/null
+++
b/traffic_ops/app/db/migrations/20180806000000_add_cachegroup_localization_method.sql
@@ -0,0 +1,33 @@
+/*
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+-- +goose Up
+-- SQL in section 'Up' is executed when this migration is applied
+
+CREATE TYPE localization_method AS ENUM ('CZ', 'DEEP_CZ', 'GEO');
+
+CREATE TABLE cachegroup_localization_method (
+ cachegroup bigint NOT NULL REFERENCES cachegroup(id) ON DELETE CASCADE,
+ method localization_method NOT NULL,
+ UNIQUE(cachegroup, method)
+);
+
+CREATE INDEX cachegroup_localization_method_cachegroup_fkey ON
cachegroup_localization_method USING btree (cachegroup);
+
+-- +goose Down
+-- SQL section 'Down' is executed when this migration is rolled back
+
+DROP TABLE IF EXISTS cachegroup_localization_method;
+
+DROP TYPE IF EXISTS localization_method;
diff --git a/traffic_ops/testing/api/v13/cachegroups_test.go
b/traffic_ops/testing/api/v13/cachegroups_test.go
index 9b6a0c1bb..9891756a4 100644
--- a/traffic_ops/testing/api/v13/cachegroups_test.go
+++ b/traffic_ops/testing/api/v13/cachegroups_test.go
@@ -17,9 +17,11 @@ package v13
import (
"fmt"
+ "reflect"
"testing"
"github.com/apache/trafficcontrol/lib/go-log"
+ "github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/lib/go-tc/v13"
"github.com/apache/trafficcontrol/traffic_ops/testing/api/utils"
)
@@ -157,6 +159,26 @@ func UpdateTestCacheGroups(t *testing.T) {
failed = true
}
+ // test localizationMethods
+ expectedMethods := []tc.LocalizationMethod{tc.LocalizationMethodGeo}
+ cg.LocalizationMethods = &expectedMethods
+ updResp, _, err = TOSession.UpdateCacheGroupNullableByID(*cg.ID, cg)
+ if err != nil {
+ t.Errorf("cannot UPDATE CacheGroup by id: %v - %v\n", err,
updResp)
+ failed = true
+ }
+
+ resp, _, err = TOSession.GetCacheGroupNullableByID(*cg.ID)
+ if err != nil {
+ t.Errorf("cannot GET CacheGroup by id: '%d', %v\n", *cg.ID, err)
+ failed = true
+ }
+ cg = resp[0]
+ if !reflect.DeepEqual(expectedMethods, *cg.LocalizationMethods) {
+ t.Errorf("failed to update localizationMethods (expected = %v,
actual = %v\n", expectedMethods, *cg.LocalizationMethods)
+ failed = true
+ }
+
if !failed {
log.Debugln("UpdateTestCacheGroups() PASSED: ")
}
diff --git a/traffic_ops/testing/api/v13/tc-fixtures.json
b/traffic_ops/testing/api/v13/tc-fixtures.json
index e1b221289..e9cdfd96e 100644
--- a/traffic_ops/testing/api/v13/tc-fixtures.json
+++ b/traffic_ops/testing/api/v13/tc-fixtures.json
@@ -38,6 +38,11 @@
"parentCachegroupName": "parentCachegroup",
"secondaryParentCachegroupName": "secondaryCachegroup",
"shortName": "cg1",
+ "localizationMethods": [
+ "CZ",
+ "DEEP_CZ",
+ "GEO"
+ ],
"typeName": "EDGE_LOC"
},
{
diff --git a/traffic_ops/traffic_ops_golang/cachegroup/cachegroups.go
b/traffic_ops/traffic_ops_golang/cachegroup/cachegroups.go
index 23a315417..d9e7ab4e6 100644
--- a/traffic_ops/traffic_ops_golang/cachegroup/cachegroups.go
+++ b/traffic_ops/traffic_ops_golang/cachegroup/cachegroups.go
@@ -175,6 +175,7 @@ func (cg TOCacheGroup) Validate() error {
"longitude":
validation.Validate(cg.Longitude, validation.Min(-180.0).Error(longitudeErr),
validation.Max(180.0).Error(longitudeErr)),
"parentCacheGroupID":
validation.Validate(cg.ParentCachegroupID, validation.Min(1)),
"secondaryParentCachegroupID":
validation.Validate(cg.SecondaryParentCachegroupID, validation.Min(1)),
+ "localizationMethods":
validation.Validate(cg.LocalizationMethods,
validation.By(tovalidate.IsPtrToSliceOfUniqueStringersICase("CZ", "DEEP_CZ",
"GEO"))),
}
return util.JoinErrs(tovalidate.ToErrors(errs))
}
@@ -236,10 +237,30 @@ func (cg *TOCacheGroup) Create() (error, tc.ApiErrorType)
{
return tc.DBError, tc.SystemError
}
cg.SetID(id)
+ if err = cg.createLocalizationMethods(); err != nil {
+ log.Errorln("creating cachegroup: " + err.Error())
+ return tc.DBError, tc.SystemError
+ }
cg.LastUpdated = &lastUpdated
return nil, tc.NoError
}
+func (cg *TOCacheGroup) createLocalizationMethods() error {
+ q := `DELETE FROM cachegroup_localization_method where cachegroup = $1`
+ if _, err := cg.ReqInfo.Tx.Exec(q, *cg.ID); err != nil {
+ return fmt.Errorf("unable to delete
cachegroup_localization_methods for cachegroup %d: %s", *cg.ID, err.Error())
+ }
+ if cg.LocalizationMethods != nil {
+ q = `INSERT INTO cachegroup_localization_method (cachegroup,
method) VALUES ($1, $2)`
+ for _, method := range *cg.LocalizationMethods {
+ if _, err := cg.ReqInfo.Tx.Exec(q, *cg.ID,
method.String()); err != nil {
+ return fmt.Errorf("unable to insert
cachegroup_localization_methods for cachegroup %d: %s", *cg.ID, err.Error())
+ }
+ }
+ }
+ return nil
+}
+
func (cg *TOCacheGroup) createCoordinate() (*int, error) {
var coordinateID *int
if cg.Latitude != nil && cg.Longitude != nil {
@@ -322,17 +343,33 @@ func (cg *TOCacheGroup) Read(parameters
map[string]string) ([]interface{}, []err
}
defer rows.Close()
- CacheGroups := []interface{}{}
+ cacheGroups := []interface{}{}
for rows.Next() {
var s TOCacheGroup
- if err = rows.StructScan(&s); err != nil {
+ lms := make([]tc.LocalizationMethod, 0)
+ if err = rows.Scan(
+ &s.ID,
+ &s.Name,
+ &s.ShortName,
+ &s.Latitude,
+ &s.Longitude,
+ pq.Array(&lms),
+ &s.ParentCachegroupID,
+ &s.ParentName,
+ &s.SecondaryParentCachegroupID,
+ &s.SecondaryParentName,
+ &s.Type,
+ &s.TypeID,
+ &s.LastUpdated,
+ ); err != nil {
log.Errorf("error parsing CacheGroup rows: %v", err)
return nil, []error{tc.DBError}, tc.SystemError
}
- CacheGroups = append(CacheGroups, s)
+ s.LocalizationMethods = &lms
+ cacheGroups = append(cacheGroups, s)
}
- return CacheGroups, []error{}, tc.NoError
+ return cacheGroups, []error{}, tc.NoError
}
//The TOCacheGroup implementation of the Updater interface
@@ -389,6 +426,10 @@ func (cg *TOCacheGroup) Update() (error, tc.ApiErrorType) {
return fmt.Errorf("this update affected too many rows:
%d", rowsAffected), tc.SystemError
}
}
+ if err = cg.createLocalizationMethods(); err != nil {
+ log.Errorln("updating cachegroup: " + err.Error())
+ return tc.DBError, tc.SystemError
+ }
return nil, tc.NoError
}
@@ -507,6 +548,7 @@ cachegroup.name,
cachegroup.short_name,
coordinate.latitude,
coordinate.longitude,
+(SELECT array_agg(CAST(method as text)) AS localization_methods FROM
cachegroup_localization_method clm WHERE clm.cachegroup = cachegroup.id),
cachegroup.parent_cachegroup_id,
cgp.name AS parent_cachegroup_name,
cachegroup.secondary_parent_cachegroup_id,
diff --git a/traffic_ops/traffic_ops_golang/cachegroup/cachegroups_test.go
b/traffic_ops/traffic_ops_golang/cachegroup/cachegroups_test.go
index 645f990e3..2335f705e 100644
--- a/traffic_ops/traffic_ops_golang/cachegroup/cachegroups_test.go
+++ b/traffic_ops/traffic_ops_golang/cachegroup/cachegroups_test.go
@@ -46,6 +46,11 @@ func getTestCacheGroups() []v13.CacheGroup {
Longitude: 90.7,
ParentCachegroupID: 2,
SecondaryParentCachegroupID: 2,
+ LocalizationMethods: []tc.LocalizationMethod{
+ tc.LocalizationMethodDeepCZ,
+ tc.LocalizationMethodCZ,
+ tc.LocalizationMethodGeo,
+ },
Type: "EDGE_LOC",
TypeID: 6,
LastUpdated: tc.TimeNoMod{Time: time.Now()},
@@ -60,6 +65,11 @@ func getTestCacheGroups() []v13.CacheGroup {
Longitude: 90.7,
ParentCachegroupID: 1,
SecondaryParentCachegroupID: 1,
+ LocalizationMethods: []tc.LocalizationMethod{
+ tc.LocalizationMethodDeepCZ,
+ tc.LocalizationMethodCZ,
+ tc.LocalizationMethodGeo,
+ },
Type: "MID_LOC",
TypeID: 7,
LastUpdated: tc.TimeNoMod{Time: time.Now()},
@@ -80,8 +90,21 @@ func TestReadCacheGroups(t *testing.T) {
defer db.Close()
testCGs := getTestCacheGroups()
- cols := test.ColsFromStructByTag("db", v13.CacheGroup{})
- rows := sqlmock.NewRows(cols)
+ rows := sqlmock.NewRows([]string{
+ "id",
+ "name",
+ "short_name",
+ "latitude",
+ "longitude",
+ "localization_methods",
+ "parent_cachegroup_id",
+ "parent_cachegroup_name",
+ "secondary_parent_cachegroup_id",
+ "secondary_parent_cachegroup_name",
+ "type_name",
+ "type_id",
+ "last_updated",
+ })
for _, ts := range testCGs {
rows = rows.AddRow(
@@ -90,11 +113,11 @@ func TestReadCacheGroups(t *testing.T) {
ts.ShortName,
ts.Latitude,
ts.Longitude,
- ts.ParentName,
+ []byte("{DEEP_CZ,CZ,GEO}"),
ts.ParentCachegroupID,
- ts.SecondaryParentName,
+ ts.ParentName,
ts.SecondaryParentCachegroupID,
- ts.FallbackToClosest,
+ ts.SecondaryParentName,
ts.Type,
ts.TypeID,
ts.LastUpdated,
@@ -177,22 +200,26 @@ func TestValidate(t *testing.T) {
sn := "not!a!valid!shortname"
la := -190.0
lo := -190.0
+ lm := []tc.LocalizationMethod{tc.LocalizationMethodGeo,
tc.LocalizationMethodInvalid}
ty := "EDGE_LOC"
ti := 6
lu := tc.TimeNoMod{Time: time.Now()}
- c := TOCacheGroup{ReqInfo: &reqInfo, CacheGroupNullable:
v13.CacheGroupNullable{ID: &id,
- Name: &nm,
- ShortName: &sn,
- Latitude: &la,
- Longitude: &lo,
- Type: &ty,
- TypeID: &ti,
- LastUpdated: &lu,
+ c := TOCacheGroup{ReqInfo: &reqInfo, CacheGroupNullable:
v13.CacheGroupNullable{
+ ID: &id,
+ Name: &nm,
+ ShortName: &sn,
+ Latitude: &la,
+ Longitude: &lo,
+ LocalizationMethods: &lm,
+ Type: &ty,
+ TypeID: &ti,
+ LastUpdated: &lu,
}}
errs :=
util.JoinErrsStr(test.SortErrors(test.SplitErrors(c.Validate())))
expectedErrs := util.JoinErrsStr([]error{
errors.New(`'latitude' Must be a floating point number within
the range +-90`),
+ errors.New(`'localizationMethods' 'invalid' is not one of [CZ
DEEP_CZ GEO]`),
errors.New(`'longitude' Must be a floating point number within
the range +-180`),
errors.New(`'name' invalid characters found - Use alphanumeric
. or - or _ .`),
errors.New(`'shortName' invalid characters found - Use
alphanumeric . or - or _ .`),
@@ -210,14 +237,17 @@ func TestValidate(t *testing.T) {
sn = `awesome-cachegroup`
la = 90.0
lo = 90.0
- c = TOCacheGroup{ReqInfo: &reqInfo, CacheGroupNullable:
v13.CacheGroupNullable{ID: &id,
- Name: &nm,
- ShortName: &sn,
- Latitude: &la,
- Longitude: &lo,
- Type: &ty,
- TypeID: &ti,
- LastUpdated: &lu,
+ lm = []tc.LocalizationMethod{tc.LocalizationMethodGeo,
tc.LocalizationMethodCZ, tc.LocalizationMethodDeepCZ}
+ c = TOCacheGroup{ReqInfo: &reqInfo, CacheGroupNullable:
v13.CacheGroupNullable{
+ ID: &id,
+ Name: &nm,
+ ShortName: &sn,
+ Latitude: &la,
+ Longitude: &lo,
+ LocalizationMethods: &lm,
+ Type: &ty,
+ TypeID: &ti,
+ LastUpdated: &lu,
}}
err = c.Validate()
if err != nil {
diff --git a/traffic_ops/traffic_ops_golang/crconfig/edgelocations.go
b/traffic_ops/traffic_ops_golang/crconfig/edgelocations.go
index 364d33608..64c216bca 100644
--- a/traffic_ops/traffic_ops_golang/crconfig/edgelocations.go
+++ b/traffic_ops/traffic_ops_golang/crconfig/edgelocations.go
@@ -24,6 +24,7 @@ import (
"errors"
"github.com/apache/trafficcontrol/lib/go-tc"
+ "github.com/lib/pq"
)
func makeLocations(cdn string, db *sql.DB)
(map[string]tc.CRConfigLatitudeLongitude,
map[string]tc.CRConfigLatitudeLongitude, error) {
@@ -32,7 +33,9 @@ func makeLocations(cdn string, db *sql.DB)
(map[string]tc.CRConfigLatitudeLongit
// TODO test whether it's faster to do a single query, joining lat/lon
into servers
q := `
-select cg.name, cg.id, t.name as type, co.latitude, co.longitude,
cg.fallback_to_closest from cachegroup as cg
+select cg.name, cg.id, t.name as type, co.latitude, co.longitude,
cg.fallback_to_closest,
+(SELECT array_agg(method::text) as localization_methods FROM
cachegroup_localization_method WHERE cachegroup = cg.id)
+from cachegroup as cg
left join coordinate as co on co.id = cg.coordinate
inner join server as s on s.cachegroup = cg.id
inner join type as t on t.id = s.type
@@ -54,9 +57,13 @@ and (st.name = 'REPORTED' or st.name = 'ONLINE' or st.name =
'ADMIN_DOWN')
ttype := ""
var fallbackToClosest *bool
latlon := tc.CRConfigLatitudeLongitude{}
- if err := rows.Scan(&cachegroup, &primaryCacheID, &ttype,
&latlon.Lat, &latlon.Lon, &fallbackToClosest); err != nil {
+ if err := rows.Scan(&cachegroup, &primaryCacheID, &ttype,
&latlon.Lat, &latlon.Lon, &fallbackToClosest,
pq.Array(&latlon.LocalizationMethods)); err != nil {
return nil, nil, errors.New("Error scanning cachegroup:
" + err.Error())
}
+ if len(latlon.LocalizationMethods) == 0 {
+ // to keep current default behavior when
localizationMethods is unset/empty, enable all current localization methods
+ latlon.LocalizationMethods =
[]tc.LocalizationMethod{tc.LocalizationMethodGeo, tc.LocalizationMethodCZ,
tc.LocalizationMethodDeepCZ}
+ }
if ttype == RouterTypeName {
routerLocs[cachegroup] = latlon
} else {
diff --git a/traffic_ops/traffic_ops_golang/crconfig/edgelocations_test.go
b/traffic_ops/traffic_ops_golang/crconfig/edgelocations_test.go
index b0cd674dc..3465fb710 100644
--- a/traffic_ops/traffic_ops_golang/crconfig/edgelocations_test.go
+++ b/traffic_ops/traffic_ops_golang/crconfig/edgelocations_test.go
@@ -30,22 +30,38 @@ import (
func ExpectedMakeLocations() (map[string]tc.CRConfigLatitudeLongitude,
map[string]tc.CRConfigLatitudeLongitude) {
return map[string]tc.CRConfigLatitudeLongitude{
- "cache0": tc.CRConfigLatitudeLongitude{Lat:
*randFloat64(), Lon: *randFloat64()},
- "cache1": tc.CRConfigLatitudeLongitude{Lat:
*randFloat64(), Lon: *randFloat64()},
+ "cache0": tc.CRConfigLatitudeLongitude{
+ Lat: *randFloat64(),
+ Lon: *randFloat64(),
+ LocalizationMethods:
[]tc.LocalizationMethod{tc.LocalizationMethodCZ},
+ },
+ "cache1": tc.CRConfigLatitudeLongitude{
+ Lat: *randFloat64(),
+ Lon: *randFloat64(),
+ LocalizationMethods:
[]tc.LocalizationMethod{tc.LocalizationMethodCZ},
+ },
},
map[string]tc.CRConfigLatitudeLongitude{
- "router0": tc.CRConfigLatitudeLongitude{Lat:
*randFloat64(), Lon: *randFloat64()},
- "router1": tc.CRConfigLatitudeLongitude{Lat:
*randFloat64(), Lon: *randFloat64()},
+ "router0": tc.CRConfigLatitudeLongitude{
+ Lat: *randFloat64(),
+ Lon: *randFloat64(),
+ LocalizationMethods:
[]tc.LocalizationMethod{tc.LocalizationMethodGeo, tc.LocalizationMethodCZ,
tc.LocalizationMethodDeepCZ},
+ },
+ "router1": tc.CRConfigLatitudeLongitude{
+ Lat: *randFloat64(),
+ Lon: *randFloat64(),
+ LocalizationMethods:
[]tc.LocalizationMethod{tc.LocalizationMethodGeo, tc.LocalizationMethodCZ,
tc.LocalizationMethodDeepCZ},
+ },
}
}
func MockMakeLocations(mock sqlmock.Sqlmock, expectedEdgeLocs
map[string]tc.CRConfigLatitudeLongitude, expectedRouterLocs
map[string]tc.CRConfigLatitudeLongitude, cdn string) {
- rows := sqlmock.NewRows([]string{"name", "id", "type", "latitude",
"longitude", "fallback_to_closest"})
+ rows := sqlmock.NewRows([]string{"name", "id", "type", "latitude",
"longitude", "fallback_to_closest", "localization_methods"})
for s, l := range expectedEdgeLocs {
- rows = rows.AddRow(s, 1, EdgeTypePrefix, l.Lat, l.Lon, false)
+ rows = rows.AddRow(s, 1, EdgeTypePrefix, l.Lat, l.Lon, false,
[]byte("{CZ}"))
}
for s, l := range expectedRouterLocs {
- rows = rows.AddRow(s, 1, RouterTypeName, l.Lat, l.Lon, false)
+ rows = rows.AddRow(s, 1, RouterTypeName, l.Lat, l.Lon, false,
nil)
}
mock.ExpectQuery("select").WithArgs(cdn).WillReturnRows(rows)
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
With regards,
Apache Git Services