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 2022/05/13 01:34:52 UTC

[trafficcontrol] branch master updated: Prevent modifying and/or deleting reserved statuses (#6817)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new aa24989fa3 Prevent modifying and/or deleting reserved statuses (#6817)
aa24989fa3 is described below

commit aa24989fa31c633df28d6b8c10a4fb86b03fbc48
Author: Srijeet Chatterjee <30...@users.noreply.github.com>
AuthorDate: Thu May 12 19:34:46 2022 -0600

    Prevent modifying and/or deleting reserved statuses (#6817)
    
    * prevent modifying and/or deleting reserved statuses
    
    * fix tests
    
    * cleanup
    
    * code review fixes
    
    * fix test
    
    * code review round 2
    
    * remove unused file
---
 CHANGELOG.md                                       |   1 +
 .../testing/ort-tests/t3c-apply-diff_test.go       |   2 +-
 .../ort-tests/t3c-apply-unset-update_test.go       |   2 +-
 .../ort-tests/t3c-apply-wait-for-parents_test.go   |   6 +-
 .../ort-tests/t3c-create-empty-file_test.go        |   2 +-
 .../testing/ort-tests/t3c-dns-local-bind_test.go   |   2 +-
 .../testing/ort-tests/t3c-fail-log_test.go         |   2 +-
 cache-config/testing/ort-tests/t3c-git_test.go     |   2 +-
 cache-config/testing/ort-tests/t3c-ims_test.go     |   2 +-
 cache-config/testing/ort-tests/t3c-jobs_test.go    |   2 +-
 .../testing/ort-tests/t3c-lockfile_test.go         |   2 +-
 .../ort-tests/t3c-no-outgoing-ip-flag_test.go      |   2 +-
 .../ort-tests/t3c-os-hostname-short_test.go        |   2 +-
 cache-config/testing/ort-tests/t3c-reload_test.go  |   2 +-
 cache-config/testing/ort-tests/t3c_mode_test.go    |   2 +-
 .../testing/ort-tests/t3c_update_to_flags_test.go  |   2 +-
 cache-config/testing/ort-tests/tc-fixtures.json    |  29 -----
 cache-config/testing/ort-tests/tcdata/statuses.go  |  62 ----------
 cache-config/testing/ort-tests/tcdata/todb.go      |   2 +-
 cache-config/testing/ort-tests/tcdata/withobjs.go  |   2 -
 .../testing/ort-tests/to_requester_test.go         |   2 +-
 cache-config/testing/ort-tests/to_updater_test.go  |   2 +-
 lib/go-tc/enum.go                                  |   3 +
 lib/go-tc/statuses.go                              |  18 +++
 .../testing/tests/health-client-startup_test.go    |   2 +-
 ...2022050916074300_add_reserved_statuses.down.sql |  17 +++
 .../2022050916074300_add_reserved_statuses.up.sql  |  23 ++++
 traffic_ops/testing/api/v2/statuses_test.go        |  77 ++++++------
 traffic_ops/testing/api/v2/tc-fixtures.json        |  20 ---
 traffic_ops/testing/api/v2/todb_test.go            |   2 +-
 traffic_ops/testing/api/v3/statuses_test.go        | 115 +++++++++---------
 traffic_ops/testing/api/v3/tc-fixtures.json        |  20 ---
 traffic_ops/testing/api/v3/todb_test.go            |   2 +-
 traffic_ops/testing/api/v4/statuses_test.go        | 134 +++++++++++----------
 traffic_ops/testing/api/v4/tc-fixtures.json        |  20 ---
 traffic_ops/testing/api/v4/todb_test.go            |   2 +-
 traffic_ops/traffic_ops_golang/status/statuses.go  |  26 +++-
 37 files changed, 282 insertions(+), 333 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 341ce78450..db7aa0e5de 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
 - Added layered profile feature to 4.0 for `GET` /deliveryservices/{id}/servers/ and /deliveryservices/{id}/servers/eligible.
 
 ### Fixed
+- [#6291](https://github.com/apache/trafficcontrol/issues/6291) Prevent Traffic Ops from modifying and/or deleting reserved statuses.
 - Update traffic\_portal dependencies to mitigate `npm audit` issues.
 - Fixed a cdn-in-a-box build issue when using `RHEL_VERSION=7`
 - `dequeueing` server updates should not require checking for cdn locks.
diff --git a/cache-config/testing/ort-tests/t3c-apply-diff_test.go b/cache-config/testing/ort-tests/t3c-apply-diff_test.go
index b35e3da372..f7613bc68e 100644
--- a/cache-config/testing/ort-tests/t3c-apply-diff_test.go
+++ b/cache-config/testing/ort-tests/t3c-apply-diff_test.go
@@ -28,7 +28,7 @@ import (
 func TestApplyDiff(t *testing.T) {
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 		tcdata.DeliveryServices}, func() {
diff --git a/cache-config/testing/ort-tests/t3c-apply-unset-update_test.go b/cache-config/testing/ort-tests/t3c-apply-unset-update_test.go
index 0d3ae688f0..55b95c36f4 100644
--- a/cache-config/testing/ort-tests/t3c-apply-unset-update_test.go
+++ b/cache-config/testing/ort-tests/t3c-apply-unset-update_test.go
@@ -73,7 +73,7 @@ func verifyUpdateStatusIsTrue() error {
 func TestT3cUnsetsUpdateFlag(t *testing.T) {
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 		tcdata.DeliveryServices}, func() {
diff --git a/cache-config/testing/ort-tests/t3c-apply-wait-for-parents_test.go b/cache-config/testing/ort-tests/t3c-apply-wait-for-parents_test.go
index 94df96e26a..4b86042d4d 100644
--- a/cache-config/testing/ort-tests/t3c-apply-wait-for-parents_test.go
+++ b/cache-config/testing/ort-tests/t3c-apply-wait-for-parents_test.go
@@ -36,7 +36,7 @@ const childCacheHostName = DefaultCacheHostName
 func TestWaitForParentsTrue(t *testing.T) {
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 		tcdata.DeliveryServices}, func() {
@@ -147,7 +147,7 @@ func TestWaitForParentsTrue(t *testing.T) {
 func TestWaitForParentsDefaultReval(t *testing.T) {
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 		tcdata.DeliveryServices}, func() {
@@ -216,7 +216,7 @@ func TestWaitForParentsDefaultReval(t *testing.T) {
 func TestWaitForParentsFalse(t *testing.T) {
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 		tcdata.DeliveryServices}, func() {
diff --git a/cache-config/testing/ort-tests/t3c-create-empty-file_test.go b/cache-config/testing/ort-tests/t3c-create-empty-file_test.go
index 9389597c79..48781faf4b 100644
--- a/cache-config/testing/ort-tests/t3c-create-empty-file_test.go
+++ b/cache-config/testing/ort-tests/t3c-create-empty-file_test.go
@@ -31,7 +31,7 @@ func TestT3cCreateEmptyFile(t *testing.T) {
 	// t3c must create semantically blank files. Failing to do so will cause other config files that reference them to fail.
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 		tcdata.DeliveryServices}, func() {
diff --git a/cache-config/testing/ort-tests/t3c-dns-local-bind_test.go b/cache-config/testing/ort-tests/t3c-dns-local-bind_test.go
index 247fc53391..be158d885e 100644
--- a/cache-config/testing/ort-tests/t3c-dns-local-bind_test.go
+++ b/cache-config/testing/ort-tests/t3c-dns-local-bind_test.go
@@ -29,7 +29,7 @@ import (
 func TestT3CDNSLocalBind(t *testing.T) {
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 		tcdata.DeliveryServices}, func() {
diff --git a/cache-config/testing/ort-tests/t3c-fail-log_test.go b/cache-config/testing/ort-tests/t3c-fail-log_test.go
index 98e310412e..47a6a3014c 100644
--- a/cache-config/testing/ort-tests/t3c-fail-log_test.go
+++ b/cache-config/testing/ort-tests/t3c-fail-log_test.go
@@ -24,7 +24,7 @@ import (
 func TestT3cApplyFailMsg(t *testing.T) {
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 		tcdata.DeliveryServices}, func() {
diff --git a/cache-config/testing/ort-tests/t3c-git_test.go b/cache-config/testing/ort-tests/t3c-git_test.go
index 2250595d83..7f58773dc5 100644
--- a/cache-config/testing/ort-tests/t3c-git_test.go
+++ b/cache-config/testing/ort-tests/t3c-git_test.go
@@ -31,7 +31,7 @@ import (
 func TestT3cGit(t *testing.T) {
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 		tcdata.DeliveryServices}, func() {
diff --git a/cache-config/testing/ort-tests/t3c-ims_test.go b/cache-config/testing/ort-tests/t3c-ims_test.go
index 7772a539ab..50e27c119a 100644
--- a/cache-config/testing/ort-tests/t3c-ims_test.go
+++ b/cache-config/testing/ort-tests/t3c-ims_test.go
@@ -34,7 +34,7 @@ import (
 func TestIMS(t *testing.T) {
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 		tcdata.DeliveryServices}, func() {
diff --git a/cache-config/testing/ort-tests/t3c-jobs_test.go b/cache-config/testing/ort-tests/t3c-jobs_test.go
index e3fff5905b..980ee67ce3 100644
--- a/cache-config/testing/ort-tests/t3c-jobs_test.go
+++ b/cache-config/testing/ort-tests/t3c-jobs_test.go
@@ -29,7 +29,7 @@ import (
 func TestT3CJobs(t *testing.T) {
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 		tcdata.DeliveryServices}, func() {
diff --git a/cache-config/testing/ort-tests/t3c-lockfile_test.go b/cache-config/testing/ort-tests/t3c-lockfile_test.go
index 677d5b2900..6f06a8ff2b 100644
--- a/cache-config/testing/ort-tests/t3c-lockfile_test.go
+++ b/cache-config/testing/ort-tests/t3c-lockfile_test.go
@@ -27,7 +27,7 @@ import (
 func TestLockfile(t *testing.T) {
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 		tcdata.DeliveryServices}, func() {
diff --git a/cache-config/testing/ort-tests/t3c-no-outgoing-ip-flag_test.go b/cache-config/testing/ort-tests/t3c-no-outgoing-ip-flag_test.go
index 2c74bb92c6..8e14dc8068 100644
--- a/cache-config/testing/ort-tests/t3c-no-outgoing-ip-flag_test.go
+++ b/cache-config/testing/ort-tests/t3c-no-outgoing-ip-flag_test.go
@@ -55,7 +55,7 @@ func testNoOutgoingIPAfterUpdate(t *testing.T, noOutgoingIP *bool) {
 func TestT3CNoOutgoingIP(t *testing.T) {
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 		tcdata.DeliveryServices}, func() {
diff --git a/cache-config/testing/ort-tests/t3c-os-hostname-short_test.go b/cache-config/testing/ort-tests/t3c-os-hostname-short_test.go
index 49d42136f8..5eaace94dc 100644
--- a/cache-config/testing/ort-tests/t3c-os-hostname-short_test.go
+++ b/cache-config/testing/ort-tests/t3c-os-hostname-short_test.go
@@ -25,7 +25,7 @@ import (
 func TestT3cApplyOSHostnameShort(t *testing.T) {
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 		tcdata.DeliveryServices}, func() {
diff --git a/cache-config/testing/ort-tests/t3c-reload_test.go b/cache-config/testing/ort-tests/t3c-reload_test.go
index a9705b66c7..5dd390d5a5 100644
--- a/cache-config/testing/ort-tests/t3c-reload_test.go
+++ b/cache-config/testing/ort-tests/t3c-reload_test.go
@@ -28,7 +28,7 @@ import (
 func TestT3cReload(t *testing.T) {
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 		tcdata.DeliveryServices, tcdata.InvalidationJobs}, func() {
diff --git a/cache-config/testing/ort-tests/t3c_mode_test.go b/cache-config/testing/ort-tests/t3c_mode_test.go
index 12cb8d667c..702e14e8e9 100644
--- a/cache-config/testing/ort-tests/t3c_mode_test.go
+++ b/cache-config/testing/ort-tests/t3c_mode_test.go
@@ -106,7 +106,7 @@ func checkDiff(fName, atsUid, atsGid string, t *testing.T) {
 func TestT3cBadassAndSyncDs(t *testing.T) {
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 		tcdata.DeliveryServices}, func() {
diff --git a/cache-config/testing/ort-tests/t3c_update_to_flags_test.go b/cache-config/testing/ort-tests/t3c_update_to_flags_test.go
index 35ae74f980..39c9dec0ce 100644
--- a/cache-config/testing/ort-tests/t3c_update_to_flags_test.go
+++ b/cache-config/testing/ort-tests/t3c_update_to_flags_test.go
@@ -25,7 +25,7 @@ import (
 func TestT3cTOUpdates(t *testing.T) {
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 		tcdata.DeliveryServices, tcdata.InvalidationJobs}, func() {
diff --git a/cache-config/testing/ort-tests/tc-fixtures.json b/cache-config/testing/ort-tests/tc-fixtures.json
index 4f7098aeb0..54a3677132 100644
--- a/cache-config/testing/ort-tests/tc-fixtures.json
+++ b/cache-config/testing/ort-tests/tc-fixtures.json
@@ -4535,35 +4535,6 @@
       "type": "AAAA_RECORD"
     }
   ],
-  "statuses": [
-    {
-      "description": "Edge: Puts server in CCR config file in this state, but CCR will never route traffic to it. Mid: Server will not be included in parent.config files for its edge caches",
-      "name": "OFFLINE"
-    },
-    {
-      "description": "Edge: Puts server in CCR config file in this state, and CCR will always route traffic to it. Mid: Server will be included in parent.config files for its edges",
-      "name": "ONLINE"
-    },
-    {
-      "description": "Edge: Puts server in CCR config file in this state, and CCR will adhere to the health protocol. Mid: N/A for now",
-      "name": "REPORTED"
-    },
-    {
-      "description": "Temporary down. Edge: XMPP client will send status OFFLINE to CCR, otherwise similar to REPORTED. Mid: Server will not be included in parent.config files for its edge caches",
-      "name": "ADMIN_DOWN"
-    },
-    {
-      "description": "Edge: 12M will not include caches in this state in CCR config files. Mid: N/A for now",
-      "name": "CCR_IGNORE"
-    },
-    {
-      "description": "Pre Production. Not active in any configuration.",
-      "name": "PRE_PROD"
-    },
-    {
-      "name": "TEST_NULL_DESCRIPTION"
-    }
-  ],
   "tenants": [
     {
       "active": true,
diff --git a/cache-config/testing/ort-tests/tcdata/statuses.go b/cache-config/testing/ort-tests/tcdata/statuses.go
deleted file mode 100644
index 81d82b288b..0000000000
--- a/cache-config/testing/ort-tests/tcdata/statuses.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package tcdata
-
-/*
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-   http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
-*/
-
-import (
-	"testing"
-)
-
-func (r *TCData) CreateTestStatuses(t *testing.T) {
-
-	for _, status := range r.TestData.Statuses {
-		resp, _, err := TOSession.CreateStatusNullable(status)
-		t.Log("Response: ", resp)
-		if err != nil {
-			t.Errorf("could not CREATE types: %v", err)
-		}
-	}
-
-}
-
-func (r *TCData) DeleteTestStatuses(t *testing.T) {
-
-	for _, status := range r.TestData.Statuses {
-		if status.Name == nil {
-			t.Fatal("cannot get ftest statuses: test data statuses must have names")
-		}
-
-		// Retrieve the Status by name so we can get the id for the Update
-		resp, _, err := TOSession.GetStatusByName(*status.Name)
-		if err != nil {
-			t.Errorf("cannot GET Status by name: %s - %v", *status.Name, err)
-		}
-		respStatus := resp[0]
-
-		delResp, _, err := TOSession.DeleteStatusByID(respStatus.ID)
-		if err != nil {
-			t.Errorf("cannot DELETE Status by name: %v - %v", err, delResp)
-		}
-
-		// Retrieve the Status to see if it got deleted
-		types, _, err := TOSession.GetStatusByName(*status.Name)
-		if err != nil {
-			t.Errorf("error deleting Status name: %v", err)
-		}
-		if len(types) > 0 {
-			t.Errorf("expected Status name: %s to be deleted", *status.Name)
-		}
-	}
-}
diff --git a/cache-config/testing/ort-tests/tcdata/todb.go b/cache-config/testing/ort-tests/tcdata/todb.go
index d1df501774..0bbb27b76c 100644
--- a/cache-config/testing/ort-tests/tcdata/todb.go
+++ b/cache-config/testing/ort-tests/tcdata/todb.go
@@ -279,7 +279,7 @@ func (r *TCData) Teardown(db *sql.DB) error {
 	DELETE FROM cachegroup;
 	DELETE FROM coordinate;
 	DELETE FROM type;
-	DELETE FROM status;
+	DELETE FROM status s WHERE s.name NOT IN ('OFFLINE', 'ONLINE', 'PRE_PROD', 'ADMIN_DOWN', 'REPORTED');
 	DELETE FROM snapshot;
 	DELETE FROM cdn;
 	DELETE FROM service_category;
diff --git a/cache-config/testing/ort-tests/tcdata/withobjs.go b/cache-config/testing/ort-tests/tcdata/withobjs.go
index b3bf3337d1..d2460bea3e 100644
--- a/cache-config/testing/ort-tests/tcdata/withobjs.go
+++ b/cache-config/testing/ort-tests/tcdata/withobjs.go
@@ -49,7 +49,6 @@ const (
 	ServerServerCapabilities
 	Servers
 	ServiceCategories
-	Statuses
 	StaticDNSEntries
 	SteeringTargets
 	Tenants
@@ -99,7 +98,6 @@ func (r *TCData) WithObjs(t *testing.T, objs []TCObj, f func()) {
 		ServerServerCapabilities:             {r.CreateTestServerServerCapabilities, r.DeleteTestServerServerCapabilities},
 		Servers:                              {r.CreateTestServers, r.DeleteTestServers},
 		ServiceCategories:                    {r.CreateTestServiceCategories, r.DeleteTestServiceCategories},
-		Statuses:                             {r.CreateTestStatuses, r.DeleteTestStatuses},
 		StaticDNSEntries:                     {r.CreateTestStaticDNSEntries, r.DeleteTestStaticDNSEntries},
 		SteeringTargets:                      {r.SetupSteeringTargets, r.DeleteTestSteeringTargets},
 		Tenants:                              {r.CreateTestTenants, r.DeleteTestTenants},
diff --git a/cache-config/testing/ort-tests/to_requester_test.go b/cache-config/testing/ort-tests/to_requester_test.go
index cbc1e521f1..982ba47923 100644
--- a/cache-config/testing/ort-tests/to_requester_test.go
+++ b/cache-config/testing/ort-tests/to_requester_test.go
@@ -35,7 +35,7 @@ type Package struct {
 func TestTORequester(t *testing.T) {
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 		tcdata.DeliveryServices}, func() {
diff --git a/cache-config/testing/ort-tests/to_updater_test.go b/cache-config/testing/ort-tests/to_updater_test.go
index 5384818e86..efc97cab4e 100644
--- a/cache-config/testing/ort-tests/to_updater_test.go
+++ b/cache-config/testing/ort-tests/to_updater_test.go
@@ -31,7 +31,7 @@ import (
 func TestTOUpdater(t *testing.T) {
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 		tcdata.DeliveryServices, tcdata.InvalidationJobs}, func() {
diff --git a/lib/go-tc/enum.go b/lib/go-tc/enum.go
index 014a624485..8d100fa683 100644
--- a/lib/go-tc/enum.go
+++ b/lib/go-tc/enum.go
@@ -374,6 +374,9 @@ const (
 	// by Traffic Monitor. The vast majority of cache servers should have this
 	// Status.
 	CacheStatusReported = CacheStatus("REPORTED")
+	// CacheStatusPreProd represents a cache server that is not deployed to "production",
+	// but is ready for it.
+	CacheStatusPreProd = CacheStatus("PRE_PROD")
 	// CacheStatusInvalid represents an unrecognized Status value. Note that
 	// this is not actually "invalid", because Statuses may have any unique
 	// name, not just those captured as CacheStatus values in this package.
diff --git a/lib/go-tc/statuses.go b/lib/go-tc/statuses.go
index eafaf1a4e5..74c847b4a3 100644
--- a/lib/go-tc/statuses.go
+++ b/lib/go-tc/statuses.go
@@ -70,3 +70,21 @@ type StatusNullable struct {
 	LastUpdated *TimeNoMod `json:"lastUpdated" db:"last_updated"`
 	Name        *string    `json:"name" db:"name"`
 }
+
+// IsReservedStatus returns true if the passed in status name is reserved, and false if it isn't.
+// Currently, the reserved statuses are OFFLINE, ONLINE, REPORTED, PRE_PROD and ADMIN_DOWN.
+func IsReservedStatus(status string) bool {
+	switch CacheStatus(status) {
+	case CacheStatusOffline:
+		fallthrough
+	case CacheStatusReported:
+		fallthrough
+	case CacheStatusOnline:
+		fallthrough
+	case CacheStatusPreProd:
+		fallthrough
+	case CacheStatusAdminDown:
+		return true
+	}
+	return false
+}
diff --git a/tc-health-client/testing/tests/health-client-startup_test.go b/tc-health-client/testing/tests/health-client-startup_test.go
index 60c198bbe8..6d03fa45f3 100644
--- a/tc-health-client/testing/tests/health-client-startup_test.go
+++ b/tc-health-client/testing/tests/health-client-startup_test.go
@@ -39,7 +39,7 @@ func startHealthClient() {
 func TestHealthClientStartup(t *testing.T) {
 	tcd.WithObjs(t, []tcdata.TCObj{
 		tcdata.CDNs, tcdata.Types, tcdata.Tenants, tcdata.Parameters,
-		tcdata.Profiles, tcdata.ProfileParameters, tcdata.Statuses,
+		tcdata.Profiles, tcdata.ProfileParameters,
 		tcdata.Divisions, tcdata.Regions, tcdata.PhysLocations,
 		tcdata.CacheGroups, tcdata.Servers, tcdata.Topologies,
 	}, func() {
diff --git a/traffic_ops/app/db/migrations/2022050916074300_add_reserved_statuses.down.sql b/traffic_ops/app/db/migrations/2022050916074300_add_reserved_statuses.down.sql
new file mode 100644
index 0000000000..6b6a6e9174
--- /dev/null
+++ b/traffic_ops/app/db/migrations/2022050916074300_add_reserved_statuses.down.sql
@@ -0,0 +1,17 @@
+/*
+ * 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.
+ */
+
diff --git a/traffic_ops/app/db/migrations/2022050916074300_add_reserved_statuses.up.sql b/traffic_ops/app/db/migrations/2022050916074300_add_reserved_statuses.up.sql
new file mode 100644
index 0000000000..ddd9a128d3
--- /dev/null
+++ b/traffic_ops/app/db/migrations/2022050916074300_add_reserved_statuses.up.sql
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+
+INSERT INTO public.status ("name", description) VALUES ('ONLINE', 'Server is online.') ON CONFLICT ("name") DO NOTHING;
+INSERT INTO public.status ("name", description) VALUES ('OFFLINE', 'Server is Offline. Not active in any configuration.') ON CONFLICT ("name") DO NOTHING;
+INSERT INTO public.status ("name", description) VALUES ('REPORTED', 'Server is online and reported in the health protocol.') ON CONFLICT ("name") DO NOTHING;
+INSERT INTO public.status ("name", description) VALUES ('ADMIN_DOWN', 'Sever is administrative down and does not receive traffic.') ON CONFLICT ("name") DO NOTHING;
+INSERT INTO public.status ("name", description) VALUES ('PRE_PROD', 'Pre Production. Not active in any configuration.') ON CONFLICT ("name") DO NOTHING;
+
diff --git a/traffic_ops/testing/api/v2/statuses_test.go b/traffic_ops/testing/api/v2/statuses_test.go
index 9a15714723..9a11866f20 100644
--- a/traffic_ops/testing/api/v2/statuses_test.go
+++ b/traffic_ops/testing/api/v2/statuses_test.go
@@ -29,48 +29,55 @@ func TestStatuses(t *testing.T) {
 }
 
 func CreateTestStatuses(t *testing.T) {
-
 	for _, status := range testData.Statuses {
 		resp, _, err := TOSession.CreateStatusNullable(status)
 		t.Log("Response: ", resp)
 		if err != nil {
-			t.Errorf("could not CREATE types: %v", err)
+			t.Errorf("could not CREATE status: %v", err)
 		}
 	}
-
 }
 
 func UpdateTestStatuses(t *testing.T) {
 
-	firstStatus := testData.Statuses[0]
-	if firstStatus.Name == nil {
-		t.Fatal("cannot update test statuses: first test data status must have a name")
-	}
-
-	// Retrieve the Status by name so we can get the id for the Update
-	resp, _, err := TOSession.GetStatusByName(*firstStatus.Name)
-	if err != nil {
-		t.Errorf("cannot GET Status by name: %v - %v", firstStatus.Name, err)
+	if len(testData.Statuses) < 1 {
+		t.Fatal("Need at least one Status to test updating a Status")
 	}
-	remoteStatus := resp[0]
-	expectedStatusDesc := "new description"
-	remoteStatus.Description = expectedStatusDesc
-	var alert tc.Alerts
-	alert, _, err = TOSession.UpdateStatusByID(remoteStatus.ID, remoteStatus)
-	if err != nil {
-		t.Errorf("cannot UPDATE Status by id: %v - %v", err, alert)
-	}
-
-	// Retrieve the Status to check Status name got updated
-	resp, _, err = TOSession.GetStatusByID(remoteStatus.ID)
-	if err != nil {
-		t.Errorf("cannot GET Status by ID: %v - %v", firstStatus.Description, err)
-	}
-	respStatus := resp[0]
-	if respStatus.Description != expectedStatusDesc {
-		t.Errorf("results do not match actual: %s, expected: %s", respStatus.Name, expectedStatusDesc)
+	for _, status := range testData.Statuses {
+		if status.Name == nil {
+			t.Fatal("cannot update test statuses: test data status must have a name")
+		}
+		// Retrieve the Status by name so we can get the id for the Update
+		resp, _, err := TOSession.GetStatusByName(*status.Name)
+		if err != nil {
+			t.Errorf("cannot GET Status by name: %s - %v", *status.Name, err)
+		}
+		remoteStatus := resp[0]
+		expectedStatusDesc := "new description"
+		remoteStatus.Description = expectedStatusDesc
+		var alert tc.Alerts
+		alert, _, err = TOSession.UpdateStatusByID(remoteStatus.ID, remoteStatus)
+
+		if tc.IsReservedStatus(*status.Name) {
+			if err == nil {
+				t.Errorf("expected an error about while updating a reserved status, but got nothing")
+			}
+		} else {
+			if err != nil {
+				t.Errorf("cannot UPDATE Status by id: %d, err: %v - %v", remoteStatus.ID, err, alert)
+			}
+
+			// Retrieve the Status to check Status name got updated
+			resp, _, err = TOSession.GetStatusByID(remoteStatus.ID)
+			if err != nil {
+				t.Errorf("cannot GET Status by ID: %d - %v", remoteStatus.ID, err)
+			}
+			respStatus := resp[0]
+			if respStatus.Description != expectedStatusDesc {
+				t.Errorf("results do not match actual: %s, expected: %s", respStatus.Name, expectedStatusDesc)
+			}
+		}
 	}
-
 }
 
 func GetTestStatuses(t *testing.T) {
@@ -97,7 +104,7 @@ func DeleteTestStatuses(t *testing.T) {
 		// Retrieve the Status by name so we can get the id for the Update
 		resp, _, err := TOSession.GetStatusByName(*status.Name)
 		if err != nil {
-			t.Errorf("cannot GET Status by name: %v - %v", status.Name, err)
+			t.Errorf("cannot GET Status by name: %s - %v", *status.Name, err)
 		}
 		if len(resp) != 1 {
 			t.Errorf("Expected exactly one Status to exist with name '%s', found: %d", *status.Name, len(resp))
@@ -107,15 +114,15 @@ func DeleteTestStatuses(t *testing.T) {
 
 		delResp, _, err := TOSession.DeleteStatusByID(respStatus.ID)
 		if err != nil {
-			t.Errorf("cannot DELETE Status by name: %v - %v", err, delResp)
+			t.Errorf("cannot DELETE Status by ID: %v - %v", err, delResp)
 		}
 
 		// Retrieve the Status to see if it got deleted
-		types, _, err := TOSession.GetStatusByName(*status.Name)
+		statuses, _, err := TOSession.GetStatusByName(*status.Name)
 		if err != nil {
-			t.Errorf("error deleting Status name: %s", err.Error())
+			t.Errorf("error getting status by name: %s, err: %v", *status.Name, err)
 		}
-		if len(types) > 0 {
+		if len(statuses) > 0 {
 			t.Errorf("expected Status name: %s to be deleted", *status.Name)
 		}
 	}
diff --git a/traffic_ops/testing/api/v2/tc-fixtures.json b/traffic_ops/testing/api/v2/tc-fixtures.json
index cdb6a0cad1..77467ff78c 100644
--- a/traffic_ops/testing/api/v2/tc-fixtures.json
+++ b/traffic_ops/testing/api/v2/tc-fixtures.json
@@ -2224,30 +2224,10 @@
         }
     ],
     "statuses": [
-        {
-            "description": "Edge: Puts server in CCR config file in this state, but CCR will never route traffic to it. Mid: Server will not be included in parent.config files for its edge caches",
-            "name": "OFFLINE"
-        },
-        {
-            "description": "Edge: Puts server in CCR config file in this state, and CCR will always route traffic to it. Mid: Server will be included in parent.config files for its edges",
-            "name": "ONLINE"
-        },
-        {
-            "description": "Edge: Puts server in CCR config file in this state, and CCR will adhere to the health protocol. Mid: N/A for now",
-            "name": "REPORTED"
-        },
-        {
-            "description": "Temporary down. Edge: XMPP client will send status OFFLINE to CCR, otherwise similar to REPORTED. Mid: Server will not be included in parent.config files for its edge caches",
-            "name": "ADMIN_DOWN"
-        },
         {
             "description": "Edge: 12M will not include caches in this state in CCR config files. Mid: N/A for now",
             "name": "CCR_IGNORE"
         },
-        {
-            "description": "Pre Production. Not active in any configuration.",
-            "name": "PRE_PROD"
-        },
         {
             "name": "TEST_NULL_DESCRIPTION"
         }
diff --git a/traffic_ops/testing/api/v2/todb_test.go b/traffic_ops/testing/api/v2/todb_test.go
index d8ea4870b0..887bbcdb78 100644
--- a/traffic_ops/testing/api/v2/todb_test.go
+++ b/traffic_ops/testing/api/v2/todb_test.go
@@ -283,7 +283,7 @@ func Teardown(db *sql.DB) error {
 	DELETE FROM cachegroup;
 	DELETE FROM coordinate;
 	DELETE FROM type;
-	DELETE FROM status;
+	DELETE FROM status s WHERE s.name NOT IN ('OFFLINE', 'ONLINE', 'PRE_PROD', 'ADMIN_DOWN', 'REPORTED');
 	DELETE FROM snapshot;
 	DELETE FROM cdn;
 	DELETE FROM service_category;
diff --git a/traffic_ops/testing/api/v3/statuses_test.go b/traffic_ops/testing/api/v3/statuses_test.go
index db095c6c83..b5ed2b1c7d 100644
--- a/traffic_ops/testing/api/v3/statuses_test.go
+++ b/traffic_ops/testing/api/v3/statuses_test.go
@@ -47,27 +47,31 @@ func TestStatuses(t *testing.T) {
 }
 
 func UpdateTestStatusesWithHeaders(t *testing.T, header http.Header) {
-	if len(testData.Statuses) > 0 {
-		firstStatus := testData.Statuses[0]
-		if firstStatus.Name == nil {
-			t.Fatal("cannot update test statuses: first test data status must have a name")
-		}
+	if len(testData.Statuses) < 1 {
+		t.Fatal("Need at least one Status to test updating a status with an HTTP header")
+	}
 
-		// Retrieve the Status by name so we can get the id for the Update
-		resp, _, err := TOSession.GetStatusByNameWithHdr(*firstStatus.Name, header)
-		if err != nil {
-			t.Errorf("cannot GET Status by name: %v - %v", firstStatus.Name, err)
+	for _, status := range testData.Statuses {
+		if status.Name == nil {
+			t.Fatal("cannot update test statuses: test data status must have a name")
 		}
-		if len(resp) > 0 {
-			remoteStatus := resp[0]
-			expectedStatusDesc := "new description"
-			remoteStatus.Description = expectedStatusDesc
-			_, reqInf, err := TOSession.UpdateStatusByIDWithHdr(remoteStatus.ID, remoteStatus, header)
-			if err == nil {
-				t.Errorf("Expected error about precondition failed, but got none")
+		if !tc.IsReservedStatus(*status.Name) {
+			// Retrieve the Status by name so we can get the id for the Update
+			resp, _, err := TOSession.GetStatusByNameWithHdr(*status.Name, header)
+			if err != nil {
+				t.Errorf("cannot GET Status by name: %s - %v", *status.Name, err)
 			}
-			if reqInf.StatusCode != http.StatusPreconditionFailed {
-				t.Errorf("Expected status code 412, got %v", reqInf.StatusCode)
+			if len(resp) > 0 {
+				remoteStatus := resp[0]
+				expectedStatusDesc := "new description"
+				remoteStatus.Description = expectedStatusDesc
+				_, reqInf, err := TOSession.UpdateStatusByIDWithHdr(remoteStatus.ID, remoteStatus, header)
+				if err == nil {
+					t.Errorf("Expected error about precondition failed, but got none")
+				}
+				if reqInf.StatusCode != http.StatusPreconditionFailed {
+					t.Errorf("Expected status code 412, got %v", reqInf.StatusCode)
+				}
 			}
 		}
 	}
@@ -125,15 +129,13 @@ func GetTestStatusesIMS(t *testing.T) {
 }
 
 func CreateTestStatuses(t *testing.T) {
-
 	for _, status := range testData.Statuses {
 		resp, _, err := TOSession.CreateStatusNullable(status)
 		t.Log("Response: ", resp)
 		if err != nil {
-			t.Errorf("could not CREATE types: %v", err)
+			t.Errorf("could not CREATE status: %v", err)
 		}
 	}
-
 }
 
 func SortTestStatuses(t *testing.T) {
@@ -156,36 +158,41 @@ func SortTestStatuses(t *testing.T) {
 }
 
 func UpdateTestStatuses(t *testing.T) {
+	for _, status := range testData.Statuses {
+		if status.Name == nil {
+			t.Fatal("cannot update test statuses: test data status must have a name")
+		}
+		// Retrieve the Status by name so we can get the id for the Update
+		resp, _, err := TOSession.GetStatusByName(*status.Name)
+		if err != nil {
+			t.Errorf("cannot GET Status by name: %s - %v", *status.Name, err)
+		}
+		remoteStatus := resp[0]
+		expectedStatusDesc := "new description"
+		remoteStatus.Description = expectedStatusDesc
+		var alert tc.Alerts
+		alert, _, err = TOSession.UpdateStatusByID(remoteStatus.ID, remoteStatus)
 
-	firstStatus := testData.Statuses[0]
-	if firstStatus.Name == nil {
-		t.Fatal("cannot update test statuses: first test data status must have a name")
-	}
-
-	// Retrieve the Status by name so we can get the id for the Update
-	resp, _, err := TOSession.GetStatusByName(*firstStatus.Name)
-	if err != nil {
-		t.Errorf("cannot GET Status by name: %v - %v", firstStatus.Name, err)
-	}
-	remoteStatus := resp[0]
-	expectedStatusDesc := "new description"
-	remoteStatus.Description = expectedStatusDesc
-	var alert tc.Alerts
-	alert, _, err = TOSession.UpdateStatusByID(remoteStatus.ID, remoteStatus)
-	if err != nil {
-		t.Errorf("cannot UPDATE Status by id: %v - %v", err, alert)
-	}
+		if tc.IsReservedStatus(*status.Name) {
+			if err == nil {
+				t.Errorf("expected an error about while updating a reserved status, but got nothing")
+			}
+		} else {
+			if err != nil {
+				t.Errorf("cannot UPDATE Status by id: %d, err: %v - %v", remoteStatus.ID, err, alert)
+			}
 
-	// Retrieve the Status to check Status name got updated
-	resp, _, err = TOSession.GetStatusByID(remoteStatus.ID)
-	if err != nil {
-		t.Errorf("cannot GET Status by ID: %v - %v", firstStatus.Description, err)
-	}
-	respStatus := resp[0]
-	if respStatus.Description != expectedStatusDesc {
-		t.Errorf("results do not match actual: %s, expected: %s", respStatus.Name, expectedStatusDesc)
+			// Retrieve the Status to check Status name got updated
+			resp, _, err = TOSession.GetStatusByID(remoteStatus.ID)
+			if err != nil {
+				t.Errorf("cannot GET Status by ID: %d - %v", remoteStatus.ID, err)
+			}
+			respStatus := resp[0]
+			if respStatus.Description != expectedStatusDesc {
+				t.Errorf("results do not match actual: %s, expected: %s", respStatus.Name, expectedStatusDesc)
+			}
+		}
 	}
-
 }
 
 func GetTestStatuses(t *testing.T) {
@@ -205,27 +212,27 @@ func DeleteTestStatuses(t *testing.T) {
 
 	for _, status := range testData.Statuses {
 		if status.Name == nil {
-			t.Fatal("cannot get ftest statuses: test data statuses must have names")
+			t.Fatal("cannot get test statuses: test data statuses must have names")
 		}
 
 		// Retrieve the Status by name so we can get the id for the Update
 		resp, _, err := TOSession.GetStatusByName(*status.Name)
 		if err != nil {
-			t.Errorf("cannot GET Status by name: %v - %v", status.Name, err)
+			t.Errorf("cannot GET Status by name: %s - %v", *status.Name, err)
 		}
 		respStatus := resp[0]
 
 		delResp, _, err := TOSession.DeleteStatusByID(respStatus.ID)
 		if err != nil {
-			t.Errorf("cannot DELETE Status by name: %v - %v", err, delResp)
+			t.Errorf("cannot DELETE Status by ID: %v - %v", err, delResp)
 		}
 
 		// Retrieve the Status to see if it got deleted
-		types, _, err := TOSession.GetStatusByName(*status.Name)
+		statuses, _, err := TOSession.GetStatusByName(*status.Name)
 		if err != nil {
-			t.Errorf("error deleting Status name: %s", err.Error())
+			t.Errorf("error getting status by name: %s, err: %v", *status.Name, err)
 		}
-		if len(types) > 0 {
+		if len(statuses) > 0 {
 			t.Errorf("expected Status name: %s to be deleted", *status.Name)
 		}
 	}
diff --git a/traffic_ops/testing/api/v3/tc-fixtures.json b/traffic_ops/testing/api/v3/tc-fixtures.json
index d9b962a512..39671def48 100644
--- a/traffic_ops/testing/api/v3/tc-fixtures.json
+++ b/traffic_ops/testing/api/v3/tc-fixtures.json
@@ -4785,30 +4785,10 @@
         }
     ],
     "statuses": [
-        {
-            "description": "Edge: Puts server in CCR config file in this state, but CCR will never route traffic to it. Mid: Server will not be included in parent.config files for its edge caches",
-            "name": "OFFLINE"
-        },
-        {
-            "description": "Edge: Puts server in CCR config file in this state, and CCR will always route traffic to it. Mid: Server will be included in parent.config files for its edges",
-            "name": "ONLINE"
-        },
-        {
-            "description": "Edge: Puts server in CCR config file in this state, and CCR will adhere to the health protocol. Mid: N/A for now",
-            "name": "REPORTED"
-        },
-        {
-            "description": "Temporary down. Edge: XMPP client will send status OFFLINE to CCR, otherwise similar to REPORTED. Mid: Server will not be included in parent.config files for its edge caches",
-            "name": "ADMIN_DOWN"
-        },
         {
             "description": "Edge: 12M will not include caches in this state in CCR config files. Mid: N/A for now",
             "name": "CCR_IGNORE"
         },
-        {
-            "description": "Pre Production. Not active in any configuration.",
-            "name": "PRE_PROD"
-        },
         {
             "name": "TEST_NULL_DESCRIPTION"
         }
diff --git a/traffic_ops/testing/api/v3/todb_test.go b/traffic_ops/testing/api/v3/todb_test.go
index 26a7539cc4..f42ae00ad4 100644
--- a/traffic_ops/testing/api/v3/todb_test.go
+++ b/traffic_ops/testing/api/v3/todb_test.go
@@ -285,7 +285,7 @@ func Teardown(db *sql.DB) error {
 	DELETE FROM cachegroup;
 	DELETE FROM coordinate;
 	DELETE FROM type;
-	DELETE FROM status;
+	DELETE FROM status s WHERE s.name NOT IN ('OFFLINE', 'ONLINE', 'PRE_PROD', 'ADMIN_DOWN', 'REPORTED');
 	DELETE FROM snapshot;
 	DELETE FROM cdn;
 	DELETE FROM service_category;
diff --git a/traffic_ops/testing/api/v4/statuses_test.go b/traffic_ops/testing/api/v4/statuses_test.go
index 106d5ca112..90958e94e5 100644
--- a/traffic_ops/testing/api/v4/statuses_test.go
+++ b/traffic_ops/testing/api/v4/statuses_test.go
@@ -23,6 +23,7 @@ import (
 	"time"
 
 	"github.com/apache/trafficcontrol/lib/go-rfc"
+	"github.com/apache/trafficcontrol/lib/go-tc"
 	client "github.com/apache/trafficcontrol/traffic_ops/v4-client"
 )
 
@@ -52,31 +53,33 @@ func UpdateTestStatusesWithHeaders(t *testing.T, header http.Header) {
 		t.Fatal("Need at least one Status to test updating a status with an HTTP header")
 	}
 
-	firstStatus := testData.Statuses[0]
-	if firstStatus.Name == nil {
-		t.Fatal("cannot update test statuses: first test data status must have a name")
-	}
-
-	// Retrieve the Status by name so we can get the id for the Update
-	opts := client.NewRequestOptions()
-	opts.Header = header
-	opts.QueryParameters.Set("name", *firstStatus.Name)
-	resp, _, err := TOSession.GetStatuses(opts)
-	if err != nil {
-		t.Errorf("cannot get Status by name '%s': %v - alerts %+v", *firstStatus.Name, err, resp.Alerts)
-	}
-	if len(resp.Response) > 0 {
-		remoteStatus := resp.Response[0]
-		expectedStatusDesc := "new description"
-		remoteStatus.Description = expectedStatusDesc
-
-		opts.QueryParameters.Del("name")
-		_, reqInf, err := TOSession.UpdateStatus(remoteStatus.ID, remoteStatus, opts)
-		if err == nil {
-			t.Errorf("Expected error about precondition failed, but got none")
+	for _, status := range testData.Statuses {
+		if status.Name == nil {
+			t.Fatal("cannot update test statuses: test data status must have a name")
 		}
-		if reqInf.StatusCode != http.StatusPreconditionFailed {
-			t.Errorf("Expected status code 412, got %d", reqInf.StatusCode)
+		if !tc.IsReservedStatus(*status.Name) {
+			// Retrieve the Status by name so we can get the id for the Update
+			opts := client.NewRequestOptions()
+			opts.Header = header
+			opts.QueryParameters.Set("name", *status.Name)
+			resp, _, err := TOSession.GetStatuses(opts)
+			if err != nil {
+				t.Errorf("cannot get Status by name '%s': %v - alerts %+v", *status.Name, err, resp.Alerts)
+			}
+			if len(resp.Response) > 0 {
+				remoteStatus := resp.Response[0]
+				expectedStatusDesc := "new description"
+				remoteStatus.Description = expectedStatusDesc
+
+				opts.QueryParameters.Del("name")
+				_, reqInf, err := TOSession.UpdateStatus(remoteStatus.ID, remoteStatus, opts)
+				if err == nil {
+					t.Errorf("Expected error about precondition failed, but got none")
+				}
+				if reqInf.StatusCode != http.StatusPreconditionFailed {
+					t.Errorf("Expected status code 412, got %d", reqInf.StatusCode)
+				}
+			}
 		}
 	}
 }
@@ -148,7 +151,6 @@ func CreateTestStatuses(t *testing.T) {
 			t.Errorf("could not create Status: %v - alerts: %+v", err, resp.Alerts)
 		}
 	}
-
 }
 
 func SortTestStatuses(t *testing.T) {
@@ -171,45 +173,50 @@ func UpdateTestStatuses(t *testing.T) {
 	if len(testData.Statuses) < 1 {
 		t.Fatal("Need at least one Status to test updating a Status")
 	}
-	firstStatus := testData.Statuses[0]
-	if firstStatus.Name == nil {
-		t.Fatal("cannot update test statuses: first test data status must have a name")
-	}
-
-	// Retrieve the Status by name so we can get the id for the Update
-	opts := client.NewRequestOptions()
-	opts.QueryParameters.Set("name", *firstStatus.Name)
-	resp, _, err := TOSession.GetStatuses(opts)
-	if err != nil {
-		t.Errorf("cannot get Status by name '%s': %v - alerts: %+v", *firstStatus.Name, err, resp.Alerts)
-	}
-	if len(resp.Response) != 1 {
-		t.Fatalf("Expected exactly one Status to exist with name '%s', found: %d", *firstStatus.Name, len(resp.Response))
-	}
-	remoteStatus := resp.Response[0]
-	expectedStatusDesc := "new description"
-	remoteStatus.Description = expectedStatusDesc
-
-	alert, _, err := TOSession.UpdateStatus(remoteStatus.ID, remoteStatus, client.RequestOptions{})
-	if err != nil {
-		t.Errorf("cannot update Status: %v - alerts: %+v", err, alert.Alerts)
-	}
-
-	// Retrieve the Status to check Status name got updated
-	opts.QueryParameters.Del("name")
-	opts.QueryParameters.Set("id", strconv.Itoa(remoteStatus.ID))
-	resp, _, err = TOSession.GetStatuses(opts)
-	if err != nil {
-		t.Errorf("cannot get Status '%s' by ID %d: %v - alerts: %+v", *firstStatus.Name, remoteStatus.ID, err, resp.Alerts)
-	}
-	if len(resp.Response) != 1 {
-		t.Fatalf("Expected exactly one Status to exist with ID %d, found: %d", remoteStatus.ID, len(resp.Response))
-	}
-	respStatus := resp.Response[0]
-	if respStatus.Description != expectedStatusDesc {
-		t.Errorf("results do not match actual: %s, expected: %s", respStatus.Name, expectedStatusDesc)
+	for _, status := range testData.Statuses {
+		if status.Name == nil {
+			t.Fatal("cannot update test statuses: test data status must have a name")
+		}
+		// Retrieve the Status by name so we can get the id for the Update
+		opts := client.NewRequestOptions()
+		opts.QueryParameters.Set("name", *status.Name)
+		resp, _, err := TOSession.GetStatuses(opts)
+		if err != nil {
+			t.Errorf("cannot get Status by name '%s': %v - alerts: %+v", *status.Name, err, resp.Alerts)
+		}
+		if len(resp.Response) != 1 {
+			t.Fatalf("Expected exactly one Status to exist with name '%s', found: %d", *status.Name, len(resp.Response))
+		}
+		remoteStatus := resp.Response[0]
+		expectedStatusDesc := "new description"
+		remoteStatus.Description = expectedStatusDesc
+		alert, _, err := TOSession.UpdateStatus(remoteStatus.ID, remoteStatus, client.RequestOptions{})
+
+		if tc.IsReservedStatus(*status.Name) {
+			if err == nil {
+				t.Errorf("expected an error about while updating a reserved status, but got nothing")
+			}
+		} else {
+			if err != nil {
+				t.Errorf("cannot update Status: %v - alerts: %+v", err, alert.Alerts)
+			}
+
+			// Retrieve the Status to check Status name got updated
+			opts.QueryParameters.Del("name")
+			opts.QueryParameters.Set("id", strconv.Itoa(remoteStatus.ID))
+			resp, _, err = TOSession.GetStatuses(opts)
+			if err != nil {
+				t.Errorf("cannot get Status '%s' by ID %d: %v - alerts: %+v", *status.Name, remoteStatus.ID, err, resp.Alerts)
+			}
+			if len(resp.Response) != 1 {
+				t.Fatalf("Expected exactly one Status to exist with ID %d, found: %d", remoteStatus.ID, len(resp.Response))
+			}
+			respStatus := resp.Response[0]
+			if respStatus.Description != expectedStatusDesc {
+				t.Errorf("results do not match actual: %s, expected: %s", respStatus.Name, expectedStatusDesc)
+			}
+		}
 	}
-
 }
 
 func GetTestStatuses(t *testing.T) {
@@ -232,7 +239,6 @@ func DeleteTestStatuses(t *testing.T) {
 		if status.Name == nil {
 			t.Fatal("cannot get test statuses: test data statuses must have names")
 		}
-
 		// Retrieve the Status by name so we can get the id for the Update
 		opts.QueryParameters.Set("name", *status.Name)
 		resp, _, err := TOSession.GetStatuses(opts)
diff --git a/traffic_ops/testing/api/v4/tc-fixtures.json b/traffic_ops/testing/api/v4/tc-fixtures.json
index 632153f725..2956a60cf1 100644
--- a/traffic_ops/testing/api/v4/tc-fixtures.json
+++ b/traffic_ops/testing/api/v4/tc-fixtures.json
@@ -5437,30 +5437,10 @@
         }
     ],
     "statuses": [
-        {
-            "description": "Edge: Puts server in CCR config file in this state, but CCR will never route traffic to it. Mid: Server will not be included in parent.config files for its edge caches",
-            "name": "OFFLINE"
-        },
-        {
-            "description": "Edge: Puts server in CCR config file in this state, and CCR will always route traffic to it. Mid: Server will be included in parent.config files for its edges",
-            "name": "ONLINE"
-        },
-        {
-            "description": "Edge: Puts server in CCR config file in this state, and CCR will adhere to the health protocol. Mid: N/A for now",
-            "name": "REPORTED"
-        },
-        {
-            "description": "Temporary down. Edge: XMPP client will send status OFFLINE to CCR, otherwise similar to REPORTED. Mid: Server will not be included in parent.config files for its edge caches",
-            "name": "ADMIN_DOWN"
-        },
         {
             "description": "Edge: 12M will not include caches in this state in CCR config files. Mid: N/A for now",
             "name": "CCR_IGNORE"
         },
-        {
-            "description": "Pre Production. Not active in any configuration.",
-            "name": "PRE_PROD"
-        },
         {
             "name": "TEST_NULL_DESCRIPTION"
         }
diff --git a/traffic_ops/testing/api/v4/todb_test.go b/traffic_ops/testing/api/v4/todb_test.go
index 697f35df36..7f9104a8a3 100644
--- a/traffic_ops/testing/api/v4/todb_test.go
+++ b/traffic_ops/testing/api/v4/todb_test.go
@@ -417,7 +417,7 @@ func Teardown(db *sql.DB) error {
 	DELETE FROM cachegroup;
 	DELETE FROM coordinate;
 	DELETE FROM type;
-	DELETE FROM status;
+	DELETE FROM status s WHERE s.name NOT IN ('OFFLINE', 'ONLINE', 'PRE_PROD', 'ADMIN_DOWN', 'REPORTED');
 	DELETE FROM snapshot;
 	DELETE FROM cdn;
 	DELETE FROM service_category;
diff --git a/traffic_ops/traffic_ops_golang/status/statuses.go b/traffic_ops/traffic_ops_golang/status/statuses.go
index f5f75d5940..875edce434 100644
--- a/traffic_ops/traffic_ops_golang/status/statuses.go
+++ b/traffic_ops/traffic_ops_golang/status/statuses.go
@@ -119,9 +119,29 @@ func (st *TOStatus) Read(h http.Header, useIMS bool) ([]interface{}, error, erro
 	return readVals, nil, nil, errCode, maxTime
 }
 
-func (st *TOStatus) Update(h http.Header) (error, error, int) { return api.GenericUpdate(h, st) }
-func (st *TOStatus) Create() (error, error, int)              { return api.GenericCreate(st) }
-func (st *TOStatus) Delete() (error, error, int)              { return api.GenericDelete(st) }
+func (st *TOStatus) Update(h http.Header) (error, error, int) {
+	var statusName string
+	err := st.APIInfo().Tx.QueryRow(`SELECT name from status WHERE id = $1`, *st.ID).Scan(&statusName)
+	if err != nil {
+		return nil, fmt.Errorf("error querying status name from ID: %w", err), http.StatusInternalServerError
+	}
+	if tc.IsReservedStatus(statusName) {
+		return fmt.Errorf("cannot modify %s status", statusName), nil, http.StatusForbidden
+	}
+	return api.GenericUpdate(h, st)
+}
+func (st *TOStatus) Create() (error, error, int) { return api.GenericCreate(st) }
+func (st *TOStatus) Delete() (error, error, int) {
+	var statusName string
+	err := st.APIInfo().Tx.QueryRow(`SELECT name from status WHERE id = $1`, *st.ID).Scan(&statusName)
+	if err != nil {
+		return nil, fmt.Errorf("error querying status name from ID: %w", err), http.StatusInternalServerError
+	}
+	if tc.IsReservedStatus(statusName) {
+		return fmt.Errorf("cannot delete %s status", statusName), nil, http.StatusForbidden
+	}
+	return api.GenericDelete(st)
+}
 
 func selectQuery() string {
 	return `