You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by oc...@apache.org on 2020/11/11 17:29:09 UTC

[trafficcontrol] branch 5.0.x updated (4b80904 -> e419784)

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

ocket8888 pushed a change to branch 5.0.x
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git.


    from 4b80904  Validate the assignment of ORG servers to topology-based DSes (#5260) (#5271)
     new 6e10068  Dnssec refresh (#5122)
     new 0c53b32  Dcz to crs stats (#5250)
     new e419784  Exclude ORG_LOC cachegroups from topology-based DS req. cap. validation (#5270)

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 CHANGELOG.md                                       |   1 +
 .../traffic_router/traffic_router_api.rst          |   3 +-
 .../bin/checks/DnssecRefresh/ToDnssecRefresh.go    |  81 +++++++++++
 .../app/bin/checks/DnssecRefresh/config/config.go  | 157 +++++++++++++++++++++
 traffic_ops/testing/api/v3/tc-fixtures.json        |   8 +-
 .../deliveryservices_required_capabilities.go      |   2 +-
 .../traffic_ops_golang/topology/topologies.go      |   2 +-
 .../core/edge/PropertiesAndCaches.java             |  60 ++++++++
 .../traffic_router/core/router/TrafficRouter.java  |  21 +++
 .../traffic_router/core/util/DataExporter.java     |  10 +-
 10 files changed, 339 insertions(+), 6 deletions(-)
 create mode 100644 traffic_ops/app/bin/checks/DnssecRefresh/ToDnssecRefresh.go
 create mode 100644 traffic_ops/app/bin/checks/DnssecRefresh/config/config.go
 create mode 100644 traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/edge/PropertiesAndCaches.java


[trafficcontrol] 02/03: Dcz to crs stats (#5250)

Posted by oc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ocket8888 pushed a commit to branch 5.0.x
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit 0c53b32f07192d488b054cf93b44df854f4e9ea1
Author: mattjackson220 <33...@users.noreply.github.com>
AuthorDate: Mon Nov 9 16:45:04 2020 -0700

    Dcz to crs stats (#5250)
    
    * updated /crs/stats/ip/{ip} to include deep coverage zone
    
    * updated /crs/stats/ip/{ip} to include deep coverage zone
    
    * added license and docs
    
    * fixed PMD failures
    
    (cherry picked from commit 4f539e71a8eb3379f3d99c19ff19338a92eecfe6)
---
 CHANGELOG.md                                       |  1 +
 .../traffic_router/traffic_router_api.rst          |  3 +-
 .../core/edge/PropertiesAndCaches.java             | 60 ++++++++++++++++++++++
 .../traffic_router/core/router/TrafficRouter.java  | 21 ++++++++
 .../traffic_router/core/util/DataExporter.java     | 10 +++-
 5 files changed, 93 insertions(+), 2 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c883cff..c0755f3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -66,6 +66,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
 - Added User-Agent string to Traffic Router log output.
 - Added default sort logic to GET API calls using Read()
 - Traffic Ops: added validation for assigning ORG servers to topology-based delivery services
+- Added locationByDeepCoverageZone to the `crs/stats/ip/{ip}` endpoint in the Traffic Router API
 
 ### Fixed
 - Fixed #5188 - DSR (delivery service request) incorrectly marked as complete and error message not displaying when DSR fulfilled and DS update fails in Traffic Portal. [Related Github issue](https://github.com/apache/trafficcontrol/issues/5188)
diff --git a/docs/source/development/traffic_router/traffic_router_api.rst b/docs/source/development/traffic_router/traffic_router_api.rst
index 67b44f9..b6047d1 100644
--- a/docs/source/development/traffic_router/traffic_router_api.rst
+++ b/docs/source/development/traffic_router/traffic_router_api.rst
@@ -150,7 +150,8 @@ Response Structure
 	},
 	"locationByFederation": "not found",
 	"requestIp": "69.241.118.34",
-	"locationByCoverageZone": "not found"
+	"locationByCoverageZone": "not found",
+	"locationByDeepCoverageZone": "not found"
 	}
 
 .. _tr-api-crs-locations:
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/edge/PropertiesAndCaches.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/edge/PropertiesAndCaches.java
new file mode 100644
index 0000000..7823ebe
--- /dev/null
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/edge/PropertiesAndCaches.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.comcast.cdn.traffic_control.traffic_router.core.edge;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * An abbreviated version of CacheLocation to show only properties and a list of cache names
+ */
+public class PropertiesAndCaches {
+    final public Map<String, String> properties;
+    final public List<String> caches;
+
+    public PropertiesAndCaches(final CacheLocation cacheLocation) {
+        properties = cacheLocation.getProperties();
+        caches = new ArrayList<>();
+        for (final Cache cache : cacheLocation.getCaches()) {
+            caches.add(cache.getId());
+        }
+    }
+
+    /**
+     * Gets properties.
+     *
+     * @return the properties
+     */
+    public Map<String, String> getProperties() {
+        return properties;
+    }
+
+    /**
+     * Gets caches.
+     *
+     * @return the caches
+     */
+    public List<String> getCaches() {
+        return caches;
+    }
+
+}
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouter.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouter.java
index 67cb266..7a45d1f 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouter.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/router/TrafficRouter.java
@@ -1349,6 +1349,27 @@ public class TrafficRouter {
 		return getCoverageZoneCacheLocation(ip, deliveryServiceId, false, null, requestVersion); // default is not deep
 	}
 
+	/**
+	 * Finds the deep coverage zone location information for a give IP address.
+	 * @param ip
+	 * @return deep coverage zone location
+	 */
+	public CacheLocation getDeepCoverageZoneLocationByIP(final String ip) {
+		final NetworkNode networkNode = getDeepNetworkNode(ip);
+
+		if (networkNode == null) {
+			return null;
+		}
+
+		final CacheLocation cacheLocation = (CacheLocation) networkNode.getLocation();
+
+		if (cacheLocation != null) {
+			cacheLocation.loadDeepCaches(networkNode.getDeepCacheNames(), cacheRegister);
+		}
+
+		return cacheLocation;
+	}
+
 	@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"})
 	public CacheLocation getCoverageZoneCacheLocation(final String ip, final String deliveryServiceId, final boolean useDeep, final Track track, final IPVersions requestVersion) {
 		final NetworkNode networkNode = useDeep ? getDeepNetworkNode(ip) : getNetworkNode(ip);
diff --git a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/util/DataExporter.java b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/util/DataExporter.java
index 0b2bb3a..be64e44 100644
--- a/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/util/DataExporter.java
+++ b/traffic_router/core/src/main/java/com/comcast/cdn/traffic_control/traffic_router/core/util/DataExporter.java
@@ -35,6 +35,7 @@ import com.comcast.cdn.traffic_control.traffic_router.core.edge.CacheLocation;
 import com.comcast.cdn.traffic_control.traffic_router.core.edge.CacheRegister;
 import com.comcast.cdn.traffic_control.traffic_router.core.edge.InetRecord;
 import com.comcast.cdn.traffic_control.traffic_router.core.edge.Location;
+import com.comcast.cdn.traffic_control.traffic_router.core.edge.PropertiesAndCaches;
 import com.comcast.cdn.traffic_control.traffic_router.geolocation.Geolocation;
 import com.comcast.cdn.traffic_control.traffic_router.geolocation.GeolocationException;
 import com.comcast.cdn.traffic_control.traffic_router.core.loc.NetworkNode;
@@ -117,7 +118,7 @@ public class DataExporter {
 			final List<Object> federationsList = federationExporter.getMatchingFederations(cidrAddress);
 
 			if (federationsList.isEmpty()) {
-				map.put("locationByFederation", "not found");
+				map.put("locationByFederation", NOT_FOUND_MESSAGE);
 			} else {
 				map.put("locationByFederation", federationsList);
 			}
@@ -125,6 +126,13 @@ public class DataExporter {
 			map.put("locationByFederation", NOT_FOUND_MESSAGE);
 		}
 
+		final CacheLocation clFromDCZ = trafficRouterManager.getTrafficRouter().getDeepCoverageZoneLocationByIP(ip);
+		if (clFromDCZ != null) {
+			map.put("locationByDeepCoverageZone", new PropertiesAndCaches(clFromDCZ));
+		} else {
+			map.put("locationByDeepCoverageZone", NOT_FOUND_MESSAGE);
+		}
+
 		return map;
 	}
 


[trafficcontrol] 01/03: Dnssec refresh (#5122)

Posted by oc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ocket8888 pushed a commit to branch 5.0.x
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit 6e10068698bd6fccf421f2c839631d23307c2027
Author: jpappa200 <jo...@cable.comcast.com>
AuthorDate: Mon Nov 9 17:36:26 2020 -0500

    Dnssec refresh (#5122)
    
    * initial add of config.go for dnssec refresh
    
    * initial add of ToDnssecRefresh.go
    
    * removed extra import block
    
    * fix a couple typos
    
    * Fixed formatting issues.
    
    * Fixed formatting issues.
    
    * Fixed formatting issues.
    
    * Fixed more formatting issues.
    
    * Changed a couple variable names for readability.
    
    * Fixed change in variable name line 48.
    
    * Added check for non 200 response.
    
    * Removed blank line and log error instead of print.
    
    * Made requested changes. will document in PR
    
    * removed leftover debug statement in ErrCheck
    
    (cherry picked from commit c0a76571a26cc28dff1de81390720014c8f6d6e7)
---
 .../bin/checks/DnssecRefresh/ToDnssecRefresh.go    |  81 +++++++++++
 .../app/bin/checks/DnssecRefresh/config/config.go  | 157 +++++++++++++++++++++
 2 files changed, 238 insertions(+)

diff --git a/traffic_ops/app/bin/checks/DnssecRefresh/ToDnssecRefresh.go b/traffic_ops/app/bin/checks/DnssecRefresh/ToDnssecRefresh.go
new file mode 100644
index 0000000..7ed89e3
--- /dev/null
+++ b/traffic_ops/app/bin/checks/DnssecRefresh/ToDnssecRefresh.go
@@ -0,0 +1,81 @@
+package main
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"bytes"
+	"encoding/json"
+	"io/ioutil"
+	"net/http"
+	"net/http/cookiejar"
+	"os"
+	"time"
+
+	"github.com/apache/trafficcontrol/lib/go-log"
+	"github.com/apache/trafficcontrol/traffic_ops/app/bin/checks/DnssecRefresh/config"
+)
+
+func main() {
+	cfg, err := config.GetCfg()
+	config.ErrCheck(err)
+	//for the -h --help option
+	if cfg == (config.Cfg{}) {
+		os.Exit(0)
+	}
+	log.Debugln("Including DEBUG messages in output. Config is:")
+	config.PrintConfig(cfg) // only if DEBUG logging is set.
+	body := &config.Creds{
+		User:     cfg.TOUser,
+		Password: cfg.TOPass,
+	}
+	loginUrl := cfg.TOUrl + "/api/2.0/user/login"
+	buf := &bytes.Buffer{}
+	err = json.NewEncoder(buf).Encode(body)
+	config.ErrCheck(err)
+	req, err := http.NewRequest(http.MethodPost, loginUrl, buf)
+	config.ErrCheck(err)
+	jar, err := cookiejar.New(nil)
+	config.ErrCheck(err)
+	client := &http.Client{Jar: jar, Transport: cfg.Transport, Timeout: 5 * time.Second}
+
+	log.Debugf("Posting to: %s", loginUrl)
+
+	res, err := client.Do(req)
+	config.ErrCheck(err)
+	defer config.Dclose(res.Body)
+	refreshUrl := cfg.TOUrl + "/api/2.0/cdns/dnsseckeys/refresh"
+	resp, err := http.NewRequest(http.MethodGet, refreshUrl, buf)
+	config.ErrCheck(err)
+	log.Debugf("Get req to: %s", refreshUrl)
+
+	refresh, err := client.Do(resp)
+	config.ErrCheck(err)
+	respData, err := ioutil.ReadAll(refresh.Body)
+	config.ErrCheck(err)
+	defer config.Dclose(refresh.Body)
+
+	if refresh.StatusCode != 200 {
+		log.Errorln(string(respData))
+		os.Exit(1)
+	}
+	response := config.ToResponse{}
+	config.ErrCheck(json.Unmarshal(respData, &response))
+	log.Debugln(response.Response)
+}
diff --git a/traffic_ops/app/bin/checks/DnssecRefresh/config/config.go b/traffic_ops/app/bin/checks/DnssecRefresh/config/config.go
new file mode 100644
index 0000000..6c5039b
--- /dev/null
+++ b/traffic_ops/app/bin/checks/DnssecRefresh/config/config.go
@@ -0,0 +1,157 @@
+package config
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import (
+	"crypto/tls"
+	"errors"
+	"fmt"
+	"io"
+	"net/http"
+	"os"
+	"strings"
+
+	"github.com/apache/trafficcontrol/lib/go-log"
+	"github.com/pborman/getopt/v2"
+)
+
+type Creds struct {
+	User     string `json:"u"`
+	Password string `json:"p"`
+}
+
+type Cfg struct {
+	LogLocationErr   string
+	LogLocationInfo  string
+	LogLocationWarn  string
+	LogLocationDebug string
+	TOInsecure       bool
+	TOUser           string
+	TOPass           string
+	TOUrl            string
+	Transport        *http.Transport
+}
+
+type ToResponse struct {
+	Response string `json:"response"`
+}
+
+func Dclose(c io.Closer) {
+	if err := c.Close(); err != nil {
+		log.Errorln(err)
+	}
+}
+
+func ErrCheck(err error) {
+	if err != nil {
+		log.Errorln(err)
+		os.Exit(1)
+	}
+}
+
+func (cfg Cfg) ErrorLog() log.LogLocation   { return log.LogLocation(cfg.LogLocationErr) }
+func (cfg Cfg) WarningLog() log.LogLocation { return log.LogLocation(cfg.LogLocationWarn) }
+func (cfg Cfg) InfoLog() log.LogLocation    { return log.LogLocation(cfg.LogLocationInfo) }
+func (cfg Cfg) DebugLog() log.LogLocation   { return log.LogLocation(cfg.LogLocationDebug) }
+func (cfg Cfg) EventLog() log.LogLocation   { return log.LogLocation(log.LogLocationNull) } // event logging not used.
+
+func GetCfg() (Cfg, error) {
+	var err error
+	logLocationDebugPtr := getopt.StringLong("log-location-debug", 'd', "", "Where to log debugs. May be a file path, stdout, stderr, or null, default ''")
+	logLocationErrorPtr := getopt.StringLong("log-location-error", 'e', "stderr", "Where to log errors. May be a file path, stdout, stderr, or null, default stderr")
+	logLocationInfoPtr := getopt.StringLong("log-location-info", 'i', "stderr", "Where to log info. May be a file path, stdout, stderr, or null, default stderr")
+	logLocationWarnPtr := getopt.StringLong("log-location-warning", 'w', "stderr", "Where to log warnings. May be a file path, stdout, stderr, or null, default stderr")
+	toInsecurePtr := getopt.BoolLong("traffic-ops-insecure", 'I', "[true | false] ignore certificate errors from Traffic Ops")
+	toUserPtr := getopt.StringLong("traffic-ops-user", 'u', "", "Traffic Ops username. Required.")
+	toPassPtr := getopt.StringLong("traffic-ops-password", 'p', "", "Traffic Ops Password. Required")
+	toUrlPtr := getopt.StringLong("traffic-ops-url", 'U', "", "Traffic ops base URL. Required.")
+	helpPtr := getopt.BoolLong("help", 'h', "Print usage information and exit")
+	getopt.ParseV2()
+
+	logLocationDebug := *logLocationDebugPtr
+	logLocationError := *logLocationErrorPtr
+	logLocationInfo := *logLocationInfoPtr
+	logLocationWarn := *logLocationWarnPtr
+	toInsecure := *toInsecurePtr
+	toURL := *toUrlPtr
+	toUser := *toUserPtr
+	toPass := *toPassPtr
+	transport := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: toInsecure}}
+	help := *helpPtr
+
+	cfg := Cfg{
+		LogLocationDebug: logLocationDebug,
+		LogLocationErr:   logLocationError,
+		LogLocationInfo:  logLocationInfo,
+		LogLocationWarn:  logLocationWarn,
+		TOInsecure:       toInsecure,
+		Transport:        transport,
+		TOUrl:            toURL,
+		TOUser:           toUser,
+		TOPass:           toPass,
+	}
+
+	if help {
+		Usage()
+		return Cfg{}, nil
+	}
+	if err = log.InitCfg(cfg); err != nil {
+		return Cfg{}, errors.New("Initializing loggers: " + err.Error() + "\n")
+	}
+
+	missingArgStr := "Missing required argument"
+	usageStr := "\nBasic usage: ToDnssecRefresh --traffic-ops-url=myurl --traffic-ops-user=myuser --traffic-ops-password=mypass\n"
+	if strings.TrimSpace(toURL) == "" {
+		return Cfg{}, errors.New(missingArgStr + " --traffic-ops-url\n" + usageStr)
+	}
+	if strings.TrimSpace(toUser) == "" {
+		return Cfg{}, errors.New(missingArgStr + " --traffic-ops-user\n" + usageStr)
+	}
+	if strings.TrimSpace(toPass) == "" {
+		return Cfg{}, errors.New(missingArgStr + " --traffic-ops-password\n" + usageStr)
+	}
+
+	return cfg, nil
+}
+
+func PrintConfig(cfg Cfg) {
+	log.Debugf("TOUrl: %s\n", cfg.TOUrl)
+	log.Debugf("TOUser: %s\n", cfg.TOUser)
+	log.Debugf("TOPass: Pass len: %d\n", len(cfg.TOPass))
+	log.Debugf("TOInsecure: %t\n", cfg.TOInsecure)
+	log.Debugf("LogLocationDebug: %s\n", cfg.LogLocationDebug)
+	log.Debugf("LogLocationErr: %s\n", cfg.LogLocationErr)
+	log.Debugf("LogLocationInfo: %s\n", cfg.LogLocationInfo)
+	log.Debugf("LogLocationWarn: %s\n", cfg.LogLocationWarn)
+}
+
+func Usage() {
+	usageStr := `Usage: ToDnssecRefresh -u <url> -U <to_user> -p <to_password> [-d|-e|-w <log_location>] [-I]
+	--log-location-debug=[value] | -d [value], Where to log debugs. May be a file path, stdout, stderr, or null, default stderr
+	--log-location-error=[value] | -e [value], Where to log errors. May be a file path, stdout, stderr, or null, default stderr
+	--log-location-info=[value] | -i [value], Where to log info. May be a file path, stdout, stderr, or null, default stderr
+	--log-location-warning=[value] | -w [value], Where to log warnings. May be a file path, stdout, stderr, or null, default stderr
+	--traffic-ops-url=[url] | -u [url], Traffic Ops URL. Must be the full URL, including the scheme. Required.
+	--traffic-ops-insecure=[true|false] -I [true | false] Whether to ignore HTTPS certificate errors from Traffic Ops. It is HIGHLY RECOMMENDED to never use this in a production environment, but only for debugging, default = false
+	--traffic-ops-user=[username] | -U [username], Traffic Ops username. Required.
+	--traffic-ops-password=[password] | -P [password], Traffic Ops password. Required.
+	--help | -h, Print usage information and exit`
+	fmt.Println(usageStr)
+}


[trafficcontrol] 03/03: Exclude ORG_LOC cachegroups from topology-based DS req. cap. validation (#5270)

Posted by oc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ocket8888 pushed a commit to branch 5.0.x
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git

commit e41978481a3751d89baad9eba70b18e0e32f75cd
Author: Rawlin Peters <ra...@apache.org>
AuthorDate: Tue Nov 10 17:29:00 2020 -0700

    Exclude ORG_LOC cachegroups from topology-based DS req. cap. validation (#5270)
    
    Server capabilities do not apply to ORG servers (origins), so the
    validation needs to exclude them.
    
    (cherry picked from commit 3564c9654d4cd5f27f72d591e9197c4882d953a3)
---
 traffic_ops/testing/api/v3/tc-fixtures.json                       | 8 ++++++--
 .../deliveryservice/deliveryservices_required_capabilities.go     | 2 +-
 traffic_ops/traffic_ops_golang/topology/topologies.go             | 2 +-
 3 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/traffic_ops/testing/api/v3/tc-fixtures.json b/traffic_ops/testing/api/v3/tc-fixtures.json
index 688ab8b..30c811f 100644
--- a/traffic_ops/testing/api/v3/tc-fixtures.json
+++ b/traffic_ops/testing/api/v3/tc-fixtures.json
@@ -4218,12 +4218,16 @@
             "description": "a topology",
             "nodes": [
                 {
-                    "cachegroup": "dtrc1",
+                    "cachegroup": "multiOriginCachegroup",
                     "parents": []
                 },
                 {
-                    "cachegroup": "dtrc2",
+                    "cachegroup": "dtrc1",
                     "parents": [0]
+                },
+                {
+                    "cachegroup": "dtrc2",
+                    "parents": [1]
                 }
             ]
         }
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 2e75315..e45e071 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices_required_capabilities.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices_required_capabilities.go
@@ -343,7 +343,6 @@ func (rc *RequiredCapability) checkServerCap() (error, error, int) {
 // EnsureTopologyBasedRequiredCapabilities ensures that at least one server per cachegroup
 // in this delivery service's topology has this delivery service's required capabilities.
 func EnsureTopologyBasedRequiredCapabilities(tx *sql.Tx, dsID int, topology string, requiredCapabilities []string) (error, error, int) {
-	// language=sql
 	q := `
 SELECT
   s.id,
@@ -357,6 +356,7 @@ JOIN topology_cachegroup tc ON tc.cachegroup = c.name
 WHERE
   s.cdn_id = (SELECT cdn_id FROM deliveryservice WHERE id = $1)
   AND tc.topology = $2
+  AND c.type != (SELECT id FROM type WHERE name = '` + tc.CacheGroupOriginTypeName + `')
 GROUP BY s.id, s.cdn_id, c.name
 `
 	rows, err := tx.Query(q, dsID, topology)
diff --git a/traffic_ops/traffic_ops_golang/topology/topologies.go b/traffic_ops/traffic_ops_golang/topology/topologies.go
index 383a689..bf77b01 100644
--- a/traffic_ops/traffic_ops_golang/topology/topologies.go
+++ b/traffic_ops/traffic_ops_golang/topology/topologies.go
@@ -328,7 +328,6 @@ func (topology TOTopology) validateDSRequiredCapabilities() error {
 	for cdn := range cdnMap {
 		CDNs = append(CDNs, cdn)
 	}
-	// language=sql
 	q := `
 SELECT
   s.id,
@@ -341,6 +340,7 @@ JOIN cachegroup c ON c.id = s.cachegroup
 WHERE
   c.name = ANY($1)
   AND s.cdn_id = ANY($2)
+  AND c.type != (SELECT id FROM type WHERE name = '` + tc.CacheGroupOriginTypeName + `')
 GROUP BY s.id, s.cdn_id, c.name
 `
 	rows, err := tx.Query(q, pq.Array(cachegroups), pq.Array(CDNs))