You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by mi...@apache.org on 2018/07/23 15:23:57 UTC

[trafficcontrol] branch master updated: Fix TO Go cachegroups PUT/POST API endpoints

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

mitchell852 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 2c56bdc  Fix TO Go cachegroups PUT/POST API endpoints
2c56bdc is described below

commit 2c56bdcd7b7931e69ee8b253b03473afd7a632cd
Author: Rawlin Peters <ra...@comcast.com>
AuthorDate: Wed Jul 18 17:28:27 2018 -0600

    Fix TO Go cachegroups PUT/POST API endpoints
    
    The Go version of the cachegroup API was looking up parent cachegroup
    IDs using the parent names in the PUT/POST request. This is
    fundamentally different from the Perl version which only accepts the
    parent IDs and doesn't look them up by name.
    
    This commit also:
    - adds cachegroup client methods that use Nullable structs for
      input/output (to have something usable for the API tests - the
      incorrect implementation of the API was holding the tests up)
    - updates all API tests to use the new Nullable cg methods
    - fixes the cachegroup API test to verify the proper API behavior for
      PUT/POST
---
 lib/go-tc/v13/cachegroups.go                       |  10 ++
 traffic_ops/client/v13/cachegroup.go               | 104 ++++++++++++++++-
 traffic_ops/testing/api/v13/cachegroups_test.go    | 129 +++++++++++++--------
 traffic_ops/testing/api/v13/origins_test.go        |   4 +-
 traffic_ops/testing/api/v13/servers_test.go        |   4 +-
 .../testing/api/v13/staticdnsentries_test.go       |   4 +-
 traffic_ops/testing/api/v13/traffic_control.go     |   2 +-
 .../traffic_ops_golang/cachegroup/cachegroups.go   |  51 --------
 8 files changed, 198 insertions(+), 110 deletions(-)

diff --git a/lib/go-tc/v13/cachegroups.go b/lib/go-tc/v13/cachegroups.go
index d053db4..6eb7c0f 100644
--- a/lib/go-tc/v13/cachegroups.go
+++ b/lib/go-tc/v13/cachegroups.go
@@ -29,6 +29,16 @@ type CacheGroupsResponse struct {
 	Response []CacheGroup `json:"response"`
 }
 
+type CacheGroupsNullableResponse struct {
+	Response []CacheGroupNullable `json:"response"`
+}
+
+// CacheGroupDetailResponse is the JSON object returned for a single CacheGroup
+type CacheGroupDetailResponse struct {
+	Response CacheGroupNullable `json:"response"`
+	tc.Alerts
+}
+
 // CacheGroup contains information about a given Cachegroup in Traffic Ops.
 type CacheGroup struct {
 	ID                          int          `json:"id" db:"id"`
diff --git a/traffic_ops/client/v13/cachegroup.go b/traffic_ops/client/v13/cachegroup.go
index cffff42..b67de6e 100644
--- a/traffic_ops/client/v13/cachegroup.go
+++ b/traffic_ops/client/v13/cachegroup.go
@@ -30,6 +30,28 @@ const (
 )
 
 // Create a CacheGroup
+func (to *Session) CreateCacheGroupNullable(cachegroup v13.CacheGroupNullable) (*v13.CacheGroupDetailResponse, ReqInf, error) {
+
+	var remoteAddr net.Addr
+	reqBody, err := json.Marshal(cachegroup)
+	reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
+	if err != nil {
+		return nil, reqInf, err
+	}
+	resp, remoteAddr, err := to.request(http.MethodPost, API_v13_CacheGroups, reqBody)
+	if err != nil {
+		return nil, reqInf, err
+	}
+	defer resp.Body.Close()
+	var cachegroupResp v13.CacheGroupDetailResponse
+	if err = json.NewDecoder(resp.Body).Decode(&cachegroupResp); err != nil {
+		return nil, reqInf, err
+	}
+	return &cachegroupResp, reqInf, nil
+}
+
+// Create a CacheGroup
+// Deprecated: Use CreateCacheGroupNullable
 func (to *Session) CreateCacheGroup(cachegroup v13.CacheGroup) (tc.Alerts, ReqInf, error) {
 
 	var remoteAddr net.Addr
@@ -49,6 +71,29 @@ func (to *Session) CreateCacheGroup(cachegroup v13.CacheGroup) (tc.Alerts, ReqIn
 }
 
 // Update a CacheGroup by ID
+func (to *Session) UpdateCacheGroupNullableByID(id int, cachegroup v13.CacheGroupNullable) (*v13.CacheGroupDetailResponse, ReqInf, error) {
+
+	var remoteAddr net.Addr
+	reqBody, err := json.Marshal(cachegroup)
+	reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
+	if err != nil {
+		return nil, reqInf, err
+	}
+	route := fmt.Sprintf("%s/%d", API_v13_CacheGroups, id)
+	resp, remoteAddr, err := to.request(http.MethodPut, route, reqBody)
+	if err != nil {
+		return nil, reqInf, err
+	}
+	defer resp.Body.Close()
+	var cachegroupResp v13.CacheGroupDetailResponse
+	if err = json.NewDecoder(resp.Body).Decode(&cachegroupResp); err != nil {
+		return nil, reqInf, err
+	}
+	return &cachegroupResp, reqInf, nil
+}
+
+// Update a CacheGroup by ID
+// Deprecated: use UpdateCachegroupNullableByID
 func (to *Session) UpdateCacheGroupByID(id int, cachegroup v13.CacheGroup) (tc.Alerts, ReqInf, error) {
 
 	var remoteAddr net.Addr
@@ -69,6 +114,23 @@ func (to *Session) UpdateCacheGroupByID(id int, cachegroup v13.CacheGroup) (tc.A
 }
 
 // Returns a list of CacheGroups
+func (to *Session) GetCacheGroupsNullable() ([]v13.CacheGroupNullable, ReqInf, error) {
+	resp, remoteAddr, err := to.request(http.MethodGet, API_v13_CacheGroups, nil)
+	reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
+	if err != nil {
+		return nil, reqInf, err
+	}
+	defer resp.Body.Close()
+
+	var data v13.CacheGroupsNullableResponse
+	if err = json.NewDecoder(resp.Body).Decode(&data); err != nil {
+		return nil, reqInf, err
+	}
+	return data.Response, reqInf, nil
+}
+
+// Returns a list of CacheGroups
+// Deprecated: use GetCacheGroupsNullable
 func (to *Session) GetCacheGroups() ([]v13.CacheGroup, ReqInf, error) {
 	resp, remoteAddr, err := to.request(http.MethodGet, API_v13_CacheGroups, nil)
 	reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
@@ -83,6 +145,25 @@ func (to *Session) GetCacheGroups() ([]v13.CacheGroup, ReqInf, error) {
 }
 
 // GET a CacheGroup by the CacheGroup id
+func (to *Session) GetCacheGroupNullableByID(id int) ([]v13.CacheGroupNullable, ReqInf, error) {
+	route := fmt.Sprintf("%s/%d", API_v13_CacheGroups, id)
+	resp, remoteAddr, err := to.request(http.MethodGet, route, nil)
+	reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
+	if err != nil {
+		return nil, reqInf, err
+	}
+	defer resp.Body.Close()
+
+	var data v13.CacheGroupsNullableResponse
+	if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
+		return nil, reqInf, err
+	}
+
+	return data.Response, reqInf, nil
+}
+
+// GET a CacheGroup by the CacheGroup id
+// Deprecated: use GetCacheGroupNullableByID
 func (to *Session) GetCacheGroupByID(id int) ([]v13.CacheGroup, ReqInf, error) {
 	route := fmt.Sprintf("%s/%d", API_v13_CacheGroups, id)
 	resp, remoteAddr, err := to.request(http.MethodGet, route, nil)
@@ -101,6 +182,25 @@ func (to *Session) GetCacheGroupByID(id int) ([]v13.CacheGroup, ReqInf, error) {
 }
 
 // GET a CacheGroup by the CacheGroup name
+func (to *Session) GetCacheGroupNullableByName(name string) ([]v13.CacheGroupNullable, ReqInf, error) {
+	url := fmt.Sprintf("%s?name=%s", API_v13_CacheGroups, name)
+	resp, remoteAddr, err := to.request(http.MethodGet, url, nil)
+	reqInf := ReqInf{CacheHitStatus: CacheHitStatusMiss, RemoteAddr: remoteAddr}
+	if err != nil {
+		return nil, reqInf, err
+	}
+	defer resp.Body.Close()
+
+	var data v13.CacheGroupsNullableResponse
+	if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
+		return nil, reqInf, err
+	}
+
+	return data.Response, reqInf, nil
+}
+
+// GET a CacheGroup by the CacheGroup name
+// Deprecated: use GetCachegroupNullableByName
 func (to *Session) GetCacheGroupByName(name string) ([]v13.CacheGroup, ReqInf, error) {
 	url := fmt.Sprintf("%s?name=%s", API_v13_CacheGroups, name)
 	resp, remoteAddr, err := to.request(http.MethodGet, url, nil)
@@ -128,6 +228,8 @@ func (to *Session) DeleteCacheGroupByID(id int) (tc.Alerts, ReqInf, error) {
 	}
 	defer resp.Body.Close()
 	var alerts tc.Alerts
-	err = json.NewDecoder(resp.Body).Decode(&alerts)
+	if err = json.NewDecoder(resp.Body).Decode(&alerts); err != nil {
+		return tc.Alerts{}, reqInf, err
+	}
 	return alerts, reqInf, nil
 }
diff --git a/traffic_ops/testing/api/v13/cachegroups_test.go b/traffic_ops/testing/api/v13/cachegroups_test.go
index 6e92d81..9b6a0c1 100644
--- a/traffic_ops/testing/api/v13/cachegroups_test.go
+++ b/traffic_ops/testing/api/v13/cachegroups_test.go
@@ -20,7 +20,6 @@ import (
 	"testing"
 
 	"github.com/apache/trafficcontrol/lib/go-log"
-	tc "github.com/apache/trafficcontrol/lib/go-tc"
 	"github.com/apache/trafficcontrol/lib/go-tc/v13"
 	"github.com/apache/trafficcontrol/traffic_ops/testing/api/utils"
 )
@@ -29,10 +28,10 @@ func TestCacheGroups(t *testing.T) {
 	CreateTestTypes(t)
 	CreateTestCacheGroups(t)
 	GetTestCacheGroups(t)
+	CheckCacheGroupsAuthentication(t)
 	UpdateTestCacheGroups(t)
 	DeleteTestCacheGroups(t)
 	DeleteTestTypes(t)
-	TestCacheGroupsAuthentication(t)
 }
 
 func CreateTestCacheGroups(t *testing.T) {
@@ -40,16 +39,38 @@ func CreateTestCacheGroups(t *testing.T) {
 
 	for _, cg := range testData.CacheGroups {
 		// get the typeID
-		typeResp, _, err := TOSession.GetTypeByName(cg.Type)
+		typeResp, _, err := TOSession.GetTypeByName(*cg.Type)
 		if err != nil {
 			t.Error("could not lookup a typeID for this cachegroup")
 			failed = true
 		}
-		cg.TypeID = typeResp[0].ID
+		cg.TypeID = &typeResp[0].ID
 
-		_, _, err = TOSession.CreateCacheGroup(cg)
+		if cg.ParentName != nil && *cg.ParentName != "" {
+			// get parent cachegroup ID (must already be created)
+			resp, _, err := TOSession.GetCacheGroupNullableByName(*cg.ParentName)
+			if err != nil {
+				t.Errorf("cannot GET CacheGroup by name: %v - %v\n", err, resp)
+				failed = true
+			}
+			cg.ParentCachegroupID = resp[0].ID
+			cg.ParentName = nil // to guarantee that parent IDs aren't looked up by name
+		}
+
+		if cg.SecondaryParentName != nil && *cg.SecondaryParentName != "" {
+			// get secondary parent cachegroup ID (must already be created)
+			resp, _, err := TOSession.GetCacheGroupNullableByName(*cg.SecondaryParentName)
+			if err != nil {
+				t.Errorf("cannot GET CacheGroup by name: %v - %v\n", err, resp)
+				failed = true
+			}
+			cg.SecondaryParentCachegroupID = resp[0].ID
+			cg.SecondaryParentName = nil // to guarantee that parent IDs aren't looked up by name
+		}
+
+		_, _, err = TOSession.CreateCacheGroupNullable(cg)
 		if err != nil {
-			t.Errorf("could not CREATE cachegroups: %v\n", err)
+			t.Errorf("could not CREATE cachegroups: %v, request: %v\n", err, cg)
 			failed = true
 		}
 	}
@@ -61,7 +82,7 @@ func CreateTestCacheGroups(t *testing.T) {
 func GetTestCacheGroups(t *testing.T) {
 	failed := false
 	for _, cg := range testData.CacheGroups {
-		resp, _, err := TOSession.GetCacheGroupByName(cg.Name)
+		resp, _, err := TOSession.GetCacheGroupNullableByName(*cg.Name)
 		if err != nil {
 			t.Errorf("cannot GET CacheGroup by name: %v - %v\n", err, resp)
 			failed = true
@@ -75,65 +96,64 @@ func GetTestCacheGroups(t *testing.T) {
 func UpdateTestCacheGroups(t *testing.T) {
 	failed := false
 	firstCG := testData.CacheGroups[0]
-	resp, _, err := TOSession.GetCacheGroupByName(firstCG.Name)
+	resp, _, err := TOSession.GetCacheGroupNullableByName(*firstCG.Name)
 	if err != nil {
-		t.Errorf("cannot GET CACHEGROUP by name: %v - %v\n", firstCG.Name, err)
+		t.Errorf("cannot GET CACHEGROUP by name: %v - %v\n", *firstCG.Name, err)
 		failed = true
 	}
 	cg := resp[0]
 	expectedShortName := "blah"
-	cg.ShortName = expectedShortName
+	cg.ShortName = &expectedShortName
 
 	// fix the type id for test
-	typeResp, _, err := TOSession.GetTypeByID(cg.TypeID)
+	typeResp, _, err := TOSession.GetTypeByID(*cg.TypeID)
 	if err != nil {
 		t.Error("could not lookup a typeID for this cachegroup")
 		failed = true
 	}
-	cg.TypeID = typeResp[0].ID
+	cg.TypeID = &typeResp[0].ID
 
-	var alert tc.Alerts
-	alert, _, err = TOSession.UpdateCacheGroupByID(cg.ID, cg)
+	updResp, _, err := TOSession.UpdateCacheGroupNullableByID(*cg.ID, cg)
 	if err != nil {
-		t.Errorf("cannot UPDATE CacheGroup by id: %v - %v\n", err, alert)
+		t.Errorf("cannot UPDATE CacheGroup by id: %v - %v\n", err, updResp)
 		failed = true
 	}
 
 	// Retrieve the CacheGroup to check CacheGroup name got updated
-	resp, _, err = TOSession.GetCacheGroupByID(cg.ID)
+	resp, _, err = TOSession.GetCacheGroupNullableByID(*cg.ID)
 	if err != nil {
-		t.Errorf("cannot GET CacheGroup by name: '$%s', %v\n", firstCG.Name, err)
+		t.Errorf("cannot GET CacheGroup by name: '$%s', %v\n", *firstCG.Name, err)
 		failed = true
 	}
 	cg = resp[0]
-	if cg.ShortName != expectedShortName {
-		t.Errorf("results do not match actual: %s, expected: %s\n", cg.ShortName, expectedShortName)
+	if *cg.ShortName != expectedShortName {
+		t.Errorf("results do not match actual: %s, expected: %s\n", *cg.ShortName, expectedShortName)
 		failed = true
 	}
 
 	// test coordinate updates
 	expectedLat := 7.0
 	expectedLong := 8.0
-	cg.Latitude = expectedLat
-	cg.Longitude = expectedLong
-	alert, _, err = TOSession.UpdateCacheGroupByID(cg.ID, cg)
+	cg.Latitude = &expectedLat
+	cg.Longitude = &expectedLong
+	updResp, _, err = TOSession.UpdateCacheGroupNullableByID(*cg.ID, cg)
 	if err != nil {
-		t.Errorf("cannot UPDATE CacheGroup by id: %v - %v\n", err, alert)
+		t.Errorf("cannot UPDATE CacheGroup by id: %v - %v\n", err, updResp)
 		failed = true
 	}
 
-	resp, _, err = TOSession.GetCacheGroupByID(cg.ID)
+	resp, _, err = TOSession.GetCacheGroupNullableByID(*cg.ID)
 	if err != nil {
-		t.Errorf("cannot GET CacheGroup by id: '%d', %v\n", cg.ID, err)
+		t.Errorf("cannot GET CacheGroup by id: '%d', %v\n", *cg.ID, err)
 		failed = true
 	}
 	cg = resp[0]
-	if cg.Latitude != expectedLat {
-		t.Errorf("failed to update latitude (expected = %f, actual = %f)\n", expectedLat, cg.Latitude)
+	if *cg.Latitude != expectedLat {
+		t.Errorf("failed to update latitude (expected = %f, actual = %f)\n", expectedLat, *cg.Latitude)
 		failed = true
 	}
-	if cg.Longitude != expectedLong {
-		t.Errorf("failed to update longitude (expected = %f, actual = %f)\n", expectedLong, cg.Longitude)
+	if *cg.Longitude != expectedLong {
+		t.Errorf("failed to update longitude (expected = %f, actual = %f)\n", expectedLong, *cg.Longitude)
 		failed = true
 	}
 
@@ -144,37 +164,37 @@ func UpdateTestCacheGroups(t *testing.T) {
 
 func DeleteTestCacheGroups(t *testing.T) {
 	failed := false
-	var mids []v13.CacheGroup
+	var mids []v13.CacheGroupNullable
 
 	// delete the edge caches.
 	for _, cg := range testData.CacheGroups {
 		// Retrieve the CacheGroup by name so we can get the id for the Update
-		resp, _, err := TOSession.GetCacheGroupByName(cg.Name)
+		resp, _, err := TOSession.GetCacheGroupNullableByName(*cg.Name)
 		if err != nil {
-			t.Errorf("cannot GET CacheGroup by name: %v - %v\n", cg.Name, err)
+			t.Errorf("cannot GET CacheGroup by name: %v - %v\n", *cg.Name, err)
 			failed = true
 		}
 		// Mids are parents and need to be deleted only after the children
 		// cachegroups are deleted.
-		if cg.Type == "MID_LOC" {
+		if *cg.Type == "MID_LOC" {
 			mids = append(mids, cg)
 			continue
 		}
 		if len(resp) > 0 {
 			respCG := resp[0]
-			_, _, err := TOSession.DeleteCacheGroupByID(respCG.ID)
+			_, _, err := TOSession.DeleteCacheGroupByID(*respCG.ID)
 			if err != nil {
-				t.Errorf("cannot DELETE CacheGroup by name: '%s' %v\n", respCG.Name, err)
+				t.Errorf("cannot DELETE CacheGroup by name: '%s' %v\n", *respCG.Name, err)
 				failed = true
 			}
 			// Retrieve the CacheGroup to see if it got deleted
-			cgs, _, err := TOSession.GetCacheGroupByName(cg.Name)
+			cgs, _, err := TOSession.GetCacheGroupNullableByName(*cg.Name)
 			if err != nil {
-				t.Errorf("error deleting CacheGroup name: %s\n", err.Error())
+				t.Errorf("error deleting CacheGroup by name: %s\n", err.Error())
 				failed = true
 			}
 			if len(cgs) > 0 {
-				t.Errorf("expected CacheGroup name: %s to be deleted\n", cg.Name)
+				t.Errorf("expected CacheGroup name: %s to be deleted\n", *cg.Name)
 				failed = true
 			}
 		}
@@ -182,27 +202,27 @@ func DeleteTestCacheGroups(t *testing.T) {
 	// now delete the mid tier caches
 	for _, cg := range mids {
 		// Retrieve the CacheGroup by name so we can get the id for the Update
-		resp, _, err := TOSession.GetCacheGroupByName(cg.Name)
+		resp, _, err := TOSession.GetCacheGroupNullableByName(*cg.Name)
 		if err != nil {
-			t.Errorf("cannot GET CacheGroup by name: %v - %v\n", cg.Name, err)
+			t.Errorf("cannot GET CacheGroup by name: %v - %v\n", *cg.Name, err)
 			failed = true
 		}
 		if len(resp) > 0 {
 			respCG := resp[0]
-			_, _, err := TOSession.DeleteCacheGroupByID(respCG.ID)
+			_, _, err := TOSession.DeleteCacheGroupByID(*respCG.ID)
 			if err != nil {
-				t.Errorf("cannot DELETE CacheGroup by name: '%s' %v\n", respCG.Name, err)
+				t.Errorf("cannot DELETE CacheGroup by name: '%s' %v\n", *respCG.Name, err)
 				failed = true
 			}
 
 			// Retrieve the CacheGroup to see if it got deleted
-			cgs, _, err := TOSession.GetCacheGroupByName(cg.Name)
+			cgs, _, err := TOSession.GetCacheGroupNullableByName(*cg.Name)
 			if err != nil {
 				t.Errorf("error deleting CacheGroup name: %s\n", err.Error())
 				failed = true
 			}
 			if len(cgs) > 0 {
-				t.Errorf("expected CacheGroup name: %s to be deleted\n", cg.Name)
+				t.Errorf("expected CacheGroup name: %s to be deleted\n", *cg.Name)
 				failed = true
 			}
 		}
@@ -213,30 +233,37 @@ func DeleteTestCacheGroups(t *testing.T) {
 	}
 }
 
-func TestCacheGroupsAuthentication(t *testing.T) {
+func CheckCacheGroupsAuthentication(t *testing.T) {
 	failed := false
 	errFormat := "expected error from %s when unauthenticated"
 
 	cg := testData.CacheGroups[0]
 
+	resp, _, err := TOSession.GetCacheGroupNullableByName(*cg.Name)
+	if err != nil {
+		t.Errorf("cannot GET CacheGroup by name: %v - %v\n", *cg.Name, err)
+		failed = true
+	}
+	cg = resp[0]
+
 	errors := make([]utils.ErrorAndMessage, 0)
 
-	_, _, err := NoAuthTOSession.CreateCacheGroup(cg)
+	_, _, err = NoAuthTOSession.CreateCacheGroupNullable(cg)
 	errors = append(errors, utils.ErrorAndMessage{err, fmt.Sprintf(errFormat, "CreateCacheGroup")})
 
-	_, _, err = NoAuthTOSession.GetCacheGroups()
+	_, _, err = NoAuthTOSession.GetCacheGroupsNullable()
 	errors = append(errors, utils.ErrorAndMessage{err, fmt.Sprintf(errFormat, "GetCacheGroups")})
 
-	_, _, err = NoAuthTOSession.GetCacheGroupByName(cg.Name)
+	_, _, err = NoAuthTOSession.GetCacheGroupNullableByName(*cg.Name)
 	errors = append(errors, utils.ErrorAndMessage{err, fmt.Sprintf(errFormat, "GetCacheGroupByName")})
 
-	_, _, err = NoAuthTOSession.GetCacheGroupByID(cg.ID)
+	_, _, err = NoAuthTOSession.GetCacheGroupNullableByID(*cg.ID)
 	errors = append(errors, utils.ErrorAndMessage{err, fmt.Sprintf(errFormat, "GetCacheGroupByID")})
 
-	_, _, err = NoAuthTOSession.UpdateCacheGroupByID(cg.ID, cg)
+	_, _, err = NoAuthTOSession.UpdateCacheGroupNullableByID(*cg.ID, cg)
 	errors = append(errors, utils.ErrorAndMessage{err, fmt.Sprintf(errFormat, "UpdateCacheGroupByID")})
 
-	_, _, err = NoAuthTOSession.DeleteCacheGroupByID(cg.ID)
+	_, _, err = NoAuthTOSession.DeleteCacheGroupByID(*cg.ID)
 	errors = append(errors, utils.ErrorAndMessage{err, fmt.Sprintf(errFormat, "DeleteCacheGroupByID")})
 
 	for _, err := range errors {
diff --git a/traffic_ops/testing/api/v13/origins_test.go b/traffic_ops/testing/api/v13/origins_test.go
index 77181df..b706db8 100644
--- a/traffic_ops/testing/api/v13/origins_test.go
+++ b/traffic_ops/testing/api/v13/origins_test.go
@@ -64,7 +64,7 @@ func CreateTestOrigins(t *testing.T) {
 	respProfile := respProfiles[0]
 
 	// GET originCachegroup cachegroup
-	respCacheGroups, _, err := TOSession.GetCacheGroupByName("originCachegroup")
+	respCacheGroups, _, err := TOSession.GetCacheGroupNullableByName("originCachegroup")
 	if err != nil {
 		t.Errorf("cannot GET CacheGroup by name: originCachegroup - %v\n", err)
 		failed = true
@@ -92,7 +92,7 @@ func CreateTestOrigins(t *testing.T) {
 
 	// loop through origins, assign FKs and create
 	for _, origin := range testData.Origins {
-		origin.CachegroupID = &respCacheGroup.ID
+		origin.CachegroupID = respCacheGroup.ID
 		origin.CoordinateID = &respCoordinate.ID
 		origin.ProfileID = &respProfile.ID
 		origin.DeliveryServiceID = &respDeliveryServices[0].ID
diff --git a/traffic_ops/testing/api/v13/servers_test.go b/traffic_ops/testing/api/v13/servers_test.go
index f87ecbb..a81b662 100644
--- a/traffic_ops/testing/api/v13/servers_test.go
+++ b/traffic_ops/testing/api/v13/servers_test.go
@@ -71,7 +71,7 @@ func CreateTestServers(t *testing.T) {
 	respPhysLocation := respPhysLocations[0]
 
 	// GET cachegroup1 cachegroup
-	respCacheGroups, _, err := TOSession.GetCacheGroupByName("cachegroup1")
+	respCacheGroups, _, err := TOSession.GetCacheGroupNullableByName("cachegroup1")
 	if err != nil {
 		t.Errorf("cannot GET CacheGroup by name: cachegroup1 - %v\n", err)
 	}
@@ -91,7 +91,7 @@ func CreateTestServers(t *testing.T) {
 		server.TypeID = respType.ID
 		server.StatusID = respStatus.ID
 		server.PhysLocationID = respPhysLocation.ID
-		server.CachegroupID = respCacheGroup.ID
+		server.CachegroupID = *respCacheGroup.ID
 
 		resp, _, err := TOSession.CreateServer(server)
 		log.Debugln("Response: ", server.HostName, " ", resp)
diff --git a/traffic_ops/testing/api/v13/staticdnsentries_test.go b/traffic_ops/testing/api/v13/staticdnsentries_test.go
index 9dad333..3dc8a6a 100644
--- a/traffic_ops/testing/api/v13/staticdnsentries_test.go
+++ b/traffic_ops/testing/api/v13/staticdnsentries_test.go
@@ -72,12 +72,12 @@ func CreateTestStaticDNSEntries(t *testing.T) {
 		staticDNSEntry.DeliveryServiceID = respDS.ID
 
 		// GET Cachegroup to associate
-		respGroups, _, err := TOSession.GetCacheGroupByName(staticDNSEntry.CacheGroupName)
+		respGroups, _, err := TOSession.GetCacheGroupNullableByName(staticDNSEntry.CacheGroupName)
 		if err != nil {
 			t.Errorf("cannot GET CacheGroup by Name: %v\n", err)
 		}
 		respGroup := respGroups[0]
-		staticDNSEntry.CacheGroupID = respGroup.ID
+		staticDNSEntry.CacheGroupID = *respGroup.ID
 
 		resp, _, err := TOSession.CreateStaticDNSEntry(staticDNSEntry)
 		log.Debugln("Response: ", resp)
diff --git a/traffic_ops/testing/api/v13/traffic_control.go b/traffic_ops/testing/api/v13/traffic_control.go
index 80e40ab..9213803 100644
--- a/traffic_ops/testing/api/v13/traffic_control.go
+++ b/traffic_ops/testing/api/v13/traffic_control.go
@@ -24,7 +24,7 @@ import (
 type TrafficControl struct {
 	ASNs                           []v12.ASN                           `json:"asns"`
 	CDNs                           []v13.CDN                           `json:"cdns"`
-	CacheGroups                    []v13.CacheGroup                    `json:"cachegroups"`
+	CacheGroups                    []v13.CacheGroupNullable            `json:"cachegroups"`
 	DeliveryServiceRequests        []v12.DeliveryServiceRequest        `json:"deliveryServiceRequests"`
 	DeliveryServiceRequestComments []v12.DeliveryServiceRequestComment `json:"deliveryServiceRequestComments"`
 	DeliveryServices               []v12.DeliveryServiceV13            `json:"deliveryservices"`
diff --git a/traffic_ops/traffic_ops_golang/cachegroup/cachegroups.go b/traffic_ops/traffic_ops_golang/cachegroup/cachegroups.go
index 6a008fc..1151bcc 100644
--- a/traffic_ops/traffic_ops_golang/cachegroup/cachegroups.go
+++ b/traffic_ops/traffic_ops_golang/cachegroup/cachegroups.go
@@ -164,44 +164,6 @@ func (cg TOCacheGroup) Validate() error {
 	return util.JoinErrs(tovalidate.ToErrors(errs))
 }
 
-// looks up the parent_cachegroup_id and the secondary_cachegroup_id
-// if the respective names are defined in the cachegroup struct.  A
-// sucessful lookup sets the two ids on the struct.
-//
-// used by Create()
-func getParentCachegroupIDs(tx *sqlx.Tx, cachegroup *TOCacheGroup) error {
-	query := `SELECT id FROM cachegroup where name=$1`
-	var parentID int
-	var secondaryParentID int
-
-	if cachegroup.ParentName != nil && *cachegroup.ParentName != "" {
-		err := tx.QueryRow(query, *cachegroup.ParentName).Scan(&parentID)
-		if err != nil {
-			log.Errorf("received error: %++v from query execution", err)
-			return err
-		}
-		cachegroup.ParentCachegroupID = &parentID
-	}
-	// not using 'omitempty' on the CacheGroup struct so a '0' is really an empty field, so set the pointer to nil
-	if cachegroup.ParentCachegroupID != nil && *cachegroup.ParentCachegroupID == 0 {
-		cachegroup.ParentCachegroupID = nil
-	}
-
-	if cachegroup.SecondaryParentName != nil && *cachegroup.SecondaryParentName != "" {
-		err := tx.QueryRow(query, *cachegroup.SecondaryParentName).Scan(&secondaryParentID)
-		if err != nil {
-			log.Errorf("received error: %++v from query execution", err)
-			return err
-		}
-		cachegroup.SecondaryParentCachegroupID = &secondaryParentID
-	}
-	// not using 'omitempty' on the CacheGroup struct so a '0' is really an empty field, so set the pointer to nil
-	if cachegroup.SecondaryParentCachegroupID != nil && *cachegroup.SecondaryParentCachegroupID == 0 {
-		cachegroup.SecondaryParentCachegroupID = nil
-	}
-	return nil
-}
-
 //The TOCacheGroup implementation of the Creator interface
 //all implementations of Creator should use transactions and return the proper errorType
 //ParsePQUniqueConstraintError is used to determine if a cachegroup with conflicting values exists
@@ -210,12 +172,6 @@ func getParentCachegroupIDs(tx *sqlx.Tx, cachegroup *TOCacheGroup) error {
 //The insert sql returns the id and lastUpdated values of the newly inserted cachegroup and have
 //to be added to the struct
 func (cg *TOCacheGroup) Create() (error, tc.ApiErrorType) {
-	err := getParentCachegroupIDs(cg.ReqInfo.Tx, cg)
-	if err != nil {
-		log.Error.Printf("failure looking up parent cache groups %v", err)
-		return tc.DBError, tc.SystemError
-	}
-
 	coordinateID, err := cg.createCoordinate()
 	if err != nil {
 		log.Errorf("creating cachegroup: %v", err)
@@ -370,13 +326,6 @@ func (cg *TOCacheGroup) Read(parameters map[string]string) ([]interface{}, []err
 //if so, it will return an errorType of DataConflict and the type should be appended to the
 //generic error message returned
 func (cg *TOCacheGroup) Update() (error, tc.ApiErrorType) {
-	// fix up parent ids.
-	err := getParentCachegroupIDs(cg.ReqInfo.Tx, cg)
-	if err != nil {
-		log.Error.Printf("failure looking up parent cache groups %v", err)
-		return tc.DBError, tc.SystemError
-	}
-
 	coordinateID, err, errType := cg.handleCoordinateUpdate()
 	if err != nil {
 		return err, errType