This is an automated email from the ASF dual-hosted git repository.
zrhoffman 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 0ca1357 Validate DS Topologies when deleting a server capability
(#5091)
0ca1357 is described below
commit 0ca1357cef54998e41a86dfa5f58bf458225e8a6
Author: Rawlin Peters <[email protected]>
AuthorDate: Thu Oct 1 14:23:09 2020 -0600
Validate DS Topologies when deleting a server capability (#5091)
* Validate DS Topologies when deleting a server capability
There must be at least one server in a cachegroup that can satisfy the
required capabilities of the delivery services the cachegroup is
assigned to via topologies. Otherwise, the topologies would no longer be
valid w.r.t. the required capabilities of the delivery services.
* Address review feedback
---
CHANGELOG.md | 1 +
.../deliveryservices_required_capabilities_test.go | 26 +-
.../testing/api/v3/serverservercapability_test.go | 165 +++++-
traffic_ops/testing/api/v3/tc-fixtures.json | 555 +++++++++++++++++++++
traffic_ops/testing/api/v3/traffic_control_test.go | 73 +--
traffic_ops/testing/api/v3/withobjs_test.go | 72 +--
.../deliveryservices_required_capabilities.go | 5 +-
.../server/servers_server_capability.go | 173 ++++++-
traffic_ops/traffic_ops_golang/tenant/tenancy.go | 12 -
9 files changed, 943 insertions(+), 139 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3662550..e2f72bb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@ The format is based on [Keep a
Changelog](http://keepachangelog.com/en/1.0.0/).
- Traffic Ops: Added support for `topology` query parameter for `GET
/api/3.0/servers` to return all servers whose cachegroups are in a given
topology.
- Traffic Ops: Added new topology-based delivery service fields for header
rewrites: `firstHeaderRewrite`, `innerHeaderRewrite`, `lastHeaderRewrite`
- Traffic Ops: Added validation to prohibit assigning caches to
topology-based delivery services
+ - Traffic Ops: Added validation to prohibit removing a capability from a
server if no other server in the same cachegroup can satisfy the required
capabilities of the delivery services assigned to it via topologies.
- Traffic Ops: Consider Topologies parentage when queueing or checking
server updates
- ORT: Added Topologies to Config Generation.
- Traffic Portal: Added the ability to create, read, update and delete
flexible topologies.
diff --git
a/traffic_ops/testing/api/v3/deliveryservices_required_capabilities_test.go
b/traffic_ops/testing/api/v3/deliveryservices_required_capabilities_test.go
index afb9091..d1e2be3 100644
--- a/traffic_ops/testing/api/v3/deliveryservices_required_capabilities_test.go
+++ b/traffic_ops/testing/api/v3/deliveryservices_required_capabilities_test.go
@@ -17,13 +17,13 @@ package v3
import (
"fmt"
- "github.com/apache/trafficcontrol/lib/go-rfc"
"net/http"
"net/url"
"strings"
"testing"
"time"
+ "github.com/apache/trafficcontrol/lib/go-rfc"
"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/lib/go-util"
)
@@ -144,7 +144,7 @@ func GetTestDeliveryServicesRequiredCapabilities(t
*testing.T) {
for _, tc := range testCases {
t.Run(tc.description, func(t *testing.T) {
- capabilities, _, err :=
TOSession.GetDeliveryServicesRequiredCapabilities(tc.capability.DeliveryServiceID,
tc.capability.XMLID, tc.capability.RequiredCapability)
+ capabilities, _, err :=
TOSession.GetDeliveryServicesRequiredCapabilitiesWithHdr(tc.capability.DeliveryServiceID,
tc.capability.XMLID, tc.capability.RequiredCapability, nil)
if err != nil {
t.Fatalf("%s; got err= %v; expected err= nil",
tc.description, err)
}
@@ -208,6 +208,20 @@ func GetTestDeliveryServicesRequiredCapabilitiesIMS(t
*testing.T) {
})
}
}
+func CreateTestTopologyBasedDeliveryServicesRequiredCapabilities(t *testing.T)
{
+ for _, td := range
testData.TopologyBasedDeliveryServicesRequiredCapabilities {
+
+ c := tc.DeliveryServicesRequiredCapability{
+ DeliveryServiceID: helperGetDeliveryServiceID(t,
td.XMLID),
+ RequiredCapability: td.RequiredCapability,
+ }
+
+ _, _, err :=
TOSession.CreateDeliveryServicesRequiredCapability(c)
+ if err != nil {
+ t.Fatalf("cannot create delivery service required
capability: %v", err)
+ }
+ }
+}
func CreateTestDeliveryServicesRequiredCapabilities(t *testing.T) {
data := testData.DeliveryServicesRequiredCapabilities
@@ -301,7 +315,7 @@ func InvalidDeliveryServicesRequiredCapabilityAddition(t
*testing.T) {
// Tests that a capability cannot be made required if the DS's services
do not have it assigned
// Get Delivery Capability for a DS
- capabilities, _, err :=
TOSession.GetDeliveryServicesRequiredCapabilities(nil, util.StrPtr("ds1"), nil)
+ capabilities, _, err :=
TOSession.GetDeliveryServicesRequiredCapabilitiesWithHdr(nil,
util.StrPtr("ds1"), nil, nil)
if err != nil {
t.Fatalf("cannot GET delivery service required capabilities:
%v", err)
}
@@ -313,7 +327,7 @@ func InvalidDeliveryServicesRequiredCapabilityAddition(t
*testing.T) {
// TODO: DON'T hard-code hostnames!
params := url.Values{}
params.Add("hostName", "atlanta-edge-01")
- resp, _, err := TOSession.GetServers(¶ms)
+ resp, _, err := TOSession.GetServersWithHdr(¶ms, nil)
if err != nil {
t.Fatalf("cannot GET Server by hostname: %v", err)
}
@@ -389,7 +403,7 @@ func InvalidDeliveryServicesRequiredCapabilityAddition(t
*testing.T) {
func DeleteTestDeliveryServicesRequiredCapabilities(t *testing.T) {
// Get Required Capabilities to delete them
- capabilities, _, err :=
TOSession.GetDeliveryServicesRequiredCapabilities(nil, nil, nil)
+ capabilities, _, err :=
TOSession.GetDeliveryServicesRequiredCapabilitiesWithHdr(nil, nil, nil, nil)
if err != nil {
t.Fatalf(err.Error())
}
@@ -445,7 +459,7 @@ func helperGetDeliveryServiceID(t *testing.T, xmlID
*string) *int {
if xmlID == nil {
t.Fatal("xml id must not be nil")
}
- ds, _, err := TOSession.GetDeliveryServiceByXMLIDNullable(*xmlID)
+ ds, _, err :=
TOSession.GetDeliveryServiceByXMLIDNullableWithHdr(*xmlID, nil)
if err != nil {
t.Fatal(err)
}
diff --git a/traffic_ops/testing/api/v3/serverservercapability_test.go
b/traffic_ops/testing/api/v3/serverservercapability_test.go
index c419422..66742d8 100644
--- a/traffic_ops/testing/api/v3/serverservercapability_test.go
+++ b/traffic_ops/testing/api/v3/serverservercapability_test.go
@@ -16,12 +16,12 @@ package v3
*/
import (
- "github.com/apache/trafficcontrol/lib/go-rfc"
"net/http"
"net/url"
"testing"
"time"
+ "github.com/apache/trafficcontrol/lib/go-rfc"
"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/lib/go-util"
)
@@ -33,6 +33,12 @@ func TestServerServerCapabilities(t *testing.T) {
})
}
+func TestServerServerCapabilitiesForTopologies(t *testing.T) {
+ WithObjs(t, []TCObj{CDNs, Types, Tenants, Parameters, Profiles,
Statuses, Divisions, Regions, PhysLocations, CacheGroups, Servers, Topologies,
DeliveryServices, ServerCapabilities, ServerServerCapabilitiesForTopologies,
TopologyBasedDeliveryServiceRequiredCapabilities}, func() {
+ DeleteTestServerServerCapabilitiesForTopologiesValidation(t)
+ })
+}
+
func GetTestServerServerCapabilitiesIMS(t *testing.T) {
var header http.Header
header = make(map[string][]string)
@@ -58,7 +64,7 @@ func CreateTestServerServerCapabilities(t *testing.T) {
t.Fatalf("server-server-capability structure had nil
server")
}
params.Set("hostName", *ssc.Server)
- resp, _, err := TOSession.GetServers(¶ms)
+ resp, _, err := TOSession.GetServersWithHdr(¶ms, nil)
if err != nil {
t.Fatalf("cannot GET Server by hostname '%s': %v - %v",
*ssc.Server, err, resp.Alerts)
}
@@ -126,7 +132,7 @@ func CreateTestServerServerCapabilities(t *testing.T) {
// Attempt to assign a server capability to a non MID/EDGE server
// TODO: DON'T hard-code server hostnames!
params.Set("hostName", "riak")
- resp, _, err := TOSession.GetServers(¶ms)
+ resp, _, err := TOSession.GetServersWithHdr(¶ms, nil)
if err != nil {
t.Fatalf("cannot GET Server by hostname 'riak': %v - %v", err,
resp.Alerts)
}
@@ -147,7 +153,7 @@ func CreateTestServerServerCapabilities(t *testing.T) {
func GetTestServerServerCapabilities(t *testing.T) {
// Get All Server Capabilities
- sscs, _, err := TOSession.GetServerServerCapabilities(nil, nil, nil)
+ sscs, _, err := TOSession.GetServerServerCapabilitiesWithHdr(nil, nil,
nil, nil)
if err != nil {
t.Fatalf("cannot GET server capabilities assigned to servers:
%v", err)
}
@@ -158,41 +164,44 @@ func GetTestServerServerCapabilities(t *testing.T) {
t.Errorf("expect %v server capabilities assigned to servers
received %v ", len(testData.ServerServerCapabilities), len(sscs))
}
- checkResp := func(t *testing.T, sscs []tc.ServerServerCapability) {
- if sscs == nil {
- t.Fatal("returned server capabilities assigned to
servers was nil\n")
- }
- if len(sscs) != 1 {
- t.Errorf("expect 1 server capabilities assigned to
server received %v ", len(sscs))
- }
- }
-
for _, ssc := range sscs {
// Get assigned Server Capabilities by server id
- sscs, _, err :=
TOSession.GetServerServerCapabilities(ssc.ServerID, nil, nil)
+ sscs, _, err :=
TOSession.GetServerServerCapabilitiesWithHdr(ssc.ServerID, nil, nil, nil)
if err != nil {
t.Fatalf("cannot GET server capabilities assigned to
servers by server ID %v: %v", *ssc.ServerID, err)
}
- checkResp(t, sscs)
+ for _, s := range sscs {
+ if *s.ServerID != *ssc.ServerID {
+ t.Errorf("GET server server capabilities by
serverID returned non-matching server ID: %d", *s.ServerID)
+ }
+ }
// Get assigned Server Capabilities by host name
- sscs, _, err = TOSession.GetServerServerCapabilities(nil,
ssc.Server, nil)
+ sscs, _, err =
TOSession.GetServerServerCapabilitiesWithHdr(nil, ssc.Server, nil, nil)
if err != nil {
t.Fatalf("cannot GET server capabilities assigned to
servers by server host name %v: %v", *ssc.Server, err)
}
- checkResp(t, sscs)
+ for _, s := range sscs {
+ if *s.Server != *ssc.Server {
+ t.Errorf("GET server server capabilities by
serverHostName returned non-matching server hostname: %s", *s.Server)
+ }
+ }
// Get assigned Server Capabilities by server capability
- sscs, _, err = TOSession.GetServerServerCapabilities(nil, nil,
ssc.ServerCapability)
+ sscs, _, err =
TOSession.GetServerServerCapabilitiesWithHdr(nil, nil, ssc.ServerCapability,
nil)
if err != nil {
t.Fatalf("cannot GET server capabilities assigned to
servers by server capability %v: %v", *ssc.ServerCapability, err)
}
- checkResp(t, sscs)
+ for _, s := range sscs {
+ if *s.ServerCapability != *ssc.ServerCapability {
+ t.Errorf("GET server server capabilities by
server capability returned non-matching server capability: %s",
*s.ServerCapability)
+ }
+ }
}
}
func DeleteTestServerServerCapabilities(t *testing.T) {
// Get Server Capabilities to delete them
- sscs, _, err := TOSession.GetServerServerCapabilities(nil, nil, nil)
+ sscs, _, err := TOSession.GetServerServerCapabilitiesWithHdr(nil, nil,
nil, nil)
if err != nil {
t.Fatalf("cannot GET server capabilities assigned to servers:
%v", err)
}
@@ -200,19 +209,40 @@ func DeleteTestServerServerCapabilities(t *testing.T) {
t.Fatal("returned server capabilities assigned to servers was
nil\n")
}
+ dses, _, err := TOSession.GetDeliveryServicesV30WithHdr(nil, nil)
+ if err != nil {
+ t.Fatalf("cannot GET delivery services: %v", err)
+ }
+ dsIDtoDS := make(map[int]tc.DeliveryServiceNullableV30, len(dses))
+ for _, ds := range dses {
+ dsIDtoDS[*ds.ID] = ds
+ }
+
// Assign servers to DSes that have the capability required
// Used to make sure we block server server_capability DELETE in that
case
dsServers := []tc.DeliveryServiceServer{}
+ assignedServers := make(map[int]bool)
for _, ssc := range sscs {
- dsReqCapResp, _, err :=
TOSession.GetDeliveryServicesRequiredCapabilities(nil, nil,
ssc.ServerCapability)
+ dsReqCapResp, _, err :=
TOSession.GetDeliveryServicesRequiredCapabilitiesWithHdr(nil, nil,
ssc.ServerCapability, nil)
if err != nil {
t.Fatalf("cannot GET delivery service required
capabilities: %v", err)
}
if len(dsReqCapResp) == 0 {
- t.Fatalf("at least one delivery service needs the
capability %v required", *ssc.ServerCapability)
+ // capability is not required by any delivery service
+ continue
+ }
+ var dsReqCap tc.DeliveryServicesRequiredCapability
+ for _, dsrc := range dsReqCapResp {
+ if dsIDtoDS[*dsrc.DeliveryServiceID].Topology == nil {
+ dsReqCap = dsrc
+ break
+ }
+ }
+ if dsReqCap.DeliveryServiceID == nil {
+ // didn't find a non-topology-based dsReqCap for this
ssc
+ continue
}
- dsReqCap := dsReqCapResp[0]
// Assign server to ds
_, _, err =
TOSession.CreateDeliveryServiceServers(*dsReqCap.DeliveryServiceID,
[]int{*ssc.ServerID}, false)
@@ -223,13 +253,19 @@ func DeleteTestServerServerCapabilities(t *testing.T) {
Server: ssc.ServerID,
DeliveryService: dsReqCap.DeliveryServiceID,
})
+ assignedServers[*ssc.ServerID] = true
+ }
+ if len(dsServers) == 0 {
+ t.Fatalf("test requires at least one server with a capability
that is required by at least one delivery service")
}
// Delete should fail as their delivery services now require the
capabilities
for _, ssc := range sscs {
- _, _, err :=
TOSession.DeleteServerServerCapability(*ssc.ServerID, *ssc.ServerCapability)
- if err == nil {
- t.Fatalf("should have gotten error when using DELETE on
the server capability %v from server %v as it is required by associated dses",
*ssc.ServerCapability, *ssc.Server)
+ if assignedServers[*ssc.ServerID] {
+ _, _, err :=
TOSession.DeleteServerServerCapability(*ssc.ServerID, *ssc.ServerCapability)
+ if err == nil {
+ t.Fatalf("should have gotten error when using
DELETE on the server capability %v from server %v as it is required by
associated dses", *ssc.ServerCapability, *ssc.Server)
+ }
}
}
@@ -250,3 +286,80 @@ func DeleteTestServerServerCapabilities(t *testing.T) {
}
}
+
+func DeleteTestServerServerCapabilitiesForTopologiesValidation(t *testing.T) {
+ // dtrc-edge-01 and dtrc-edge-02 (capabilities = ram, disk) are
assigned to
+ // ds-top-req-cap (topology = top-for-ds-req; required capabilities =
ram, disk) and
+ // ds-top-req-cap2 (topology = top-for-ds-req2; required capabilities =
ram)
+ var edge1 tc.ServerNullable
+ var edge2 tc.ServerNullable
+
+ servers, _, err := TOSession.GetServersWithHdr(nil, nil)
+ if err != nil {
+ t.Fatalf("cannot GET servers: %v", err)
+ }
+ for _, s := range servers.Response {
+ if *s.HostName == "dtrc-edge-01" {
+ edge1 = s
+ }
+ if *s.HostName == "dtrc-edge-02" {
+ edge2 = s
+ }
+ }
+ if edge1.HostName == nil || edge2.HostName == nil {
+ t.Fatalf("expected servers with hostName dtrc-edge-01 and
dtrc-edge-02")
+ }
+
+ // delete should succeed because dtrc-edge-02 still has the required
capabilities
+ // for ds-top-req-cap and ds-top-req-cap2 within the cachegroup
+ _, _, err = TOSession.DeleteServerServerCapability(*edge1.ID, "ram")
+ if err != nil {
+ t.Fatalf("when deleting server server capability, expected: nil
error, actual: %v", err)
+ }
+
+ // delete should fail because dtrc-edge-02 is the last server in the
cachegroup that
+ // has ds-top-req-cap's required capabilities
+ _, reqInf, err := TOSession.DeleteServerServerCapability(*edge2.ID,
"ram")
+ if err == nil {
+ t.Fatalf("when deleting server server capability, expected:
error, actual: nil")
+ }
+ if reqInf.StatusCode != http.StatusBadRequest {
+ t.Errorf("when deleting server server capability, expected
status code: %d, actual: %d", http.StatusBadRequest, reqInf.StatusCode)
+ }
+
+ // delete should fail because dtrc-edge-02 is the last server in the
cachegroup that
+ // has ds-top-req-cap's required capabilities
+ _, r, err := TOSession.DeleteServerServerCapability(*edge2.ID, "disk")
+ if err == nil {
+ t.Fatalf("when deleting required server server capability,
expected: error, actual: nil")
+ }
+ if r.StatusCode != http.StatusBadRequest {
+ t.Errorf("when deleting required server server capability,
expected status code: %d, actual: %d", http.StatusBadRequest, reqInf.StatusCode)
+ }
+
+ // delete should succeed because dtrc-edge-02 still has the required
capabilities
+ // for ds-top-req-cap and ds-top-req-cap2 within the cachegroup
+ _, _, err = TOSession.DeleteServerServerCapability(*edge1.ID, "disk")
+ if err != nil {
+ t.Fatalf("when deleting server server capability, expected: nil
error, actual: %v", err)
+ }
+}
+
+func DeleteTestServerServerCapabilitiesForTopologies(t *testing.T) {
+ // Get Server Capabilities to delete them
+ sscs, _, err := TOSession.GetServerServerCapabilitiesWithHdr(nil, nil,
nil, nil)
+ if err != nil {
+ t.Fatalf("cannot GET server capabilities assigned to servers:
%v", err)
+ }
+ if sscs == nil {
+ t.Fatal("returned server capabilities assigned to servers was
nil\n")
+ }
+
+ for _, ssc := range sscs {
+ _, _, err :=
TOSession.DeleteServerServerCapability(*ssc.ServerID, *ssc.ServerCapability)
+ if err != nil {
+ t.Errorf("could not DELETE the server capability %v
from server %v: %v", *ssc.ServerCapability, *ssc.Server, err)
+ }
+ }
+
+}
diff --git a/traffic_ops/testing/api/v3/tc-fixtures.json
b/traffic_ops/testing/api/v3/tc-fixtures.json
index 5561716..fcf7d5c 100644
--- a/traffic_ops/testing/api/v3/tc-fixtures.json
+++ b/traffic_ops/testing/api/v3/tc-fixtures.json
@@ -201,6 +201,27 @@
"name": "topology-mid-cg-07",
"shortName": "tm7",
"typeName": "MID_LOC"
+ },
+ {
+ "latitude": 0,
+ "longitude": 0,
+ "name": "dtrc1",
+ "shortName": "dtrc1",
+ "typeName": "MID_LOC"
+ },
+ {
+ "latitude": 0,
+ "longitude": 0,
+ "name": "dtrc2",
+ "shortName": "dtrc2",
+ "typeName": "EDGE_LOC"
+ },
+ {
+ "latitude": 0,
+ "longitude": 0,
+ "name": "dtrc3",
+ "shortName": "dtrc3",
+ "typeName": "EDGE_LOC"
}
],
"cdns": [
@@ -863,6 +884,134 @@
"checkPath": "",
"consistentHashQueryParams": [],
"deepCachingType": "NEVER",
+ "displayName": "ds-top-req-cap",
+ "dnsBypassCname": null,
+ "dnsBypassIp": "",
+ "dnsBypassIp6": "",
+ "dnsBypassTtl": 30,
+ "dscp": 40,
+ "edgeHeaderRewrite": null,
+ "fqPacingRate": 0,
+ "geoLimit": 0,
+ "geoLimitCountries": "",
+ "geoLimitRedirectURL": null,
+ "geoProvider": 0,
+ "globalMaxMbps": 0,
+ "globalMaxTps": 0,
+ "httpBypassFqdn": "",
+ "infoUrl": "TBD",
+ "initialDispersion": 1,
+ "ipv6RoutingEnabled": true,
+ "lastUpdated": "2018-04-06 16:48:51+00",
+ "logsEnabled": false,
+ "longDesc": "",
+ "longDesc1": "",
+ "longDesc2": "",
+ "matchList": [
+ {
+ "pattern": ".*\\.ds-top-req-cap\\..*",
+ "setNumber": 0,
+ "type": "HOST_REGEXP"
+ }
+ ],
+ "maxDnsAnswers": 0,
+ "midHeaderRewrite": null,
+ "missLat": 41.881944,
+ "missLong": -87.627778,
+ "multiSiteOrigin": false,
+ "orgServerFqdn": "http://example.org",
+ "originShield": null,
+ "profileDescription": null,
+ "profileName": null,
+ "protocol": 0,
+ "qstringIgnore": 0,
+ "rangeRequestHandling": 0,
+ "regexRemap": null,
+ "regionalGeoBlocking": false,
+ "remapText": null,
+ "routingName": "cdn",
+ "signed": false,
+ "signingAlgorithm": null,
+ "sslKeyVersion": 0,
+ "tenant": "tenant1",
+ "tenantName": "tenant1",
+ "topology": "top-for-ds-req",
+ "type": "HTTP",
+ "xmlId": "ds-top-req-cap",
+ "anonymousBlockingEnabled": false
+ },
+ {
+ "active": true,
+ "cdnName": "cdn1",
+ "cacheurl": "",
+ "ccrDnsTtl": 3600,
+ "checkPath": "",
+ "consistentHashQueryParams": [],
+ "deepCachingType": "NEVER",
+ "displayName": "ds-top-req-cap2",
+ "dnsBypassCname": null,
+ "dnsBypassIp": "",
+ "dnsBypassIp6": "",
+ "dnsBypassTtl": 30,
+ "dscp": 40,
+ "edgeHeaderRewrite": null,
+ "fqPacingRate": 0,
+ "geoLimit": 0,
+ "geoLimitCountries": "",
+ "geoLimitRedirectURL": null,
+ "geoProvider": 0,
+ "globalMaxMbps": 0,
+ "globalMaxTps": 0,
+ "httpBypassFqdn": "",
+ "infoUrl": "TBD",
+ "initialDispersion": 1,
+ "ipv6RoutingEnabled": true,
+ "lastUpdated": "2018-04-06 16:48:51+00",
+ "logsEnabled": false,
+ "longDesc": "",
+ "longDesc1": "",
+ "longDesc2": "",
+ "matchList": [
+ {
+ "pattern": ".*\\.ds-top-req-cap2\\..*",
+ "setNumber": 0,
+ "type": "HOST_REGEXP"
+ }
+ ],
+ "maxDnsAnswers": 0,
+ "midHeaderRewrite": null,
+ "missLat": 41.881944,
+ "missLong": -87.627778,
+ "multiSiteOrigin": false,
+ "orgServerFqdn": "http://example.org",
+ "originShield": null,
+ "profileDescription": null,
+ "profileName": null,
+ "protocol": 0,
+ "qstringIgnore": 0,
+ "rangeRequestHandling": 0,
+ "regexRemap": null,
+ "regionalGeoBlocking": false,
+ "remapText": null,
+ "routingName": "cdn",
+ "signed": false,
+ "signingAlgorithm": null,
+ "sslKeyVersion": 0,
+ "tenant": "tenant1",
+ "tenantName": "tenant1",
+ "topology": "top-for-ds-req2",
+ "type": "HTTP",
+ "xmlId": "ds-top-req-cap2",
+ "anonymousBlockingEnabled": false
+ },
+ {
+ "active": true,
+ "cdnName": "cdn1",
+ "cacheurl": "",
+ "ccrDnsTtl": 3600,
+ "checkPath": "",
+ "consistentHashQueryParams": [],
+ "deepCachingType": "NEVER",
"displayName": "ds-client-steering",
"dnsBypassCname": null,
"dnsBypassIp": "",
@@ -959,6 +1108,20 @@
"RequiredCapability": "bar"
}
],
+ "topologyBasedDeliveryServicesRequiredCapabilities": [
+ {
+ "xmlID": "ds-top-req-cap",
+ "RequiredCapability": "ram"
+ },
+ {
+ "xmlID": "ds-top-req-cap",
+ "RequiredCapability": "disk"
+ },
+ {
+ "xmlID": "ds-top-req-cap2",
+ "RequiredCapability": "ram"
+ }
+ ],
"divisions": [
{
"name": "division1"
@@ -2653,6 +2816,312 @@
"tcpPort": 80,
"type": "MID",
"updPending": false
+ },
+ {
+ "cachegroup": "dtrc1",
+ "cdnName": "cdn1",
+ "domainName": "kabletown.net",
+ "hostName": "dtrc-mid-01",
+ "httpsPort": 443,
+ "interfaces": [
+ {
+ "ipAddresses": [
+ {
+ "address": "2001:db8:dead:beef::2/64",
+ "gateway": "2001:db8:dead:beef::1",
+ "serviceAddress": false
+ },
+ {
+ "address": "192.0.2.2/24",
+ "gateway": "192.0.2.1",
+ "serviceAddress": true
+ }
+ ],
+ "monitor": true,
+ "mtu": 9000,
+ "name": "bond0"
+ }
+ ],
+ "physLocation": "Denver",
+ "profile": "MID1",
+ "rack": "RR 119.02",
+ "revalPending": false,
+ "status": "REPORTED",
+ "tcpPort": 80,
+ "type": "MID",
+ "updPending": false
+ },
+ {
+ "cachegroup": "dtrc1",
+ "cdnName": "cdn1",
+ "domainName": "kabletown.net",
+ "hostName": "dtrc-mid-02",
+ "httpsPort": 443,
+ "interfaces": [
+ {
+ "ipAddresses": [
+ {
+ "address": "2001:db8:dead:beef::3/64",
+ "gateway": "2001:db8:dead:beef::1",
+ "serviceAddress": false
+ },
+ {
+ "address": "192.0.2.3/24",
+ "gateway": "192.0.2.1",
+ "serviceAddress": true
+ }
+ ],
+ "monitor": true,
+ "mtu": 9000,
+ "name": "bond0"
+ }
+ ],
+ "physLocation": "Denver",
+ "profile": "MID1",
+ "rack": "RR 119.02",
+ "revalPending": false,
+ "status": "REPORTED",
+ "tcpPort": 80,
+ "type": "MID",
+ "updPending": false
+ },
+ {
+ "cachegroup": "dtrc1",
+ "cdnName": "cdn1",
+ "domainName": "kabletown.net",
+ "hostName": "dtrc-mid-03",
+ "httpsPort": 443,
+ "interfaces": [
+ {
+ "ipAddresses": [
+ {
+ "address": "2001:db8:dead:beef::4/64",
+ "gateway": "2001:db8:dead:beef::1",
+ "serviceAddress": false
+ },
+ {
+ "address": "192.0.2.4/24",
+ "gateway": "192.0.2.1",
+ "serviceAddress": true
+ }
+ ],
+ "monitor": true,
+ "mtu": 9000,
+ "name": "bond0"
+ }
+ ],
+ "physLocation": "Denver",
+ "profile": "MID1",
+ "rack": "RR 119.02",
+ "revalPending": false,
+ "status": "REPORTED",
+ "tcpPort": 80,
+ "type": "MID",
+ "updPending": false
+ },
+ {
+ "cachegroup": "dtrc2",
+ "cdnName": "cdn1",
+ "domainName": "kabletown.net",
+ "hostName": "dtrc-edge-01",
+ "httpsPort": 443,
+ "interfaces": [
+ {
+ "ipAddresses": [
+ {
+ "address": "2001:db8:dead:beef::5/64",
+ "gateway": "2001:db8:dead:beef::1",
+ "serviceAddress": false
+ },
+ {
+ "address": "192.0.2.5/24",
+ "gateway": "192.0.2.1",
+ "serviceAddress": true
+ }
+ ],
+ "monitor": true,
+ "mtu": 9000,
+ "name": "bond0"
+ }
+ ],
+ "physLocation": "Denver",
+ "profile": "EDGE1",
+ "rack": "RR 119.02",
+ "revalPending": false,
+ "status": "REPORTED",
+ "tcpPort": 80,
+ "type": "EDGE",
+ "updPending": false
+ },
+ {
+ "cachegroup": "dtrc2",
+ "cdnName": "cdn1",
+ "domainName": "kabletown.net",
+ "hostName": "dtrc-edge-02",
+ "httpsPort": 443,
+ "interfaces": [
+ {
+ "ipAddresses": [
+ {
+ "address": "2001:db8:dead:beef::6/64",
+ "gateway": "2001:db8:dead:beef::1",
+ "serviceAddress": false
+ },
+ {
+ "address": "192.0.2.6/24",
+ "gateway": "192.0.2.1",
+ "serviceAddress": true
+ }
+ ],
+ "monitor": true,
+ "mtu": 9000,
+ "name": "bond0"
+ }
+ ],
+ "physLocation": "Denver",
+ "profile": "EDGE1",
+ "rack": "RR 119.02",
+ "revalPending": false,
+ "status": "REPORTED",
+ "tcpPort": 80,
+ "type": "EDGE",
+ "updPending": false
+ },
+ {
+ "cachegroup": "dtrc2",
+ "cdnName": "cdn1",
+ "domainName": "kabletown.net",
+ "hostName": "dtrc-edge-03",
+ "httpsPort": 443,
+ "interfaces": [
+ {
+ "ipAddresses": [
+ {
+ "address": "2001:db8:dead:beef::7/64",
+ "gateway": "2001:db8:dead:beef::1",
+ "serviceAddress": false
+ },
+ {
+ "address": "192.0.2.7/24",
+ "gateway": "192.0.2.1",
+ "serviceAddress": true
+ }
+ ],
+ "monitor": true,
+ "mtu": 9000,
+ "name": "bond0"
+ }
+ ],
+ "physLocation": "Denver",
+ "profile": "EDGE1",
+ "rack": "RR 119.02",
+ "revalPending": false,
+ "status": "REPORTED",
+ "tcpPort": 80,
+ "type": "EDGE",
+ "updPending": false
+ },
+ {
+ "cachegroup": "dtrc3",
+ "cdnName": "cdn1",
+ "domainName": "kabletown.net",
+ "hostName": "dtrc-edge-04",
+ "httpsPort": 443,
+ "interfaces": [
+ {
+ "ipAddresses": [
+ {
+ "address": "2001:db8:dead:beef::8/64",
+ "gateway": "2001:db8:dead:beef::1",
+ "serviceAddress": false
+ },
+ {
+ "address": "192.0.2.8/24",
+ "gateway": "192.0.2.1",
+ "serviceAddress": true
+ }
+ ],
+ "monitor": true,
+ "mtu": 9000,
+ "name": "bond0"
+ }
+ ],
+ "physLocation": "Denver",
+ "profile": "EDGE1",
+ "rack": "RR 119.02",
+ "revalPending": false,
+ "status": "REPORTED",
+ "tcpPort": 80,
+ "type": "EDGE",
+ "updPending": false
+ },
+ {
+ "cachegroup": "dtrc3",
+ "cdnName": "cdn1",
+ "domainName": "kabletown.net",
+ "hostName": "dtrc-edge-05",
+ "httpsPort": 443,
+ "interfaces": [
+ {
+ "ipAddresses": [
+ {
+ "address": "2001:db8:dead:beef::9/64",
+ "gateway": "2001:db8:dead:beef::1",
+ "serviceAddress": false
+ },
+ {
+ "address": "192.0.2.9/24",
+ "gateway": "192.0.2.1",
+ "serviceAddress": true
+ }
+ ],
+ "monitor": true,
+ "mtu": 9000,
+ "name": "bond0"
+ }
+ ],
+ "physLocation": "Denver",
+ "profile": "EDGE1",
+ "rack": "RR 119.02",
+ "revalPending": false,
+ "status": "REPORTED",
+ "tcpPort": 80,
+ "type": "EDGE",
+ "updPending": false
+ },
+ {
+ "cachegroup": "dtrc3",
+ "cdnName": "cdn1",
+ "domainName": "kabletown.net",
+ "hostName": "dtrc-edge-06",
+ "httpsPort": 443,
+ "interfaces": [
+ {
+ "ipAddresses": [
+ {
+ "address": "2001:db8:dead:beef::10/64",
+ "gateway": "2001:db8:dead:beef::1",
+ "serviceAddress": false
+ },
+ {
+ "address": "192.0.2.10/24",
+ "gateway": "192.0.2.1",
+ "serviceAddress": true
+ }
+ ],
+ "monitor": true,
+ "mtu": 9000,
+ "name": "bond0"
+ }
+ ],
+ "physLocation": "Denver",
+ "profile": "EDGE1",
+ "rack": "RR 119.02",
+ "revalPending": false,
+ "status": "REPORTED",
+ "tcpPort": 80,
+ "type": "EDGE",
+ "updPending": false
}
],
"serverCapabilities": [
@@ -2661,6 +3130,12 @@
},
{
"name": "bar"
+ },
+ {
+ "name": "ram"
+ },
+ {
+ "name": "disk"
}
],
"serverServerCapabilities": [
@@ -2671,6 +3146,54 @@
{
"serverHostName": "atlanta-org-2",
"serverCapability": "bar"
+ },
+ {
+ "serverHostName": "dtrc-mid-01",
+ "serverCapability": "ram"
+ },
+ {
+ "serverHostName": "dtrc-mid-01",
+ "serverCapability": "disk"
+ },
+ {
+ "serverHostName": "dtrc-mid-02",
+ "serverCapability": "ram"
+ },
+ {
+ "serverHostName": "dtrc-mid-02",
+ "serverCapability": "disk"
+ },
+ {
+ "serverHostName": "dtrc-edge-01",
+ "serverCapability": "ram"
+ },
+ {
+ "serverHostName": "dtrc-edge-01",
+ "serverCapability": "disk"
+ },
+ {
+ "serverHostName": "dtrc-edge-02",
+ "serverCapability": "ram"
+ },
+ {
+ "serverHostName": "dtrc-edge-02",
+ "serverCapability": "disk"
+ },
+ {
+ "serverHostName": "dtrc-edge-04",
+ "serverCapability": "ram"
+ },
+ {
+ "serverHostName": "dtrc-edge-04",
+ "serverCapability": "disk"
+ },
+ {
+ "serverHostName": "dtrc-edge-05",
+ "serverCapability": "ram"
+ },
+ {
+ "serverHostName": "dtrc-edge-05",
+ "serverCapability": "disk"
}
],
"serviceCategories": [
@@ -2940,6 +3463,38 @@
"parents": []
}
]
+ },
+ {
+ "name": "top-for-ds-req",
+ "description": "a topology",
+ "nodes": [
+ {
+ "cachegroup": "dtrc1",
+ "parents": []
+ },
+ {
+ "cachegroup": "dtrc2",
+ "parents": [0]
+ },
+ {
+ "cachegroup": "dtrc3",
+ "parents": [0]
+ }
+ ]
+ },
+ {
+ "name": "top-for-ds-req2",
+ "description": "a topology",
+ "nodes": [
+ {
+ "cachegroup": "dtrc1",
+ "parents": []
+ },
+ {
+ "cachegroup": "dtrc2",
+ "parents": [0]
+ }
+ ]
}
],
"types": [
diff --git a/traffic_ops/testing/api/v3/traffic_control_test.go
b/traffic_ops/testing/api/v3/traffic_control_test.go
index 478ad01..9b858a9 100644
--- a/traffic_ops/testing/api/v3/traffic_control_test.go
+++ b/traffic_ops/testing/api/v3/traffic_control_test.go
@@ -21,40 +21,41 @@ import (
// TrafficControl - maps to the tc-fixtures.json file
type TrafficControl struct {
- ASNs []tc.ASN
`json:"asns"`
- CDNs []tc.CDN
`json:"cdns"`
- CacheGroups []tc.CacheGroupNullable
`json:"cachegroups"`
- CacheGroupParameterRequests []tc.CacheGroupParameterRequest
`json:"cachegroupParameters"`
- Capabilities []tc.Capability
`json:"capability"`
- Coordinates []tc.Coordinate
`json:"coordinates"`
- DeliveryServicesRegexes []tc.DeliveryServiceRegexesTest
`json:"deliveryServicesRegexes"`
- DeliveryServiceRequests []tc.DeliveryServiceRequest
`json:"deliveryServiceRequests"`
- DeliveryServiceRequestComments []tc.DeliveryServiceRequestComment
`json:"deliveryServiceRequestComments"`
- DeliveryServices []tc.DeliveryServiceNullableV30
`json:"deliveryservices"`
- DeliveryServicesRequiredCapabilities
[]tc.DeliveryServicesRequiredCapability
`json:"deliveryservicesRequiredCapabilities"`
- Divisions []tc.Division
`json:"divisions"`
- Federations []tc.CDNFederation
`json:"federations"`
- FederationResolvers []tc.FederationResolver
`json:"federation_resolvers"`
- Origins []tc.Origin
`json:"origins"`
- Profiles []tc.Profile
`json:"profiles"`
- Parameters []tc.Parameter
`json:"parameters"`
- ProfileParameters []tc.ProfileParameter
`json:"profileParameters"`
- PhysLocations []tc.PhysLocation
`json:"physLocations"`
- Regions []tc.Region
`json:"regions"`
- Roles []tc.Role
`json:"roles"`
- Servers []tc.ServerNullable
`json:"servers"`
- ServerServerCapabilities []tc.ServerServerCapability
`json:"serverServerCapabilities"`
- ServerCapabilities []tc.ServerCapability
`json:"serverCapabilities"`
- ServiceCategories []tc.ServiceCategory
`json:"serviceCategories"`
- Statuses []tc.StatusNullable
`json:"statuses"`
- StaticDNSEntries []tc.StaticDNSEntry
`json:"staticdnsentries"`
- StatsSummaries []tc.StatsSummary
`json:"statsSummaries"`
- Tenants []tc.Tenant
`json:"tenants"`
- ServerCheckExtensions []tc.ServerCheckExtensionNullable
`json:"servercheck_extensions"`
- Topologies []tc.Topology
`json:"topologies"`
- Types []tc.Type
`json:"types"`
- SteeringTargets []tc.SteeringTargetNullable
`json:"steeringTargets"`
- Serverchecks []tc.ServercheckRequestNullable
`json:"serverchecks"`
- Users []tc.User
`json:"users"`
- InvalidationJobs []tc.InvalidationJobInput
`json:"invalidationJobs"`
+ ASNs []tc.ASN
`json:"asns"`
+ CDNs []tc.CDN
`json:"cdns"`
+ CacheGroups
[]tc.CacheGroupNullable `json:"cachegroups"`
+ CacheGroupParameterRequests
[]tc.CacheGroupParameterRequest `json:"cachegroupParameters"`
+ Capabilities []tc.Capability
`json:"capability"`
+ Coordinates []tc.Coordinate
`json:"coordinates"`
+ DeliveryServicesRegexes
[]tc.DeliveryServiceRegexesTest `json:"deliveryServicesRegexes"`
+ DeliveryServiceRequests
[]tc.DeliveryServiceRequest `json:"deliveryServiceRequests"`
+ DeliveryServiceRequestComments
[]tc.DeliveryServiceRequestComment `json:"deliveryServiceRequestComments"`
+ DeliveryServices
[]tc.DeliveryServiceNullableV30 `json:"deliveryservices"`
+ DeliveryServicesRequiredCapabilities
[]tc.DeliveryServicesRequiredCapability
`json:"deliveryservicesRequiredCapabilities"`
+ TopologyBasedDeliveryServicesRequiredCapabilities
[]tc.DeliveryServicesRequiredCapability
`json:"topologyBasedDeliveryServicesRequiredCapabilities"`
+ Divisions []tc.Division
`json:"divisions"`
+ Federations []tc.CDNFederation
`json:"federations"`
+ FederationResolvers
[]tc.FederationResolver `json:"federation_resolvers"`
+ Origins []tc.Origin
`json:"origins"`
+ Profiles []tc.Profile
`json:"profiles"`
+ Parameters []tc.Parameter
`json:"parameters"`
+ ProfileParameters []tc.ProfileParameter
`json:"profileParameters"`
+ PhysLocations []tc.PhysLocation
`json:"physLocations"`
+ Regions []tc.Region
`json:"regions"`
+ Roles []tc.Role
`json:"roles"`
+ Servers []tc.ServerNullable
`json:"servers"`
+ ServerServerCapabilities
[]tc.ServerServerCapability `json:"serverServerCapabilities"`
+ ServerCapabilities []tc.ServerCapability
`json:"serverCapabilities"`
+ ServiceCategories []tc.ServiceCategory
`json:"serviceCategories"`
+ Statuses []tc.StatusNullable
`json:"statuses"`
+ StaticDNSEntries []tc.StaticDNSEntry
`json:"staticdnsentries"`
+ StatsSummaries []tc.StatsSummary
`json:"statsSummaries"`
+ Tenants []tc.Tenant
`json:"tenants"`
+ ServerCheckExtensions
[]tc.ServerCheckExtensionNullable `json:"servercheck_extensions"`
+ Topologies []tc.Topology
`json:"topologies"`
+ Types []tc.Type
`json:"types"`
+ SteeringTargets
[]tc.SteeringTargetNullable `json:"steeringTargets"`
+ Serverchecks
[]tc.ServercheckRequestNullable `json:"serverchecks"`
+ Users []tc.User
`json:"users"`
+ InvalidationJobs
[]tc.InvalidationJobInput `json:"invalidationJobs"`
}
diff --git a/traffic_ops/testing/api/v3/withobjs_test.go
b/traffic_ops/testing/api/v3/withobjs_test.go
index 27c8917..49fb511 100644
--- a/traffic_ops/testing/api/v3/withobjs_test.go
+++ b/traffic_ops/testing/api/v3/withobjs_test.go
@@ -60,6 +60,7 @@ const (
ServerCapabilities
ServerChecks
ServerServerCapabilities
+ ServerServerCapabilitiesForTopologies
Servers
ServiceCategories
Statuses
@@ -68,6 +69,7 @@ const (
Tenants
ServerCheckExtensions
Topologies
+ TopologyBasedDeliveryServiceRequiredCapabilities
Types
Users
)
@@ -78,38 +80,40 @@ type TCObjFuncs struct {
}
var withFuncs = map[TCObj]TCObjFuncs{
- CacheGroups: {CreateTestCacheGroups,
DeleteTestCacheGroups},
- CacheGroupsDeliveryServices:
{CreateTestCachegroupsDeliveryServices, DeleteTestCachegroupsDeliveryServices},
- CacheGroupParameters: {CreateTestCacheGroupParameters,
DeleteTestCacheGroupParameters},
- CDNs: {CreateTestCDNs, DeleteTestCDNs},
- CDNFederations: {CreateTestCDNFederations,
DeleteTestCDNFederations},
- Coordinates: {CreateTestCoordinates,
DeleteTestCoordinates},
- DeliveryServices: {CreateTestDeliveryServices,
DeleteTestDeliveryServices},
- DeliveryServicesRegexes:
{CreateTestDeliveryServicesRegexes, DeleteTestDeliveryServicesRegexes},
- DeliveryServiceRequests:
{CreateTestDeliveryServiceRequests, DeleteTestDeliveryServiceRequests},
- DeliveryServiceRequestComments:
{CreateTestDeliveryServiceRequestComments,
DeleteTestDeliveryServiceRequestComments},
- DeliveryServicesRequiredCapabilities:
{CreateTestDeliveryServicesRequiredCapabilities,
DeleteTestDeliveryServicesRequiredCapabilities},
- Divisions: {CreateTestDivisions,
DeleteTestDivisions},
- FederationUsers: {CreateTestFederationUsers,
DeleteTestFederationUsers},
- FederationResolvers: {CreateTestFederationResolvers,
DeleteTestFederationResolvers},
- Origins: {CreateTestOrigins,
DeleteTestOrigins},
- Parameters: {CreateTestParameters,
DeleteTestParameters},
- PhysLocations: {CreateTestPhysLocations,
DeleteTestPhysLocations},
- Profiles: {CreateTestProfiles,
DeleteTestProfiles},
- ProfileParameters: {CreateTestProfileParameters,
DeleteTestProfileParameters},
- Regions: {CreateTestRegions,
DeleteTestRegions},
- Roles: {CreateTestRoles,
DeleteTestRoles},
- ServerCapabilities: {CreateTestServerCapabilities,
DeleteTestServerCapabilities},
- ServerChecks: {CreateTestServerChecks,
DeleteTestServerChecks},
- ServerServerCapabilities:
{CreateTestServerServerCapabilities, DeleteTestServerServerCapabilities},
- Servers: {CreateTestServers,
DeleteTestServers},
- ServiceCategories: {CreateTestServiceCategories,
DeleteTestServiceCategories},
- Statuses: {CreateTestStatuses,
DeleteTestStatuses},
- StaticDNSEntries: {CreateTestStaticDNSEntries,
DeleteTestStaticDNSEntries},
- SteeringTargets: {SetupSteeringTargets,
DeleteTestSteeringTargets},
- Tenants: {CreateTestTenants,
DeleteTestTenants},
- ServerCheckExtensions: {CreateTestServerCheckExtensions,
DeleteTestServerCheckExtensions},
- Topologies: {CreateTestTopologies,
DeleteTestTopologies},
- Types: {CreateTestTypes,
DeleteTestTypes},
- Users: {CreateTestUsers,
ForceDeleteTestUsers},
+ CacheGroups: {CreateTestCacheGroups,
DeleteTestCacheGroups},
+ CacheGroupsDeliveryServices:
{CreateTestCachegroupsDeliveryServices, DeleteTestCachegroupsDeliveryServices},
+ CacheGroupParameters: {CreateTestCacheGroupParameters,
DeleteTestCacheGroupParameters},
+ CDNs: {CreateTestCDNs, DeleteTestCDNs},
+ CDNFederations: {CreateTestCDNFederations,
DeleteTestCDNFederations},
+ Coordinates: {CreateTestCoordinates,
DeleteTestCoordinates},
+ DeliveryServices: {CreateTestDeliveryServices,
DeleteTestDeliveryServices},
+ DeliveryServicesRegexes:
{CreateTestDeliveryServicesRegexes, DeleteTestDeliveryServicesRegexes},
+ DeliveryServiceRequests:
{CreateTestDeliveryServiceRequests, DeleteTestDeliveryServiceRequests},
+ DeliveryServiceRequestComments:
{CreateTestDeliveryServiceRequestComments,
DeleteTestDeliveryServiceRequestComments},
+ DeliveryServicesRequiredCapabilities:
{CreateTestDeliveryServicesRequiredCapabilities,
DeleteTestDeliveryServicesRequiredCapabilities},
+ Divisions: {CreateTestDivisions,
DeleteTestDivisions},
+ FederationUsers: {CreateTestFederationUsers,
DeleteTestFederationUsers},
+ FederationResolvers: {CreateTestFederationResolvers,
DeleteTestFederationResolvers},
+ Origins: {CreateTestOrigins,
DeleteTestOrigins},
+ Parameters: {CreateTestParameters,
DeleteTestParameters},
+ PhysLocations: {CreateTestPhysLocations,
DeleteTestPhysLocations},
+ Profiles: {CreateTestProfiles,
DeleteTestProfiles},
+ ProfileParameters: {CreateTestProfileParameters,
DeleteTestProfileParameters},
+ Regions: {CreateTestRegions,
DeleteTestRegions},
+ Roles: {CreateTestRoles,
DeleteTestRoles},
+ ServerCapabilities: {CreateTestServerCapabilities,
DeleteTestServerCapabilities},
+ ServerChecks: {CreateTestServerChecks,
DeleteTestServerChecks},
+ ServerServerCapabilities:
{CreateTestServerServerCapabilities, DeleteTestServerServerCapabilities},
+ ServerServerCapabilitiesForTopologies:
{CreateTestServerServerCapabilities,
DeleteTestServerServerCapabilitiesForTopologies},
+ Servers: {CreateTestServers,
DeleteTestServers},
+ ServiceCategories: {CreateTestServiceCategories,
DeleteTestServiceCategories},
+ Statuses: {CreateTestStatuses,
DeleteTestStatuses},
+ StaticDNSEntries: {CreateTestStaticDNSEntries,
DeleteTestStaticDNSEntries},
+ SteeringTargets: {SetupSteeringTargets,
DeleteTestSteeringTargets},
+ Tenants: {CreateTestTenants,
DeleteTestTenants},
+ ServerCheckExtensions:
{CreateTestServerCheckExtensions, DeleteTestServerCheckExtensions},
+ Topologies: {CreateTestTopologies,
DeleteTestTopologies},
+ TopologyBasedDeliveryServiceRequiredCapabilities:
{CreateTestTopologyBasedDeliveryServicesRequiredCapabilities,
DeleteTestDeliveryServicesRequiredCapabilities},
+ Types: {CreateTestTypes, DeleteTestTypes},
+ Users: {CreateTestUsers, ForceDeleteTestUsers},
}
diff --git
a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices_required_capabilities.go
b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices_required_capabilities.go
index 44333eb..c08b281 100644
---
a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices_required_capabilities.go
+++
b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices_required_capabilities.go
@@ -23,19 +23,20 @@ import (
"database/sql"
"errors"
"fmt"
- "github.com/apache/trafficcontrol/lib/go-log"
-
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/util/ims"
"net/http"
"strconv"
"strings"
"time"
+ "github.com/apache/trafficcontrol/lib/go-log"
"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/lib/go-tc/tovalidate"
"github.com/apache/trafficcontrol/lib/go-util"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant"
+
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/util/ims"
+
validation "github.com/go-ozzo/ozzo-validation"
"github.com/lib/pq"
)
diff --git a/traffic_ops/traffic_ops_golang/server/servers_server_capability.go
b/traffic_ops/traffic_ops_golang/server/servers_server_capability.go
index c905d40..1ae6268 100644
--- a/traffic_ops/traffic_ops_golang/server/servers_server_capability.go
+++ b/traffic_ops/traffic_ops_golang/server/servers_server_capability.go
@@ -27,15 +27,16 @@ import (
"strings"
"time"
- "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant"
- "github.com/jmoiron/sqlx"
-
+ "github.com/apache/trafficcontrol/lib/go-log"
"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/lib/go-tc/tovalidate"
"github.com/apache/trafficcontrol/lib/go-util"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant"
+
validation "github.com/go-ozzo/ozzo-validation"
+ "github.com/jmoiron/sqlx"
"github.com/lib/pq"
)
@@ -52,8 +53,8 @@ type (
}
DSTenant struct {
- TenantID int64 `db:"tenant_id"`
- ID int64 `db:"id"`
+ TenantID int `db:"tenant_id"`
+ ID int `db:"id"`
}
)
@@ -137,22 +138,120 @@ JOIN server s ON sc.server = s.id ` + where + orderBy +
pagination +
}
func (ssc *TOServerServerCapability) Delete() (error, error, int) {
+ tenantIDs, err := tenant.GetUserTenantIDListTx(ssc.APIInfo().Tx.Tx,
ssc.APIInfo().User.TenantID)
+ if err != nil {
+ return nil, fmt.Errorf("deleting servers_server_capability:
%v", err), http.StatusInternalServerError
+ }
+ accessibleTenants := make(map[int]struct{}, len(tenantIDs))
+ for _, id := range tenantIDs {
+ accessibleTenants[id] = struct{}{}
+ }
+ userErr, sysErr, status :=
checkTopologyBasedDSRequiredCapabilities(ssc, accessibleTenants)
+ if userErr != nil || sysErr != nil {
+ return userErr, sysErr, status
+ }
+
+ userErr, sysErr, status = checkDSRequiredCapabilities(ssc,
accessibleTenants)
+ if userErr != nil || sysErr != nil {
+ return userErr, sysErr, status
+ }
+
+ return api.GenericDelete(ssc)
+}
+
+func checkTopologyBasedDSRequiredCapabilities(ssc *TOServerServerCapability,
accessibleTenants map[int]struct{}) (error, error, int) {
+ dsRows, err :=
ssc.APIInfo().Tx.Tx.Query(getTopologyBasedDSesReqCapQuery(), ssc.ServerID,
ssc.ServerCapability)
+ if err != nil {
+ return nil, fmt.Errorf("querying topology-based DSes with the
required capability %s: %v", *ssc.ServerCapability, err),
http.StatusInternalServerError
+ }
+ defer log.Close(dsRows, "closing dsRows in
checkTopologyBasedDSRequiredCapabilities")
+
+ xmlidToTopology := make(map[string]string)
+ xmlidToTenantID := make(map[string]int)
+ xmlidToReqCaps := make(map[string][]string)
+ for dsRows.Next() {
+ xmlID := ""
+ topology := ""
+ tenantID := 0
+ reqCaps := []string{}
+ if err := dsRows.Scan(&xmlID, &topology, &tenantID,
pq.Array(&reqCaps)); err != nil {
+ return nil, fmt.Errorf("scanning dsRows in
checkTopologyBasedDSRequiredCapabilities: %v", err),
http.StatusInternalServerError
+ }
+ xmlidToTenantID[xmlID] = tenantID
+ xmlidToTopology[xmlID] = topology
+ xmlidToReqCaps[xmlID] = reqCaps
+ }
+ if len(xmlidToTopology) == 0 {
+ return nil, nil, http.StatusOK
+ }
+
+ serverRows, err :=
ssc.APIInfo().Tx.Tx.Query(getServerCapabilitiesOfCachegoupQuery(),
ssc.ServerID, ssc.ServerCapability)
+ if err != nil {
+ return nil, fmt.Errorf("querying server capabilitites of server
%d's cachegroup: %v", *ssc.ServerID, err), http.StatusInternalServerError
+ }
+ defer log.Close(serverRows, "closing serverRows in
checkTopologyBasedDSRequiredCapabilities")
+
+ serverIDToCapabilities := make(map[int]map[string]struct{})
+ for serverRows.Next() {
+ serverID := 0
+ capabilities := []string{}
+ if err := serverRows.Scan(&serverID, pq.Array(&capabilities));
err != nil {
+ return nil, fmt.Errorf("scanning serverRows in
checkTopologyBasedDSRequiredCapabilities: %v", err),
http.StatusInternalServerError
+ }
+ serverIDToCapabilities[serverID] = make(map[string]struct{})
+ for _, c := range capabilities {
+ serverIDToCapabilities[serverID][c] = struct{}{}
+ }
+ }
+
+ unsatisfiedDSes := []string{}
+ for ds, dsReqCaps := range xmlidToReqCaps {
+ dsIsSatisfied := false
+ for _, serverCaps := range serverIDToCapabilities {
+ serverHasCapabilities := true
+ for _, dsReqCap := range dsReqCaps {
+ if _, ok := serverCaps[dsReqCap]; !ok {
+ serverHasCapabilities = false
+ break
+ }
+ }
+ if serverHasCapabilities {
+ dsIsSatisfied = true
+ break
+ }
+ }
+ if !dsIsSatisfied {
+ unsatisfiedDSes = append(unsatisfiedDSes, ds)
+ }
+ }
+ if len(unsatisfiedDSes) == 0 {
+ return nil, nil, http.StatusOK
+ }
+
+ dsStrings := make([]string, 0, len(unsatisfiedDSes))
+ for _, ds := range unsatisfiedDSes {
+ if _, ok := accessibleTenants[xmlidToTenantID[ds]]; ok {
+ dsStrings = append(dsStrings, "(xml_id = "+ds+",
topology = "+xmlidToTopology[ds]+")")
+ }
+ }
+ return fmt.Errorf("this capability is required by delivery services,
but there are no other servers in this server's cachegroup to satisfy them %s",
strings.Join(dsStrings, ", ")), nil, http.StatusBadRequest
+}
+
+func checkDSRequiredCapabilities(ssc *TOServerServerCapability,
accessibleTenants map[int]struct{}) (error, error, int) {
// Ensure that the user is not removing a server capability from the
server
// that is required by the delivery services the server is assigned to
(if applicable)
dsIDs := []int64{}
- if err := ssc.APIInfo().Tx.QueryRow(checkDSReqCapQuery(), ssc.ServerID,
ssc.ServerCapability).Scan(pq.Array(&dsIDs)); err != nil {
+ if err := ssc.APIInfo().Tx.Tx.QueryRow(checkDSReqCapQuery(),
ssc.ServerID, ssc.ServerCapability).Scan(pq.Array(&dsIDs)); err != nil {
return nil, fmt.Errorf("checking removing server server
capability would still suffice delivery service requried capabilites: %v",
err), http.StatusInternalServerError
}
if len(dsIDs) > 0 {
- return ssc.buildDSReqCapError(dsIDs)
+ return ssc.buildDSReqCapError(dsIDs, accessibleTenants)
}
-
- // Delete association
- return api.GenericDelete(ssc)
+ return nil, nil, http.StatusOK
}
-func (ssc *TOServerServerCapability) buildDSReqCapError(dsIDs []int64) (error,
error, int) {
+func (ssc *TOServerServerCapability) buildDSReqCapError(dsIDs []int64,
accessibleTenants map[int]struct{}) (error, error, int) {
dsTenantIDs, err := getDSTenantIDsByIDs(ssc.APIInfo().Tx, dsIDs)
if err != nil {
@@ -160,23 +259,14 @@ func (ssc *TOServerServerCapability)
buildDSReqCapError(dsIDs []int64) (error, e
}
authDSIDs := []string{}
- checkedTenants := map[int64]bool{}
for _, dsTenantID := range dsTenantIDs {
- if auth, ok := checkedTenants[dsTenantID.TenantID]; ok { // No
need to check tenant again
- if auth {
- authDSIDs = append(authDSIDs,
strconv.FormatInt(dsTenantID.ID, 10))
+ if _, ok := accessibleTenants[dsTenantID.TenantID]; ok {
+ if ok {
+ authDSIDs = append(authDSIDs,
strconv.Itoa(dsTenantID.ID))
}
continue
}
- authorized, err :=
tenant.IsResourceAuthorizedToUserTx(int(dsTenantID.TenantID),
ssc.APIInfo().User, ssc.APIInfo().Tx.Tx)
- if err != nil {
- return nil, fmt.Errorf("checking tenancy on delivery
service: %v", err), http.StatusInternalServerError
- }
- if authorized {
- authDSIDs = append(authDSIDs,
strconv.FormatInt(dsTenantID.ID, 10))
- }
- checkedTenants[dsTenantID.TenantID] = authorized
}
dsStr := "delivery services"
@@ -275,6 +365,42 @@ SELECT ARRAY(
AND dsrc.required_capability = $2)`
}
+// get the topology-based DSes (with all their required capabilities) that a
given
+// server is assigned to, filtered by the given capability
+func getTopologyBasedDSesReqCapQuery() string {
+ return `
+SELECT
+ ds.xml_id,
+ ds.topology,
+ ds.tenant_id,
+ ARRAY_AGG(dsrc.required_capability) AS req_caps
+FROM server s
+JOIN cachegroup c ON s.cachegroup = c.id
+JOIN topology_cachegroup tc ON c.name = tc.cachegroup
+JOIN deliveryservice ds ON ds.topology = tc.topology
+JOIN deliveryservices_required_capability dsrc ON dsrc.deliveryservice_id =
ds.id
+WHERE s.id = $1
+GROUP BY ds.xml_id, ds.tenant_id, ds.topology
+HAVING $2 = ANY(ARRAY_AGG(dsrc.required_capability))
+`
+}
+
+// get all the capabilities of the servers in a given server's cachegroup
+// that have a given capability
+func getServerCapabilitiesOfCachegoupQuery() string {
+ return `
+SELECT s.id, ARRAY_AGG(ssc.server_capability) AS capabilities
+FROM server s
+JOIN cachegroup c ON c.id = s.cachegroup AND c.id = (SELECT cachegroup FROM
server WHERE server.id = $1)
+JOIN server_server_capability ssc ON ssc.server = s.id
+WHERE
+ s.cdn_id = (SELECT cdn_id FROM server WHERE server.id = $1)
+ AND s.id != $1
+GROUP BY s.id
+HAVING $2 = ANY(ARRAY_AGG(ssc.server_capability));
+`
+}
+
func getDSTenantIDsByIDs(tx *sqlx.Tx, dsIDs []int64) ([]DSTenant, error) {
dsTenantIDs := []DSTenant{}
@@ -287,6 +413,7 @@ func getDSTenantIDsByIDs(tx *sqlx.Tx, dsIDs []int64)
([]DSTenant, error) {
if err != nil {
return nil, fmt.Errorf("querying tenant IDs for delivery
service IDs: %v", err)
}
+ defer log.Close(resultRows, "closing resultRows in getDSTenantIDsByIDs")
for resultRows.Next() {
dsTenantID := DSTenant{}
diff --git a/traffic_ops/traffic_ops_golang/tenant/tenancy.go
b/traffic_ops/traffic_ops_golang/tenant/tenancy.go
index 15c6843..c40643c 100644
--- a/traffic_ops/traffic_ops_golang/tenant/tenancy.go
+++ b/traffic_ops/traffic_ops_golang/tenant/tenancy.go
@@ -58,18 +58,6 @@ func GetDeliveryServiceTenantInfo(xmlID string, tx *sql.Tx)
(*DeliveryServiceTen
return &ds, nil
}
-func GetDeliveryServiceTenantInfoID(tx *sql.Tx, dsID int)
(*DeliveryServiceTenantInfo, error) {
- ds := DeliveryServiceTenantInfo{}
- ds.ID = util.IntPtr(dsID)
- if err := tx.QueryRow(`SELECT tenant_id FROM deliveryservice where id =
$1`, &ds.ID).Scan(&ds.TenantID); err != nil {
- if err == sql.ErrNoRows {
- return &ds, errors.New("a deliveryservice with id '" +
strconv.Itoa(dsID) + "' was not found")
- }
- return nil, errors.New("querying tenant id from delivery
service: " + err.Error())
- }
- return &ds, nil
-}
-
// Check checks that the given user has access to the given XMLID. Returns a
user error, system error,
// and the HTTP status code to be returned to the user if an error occurred.
On success, the user error
// and system error will both be nil, and the error code should be ignored.