ocket8888 commented on code in PR #6817:
URL: https://github.com/apache/trafficcontrol/pull/6817#discussion_r869458440
##########
cache-config/testing/ort-tests/tcdata/statuses.go:
##########
@@ -19,6 +19,14 @@ import (
"testing"
)
+var statusNameMap = map[string]bool{
Review Comment:
I see this map repeated in 5 different places in this PR. I think the reason
you're doing that is because it's mutable so exposing it as an exported member
of `lib/go-tc` could be problematic. But functions are immutable, so you could
just have one function in the lib that does this instead of repeating it 5
times:
```go
// IsReservedStatus checks if the given status name is reserved; i.e. may
not be
// deleted or modified.
func IsReservedStatus(status string) bool {
switch (status) {
case CacheStatusAdminDown:
fallthrough
case CacheStatusOffline:
fallthrough
case CacheStatusOnline:
fallthrough
case CacheStatusPreProd:
fallthrough
case CacheStatusReported:
return true
}
return false
}
```
##########
cache-config/testing/ort-tests/tcdata/statuses.go:
##########
@@ -46,17 +54,43 @@ func (r *TCData) DeleteTestStatuses(t *testing.T) {
respStatus := resp[0]
delResp, _, err := TOSession.DeleteStatusByID(respStatus.ID)
- if err != nil {
- t.Errorf("cannot DELETE Status by name: %v - %v", err,
delResp)
- }
+ if _, ok := statusNameMap[*status.Name]; !ok {
+ 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)
+ // 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)
+ }
+ } else {
+ if err == nil {
+ t.Errorf("expected an error while trying to
delete a reserved status, but got nothing")
+ }
}
}
+ r.ForceDeleteStatuses(t)
+}
+
+// ForceDeleteStatuses forcibly deletes the statuses from the db.
+func (r *TCData) ForceDeleteStatuses(t *testing.T) {
Review Comment:
Instead of doing this, we should just treat the statuses as immutable, which
they now are, and not create an invalid Traffic Ops instance by deleting them.
That means removing the Statuses which already exist in every valid Traffic Ops
instance from the testing data "fixtures".
##########
traffic_ops/app/db/migrations/2022050916074300_add_reserved_statuses.down.sql:
##########
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+DELETE FROM public.status WHERE name IN ('ONLINE', 'OFFLINE', 'REPORTED',
'ADMIN_DOWN', 'PRE_PROD');
Review Comment:
This will break existing TO installations. If you have data unchanged from
seeds.sql and then try to upgrade with this migration, then downgrade back,
first of all it will probably fail to roll back because there will almost
certainly be servers using at least one of these statuses, but then also your
data will not be left in the same state as it was pre-upgrade.
This file should be blank. In the worst case scenario, that would mean
downgrading creates statuses that need to exist for ATC to work, so there's
only benefit.
##########
traffic_ops/testing/api/v2/statuses_test.go:
##########
@@ -42,35 +50,44 @@ func CreateTestStatuses(t *testing.T) {
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)
- }
- 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)
+ if len(testData.Statuses) < 1 {
+ t.Fatal("Need at least one Status to test updating a Status")
}
- 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: %v - %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 _, ok := statusNameMap[*status.Name]; ok {
+ 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: %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",
status.Description, err)
Review Comment:
Same as above; `status.Description` is a `*string`.
##########
cache-config/testing/ort-tests/tcdata/statuses.go:
##########
@@ -46,17 +54,43 @@ func (r *TCData) DeleteTestStatuses(t *testing.T) {
respStatus := resp[0]
delResp, _, err := TOSession.DeleteStatusByID(respStatus.ID)
- if err != nil {
- t.Errorf("cannot DELETE Status by name: %v - %v", err,
delResp)
- }
+ if _, ok := statusNameMap[*status.Name]; !ok {
+ 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)
+ // 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)
+ }
+ } else {
+ if err == nil {
+ t.Errorf("expected an error while trying to
delete a reserved status, but got nothing")
+ }
}
Review Comment:
nit:
```go
...
} else {
if condition {
...
}
}
```
is equivalent to the shorter
```go
...
} else if condition {
...
}
##########
traffic_ops/traffic_ops_golang/status/statuses.go:
##########
@@ -119,9 +127,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:
%v", err), http.StatusInternalServerError
Review Comment:
errors constructed from errors should wrap with `%w`.
##########
traffic_ops/testing/api/v2/statuses_test.go:
##########
@@ -42,35 +50,44 @@ func CreateTestStatuses(t *testing.T) {
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)
- }
- 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)
+ if len(testData.Statuses) < 1 {
+ t.Fatal("Need at least one Status to test updating a Status")
}
- 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: %v - %v",
status.Name, err)
Review Comment:
`status.Name` is a pointer to a string, so using `%v` to format it is going
to not be very helpful since [`%v` formats `*string`s as memory
addresses](https://go.dev/play/p/YEGLp3CQe7x). This is why types should always
use the most specific formatting parameter they can; if you use `%s` here it
will fail `go vet` and thus the test won't build, which lets you know
immediately that it's not printing what it should be.
##########
traffic_ops/testing/api/v2/statuses_test.go:
##########
@@ -106,17 +123,43 @@ func DeleteTestStatuses(t *testing.T) {
respStatus := resp[0]
delResp, _, err := TOSession.DeleteStatusByID(respStatus.ID)
- if err != nil {
- t.Errorf("cannot DELETE Status by name: %v - %v", err,
delResp)
+ if _, ok := statusNameMap[*status.Name]; !ok {
+ 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: %s",
err.Error())
Review Comment:
nit: `.Error()` is redundant because `%s` formats errors as strings by
calling their `Error` method.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]