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 <[email protected]>
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 `

Reply via email to