blob: e062622decb651c72f4ca7db5d87db549c6fff94 [file] [log] [blame]
package v4
/*
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 (
"net/http"
"testing"
"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/lib/go-util"
"github.com/apache/trafficcontrol/lib/go-util/assert"
"github.com/apache/trafficcontrol/traffic_ops/testing/api/utils"
"github.com/apache/trafficcontrol/traffic_ops/toclientlib"
client "github.com/apache/trafficcontrol/traffic_ops/v4-client"
)
func TestServersIDStatus(t *testing.T) {
WithObjs(t, []TCObj{CDNs, Types, Tenants, Parameters, Profiles, Statuses, Divisions, Regions, PhysLocations, CacheGroups, Servers, ServiceCategories, Topologies, DeliveryServices, DeliveryServiceServerAssignments}, func() {
methodTests := utils.TestCase[client.Session, client.RequestOptions, tc.ServerPutStatus]{
"PUT": {
"OK when VALID request": {
EndpointID: GetServerID(t, "atlanta-mid-01"),
ClientSession: TOSession,
RequestBody: tc.ServerPutStatus{
Status: util.JSONNameOrIDStr{ID: util.Ptr(GetStatusID(t, "OFFLINE")())},
OfflineReason: util.Ptr("test mid"),
},
Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), validateUpdPending("atlanta-mid-01")),
},
"OK when using STATUS ID FIELD": {
EndpointID: GetServerID(t, "atlanta-mid-16"),
ClientSession: TOSession,
RequestBody: tc.ServerPutStatus{
Status: util.JSONNameOrIDStr{ID: util.Ptr(GetStatusID(t, "OFFLINE")())},
OfflineReason: util.Ptr("test mid"),
},
Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), validateUpdPending("atlanta-mid-16")),
},
"VALIDATE TOPOLOGY DESCENDANTS receive STATUS UPDATES": {
EndpointID: GetServerID(t, "topology-mid-04"),
ClientSession: TOSession,
RequestBody: tc.ServerPutStatus{
Status: util.JSONNameOrIDStr{ID: util.Ptr(GetStatusID(t, "OFFLINE")())},
OfflineReason: util.Ptr("test topology mid cachegroup"),
},
Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK),
validateUpdPendingSpecificServers(map[string]bool{"topology-mid-04": false, "midInTopologyMidCg04": false,
"topology-edge-01": true, "edgeInTopologyEdgeCg01": false, "topology-edge-02": false, "edgeInTopologyEdgeCg02": false}),
validateParentPendingSpecificServers(map[string]bool{"topology-edge-01": true, "edgeInTopologyEdgeCg02": false})),
},
"NOT FOUND when SERVER DOESNT EXIST": {
EndpointID: func() int { return 11111111 },
ClientSession: TOSession,
RequestBody: tc.ServerPutStatus{
Status: util.JSONNameOrIDStr{Name: util.Ptr("OFFLINE")},
OfflineReason: util.Ptr("test last edge"),
},
Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusNotFound)),
},
"BAD REQUEST when STATUS DOESNT EXIST": {
EndpointID: GetServerID(t, "atlanta-mid-16"),
ClientSession: TOSession,
RequestBody: tc.ServerPutStatus{
Status: util.JSONNameOrIDStr{Name: util.Ptr("NOT_A_REAL_STATUS")},
OfflineReason: util.Ptr("test last edge"),
},
Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
},
"BAD REQUEST when MISSING OFFLINE REASON when OFFLINE STATUS": {
EndpointID: GetServerID(t, "atlanta-mid-16"),
ClientSession: TOSession,
RequestBody: tc.ServerPutStatus{
Status: util.JSONNameOrIDStr{Name: util.Ptr("OFFLINE")},
},
Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
},
"BAD REQUEST when MISSING OFFLINE REASON when ADMIN_DOWN STATUS": {
EndpointID: GetServerID(t, "atlanta-mid-16"),
ClientSession: TOSession,
RequestBody: tc.ServerPutStatus{
Status: util.JSONNameOrIDStr{Name: util.Ptr("ADMIN_DOWN")},
},
Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
},
"CONFLICT when SERVER STATUS OFFLINE when ONLY EDGE SERVER ASSIGNED": {
EndpointID: GetServerID(t, "test-ds-server-assignments"),
ClientSession: TOSession,
RequestBody: tc.ServerPutStatus{
Status: util.JSONNameOrIDStr{Name: util.Ptr("OFFLINE")},
OfflineReason: util.Ptr("test last edge"),
},
Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusConflict)),
},
"CONFLICT when SERVER STATUS OFFLINE when ONLY ORIGIN SERVER ASSIGNED": {
EndpointID: GetServerID(t, "test-mso-org-01"),
ClientSession: TOSession,
RequestBody: tc.ServerPutStatus{
Status: util.JSONNameOrIDStr{Name: util.Ptr("OFFLINE")},
OfflineReason: util.Ptr("test last origin"),
},
Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusConflict)),
},
},
}
for method, testCases := range methodTests {
t.Run(method, func(t *testing.T) {
for name, testCase := range testCases {
switch method {
case "PUT":
t.Run(name, func(t *testing.T) {
clearUpdates(t)
alerts, reqInf, err := testCase.ClientSession.UpdateServerStatus(testCase.EndpointID(), testCase.RequestBody, testCase.RequestOpts)
for _, check := range testCase.Expectations {
check(t, reqInf, nil, alerts, err)
}
})
}
}
})
}
})
}
func validateUpdPending(hostName string) utils.CkReqFunc {
return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) {
descendants := make(map[string]struct{})
opts := client.NewRequestOptions()
opts.QueryParameters.Set("hostName", hostName)
servers, _, err := TOSession.GetServers(opts)
assert.RequireNoError(t, err, "Expected no error when getting servers: %v", err)
assert.RequireEqual(t, 1, len(servers.Response), "Expected exactly one server returned from response, Got: %d", len(servers.Response))
updatedServer := servers.Response[0]
assert.RequireNotNil(t, updatedServer.CachegroupID, "Expected Server's CachegroupID to NOT be nil.")
assert.RequireNotNil(t, updatedServer.Cachegroup, "Expected Server's Cachegroup to NOT be nil.")
opts.QueryParameters.Del("hostName")
cacheGroups, _, err := TOSession.GetCacheGroups(opts)
assert.RequireNoError(t, err, "Expected no error when getting cache groups: %v", err)
for _, cacheGroup := range cacheGroups.Response {
if cacheGroup.ParentCachegroupID != nil {
if *cacheGroup.ParentCachegroupID == *servers.Response[0].CachegroupID {
assert.RequireNotNil(t, cacheGroup.Name, "Expected Cachegroup's Name to NOT be nil.")
descendants[*cacheGroup.Name] = struct{}{}
if cacheGroup.SecondaryParentCachegroupID != nil {
assert.RequireNotNil(t, cacheGroup.SecondaryParentName, "Expected Cachegroup's Secondary Parent's Name to NOT be nil.")
descendants[*cacheGroup.SecondaryParentName] = struct{}{}
}
}
}
if cacheGroup.SecondaryParentCachegroupID != nil {
if *cacheGroup.SecondaryParentCachegroupID == *servers.Response[0].CachegroupID {
assert.RequireNotNil(t, cacheGroup.Name, "Expected Cachegroup's Name to NOT be nil.")
descendants[*cacheGroup.Name] = struct{}{}
}
}
}
allServers, _, err := TOSession.GetServers(opts)
assert.RequireNoError(t, err, "Expected no error when getting servers: %v", err)
for _, server := range allServers.Response {
assert.RequireNotNil(t, server.HostName, "Expected Hostname to NOT be nil.")
assert.RequireNotNil(t, server.Cachegroup, "Expected Cachegroup to NOT be nil.")
assert.RequireNotNil(t, server.UpdPending, "Expected UpdPending to NOT be nil.")
_, ok := descendants[*server.Cachegroup]
if ok && *server.CDNName == *updatedServer.CDNName {
assert.Equal(t, true, *server.UpdPending, "Expected server %s with cachegroup %s to have updates pending.", *server.HostName, *server.Cachegroup)
} else {
assert.Equal(t, false, *server.UpdPending, "Expected server %s with cachegroup %s to NOT have updates pending.", *server.HostName, *server.Cachegroup)
}
}
}
}
func clearUpdates(t *testing.T) {
cdns, _, err := TOSession.GetCDNs(client.RequestOptions{})
assert.RequireNoError(t, err, "Error getting CDNs: %v", err)
for _, cdn := range cdns.Response {
_, _, err := TOSession.QueueUpdatesForCDN(cdn.ID, false, client.RequestOptions{})
assert.RequireNoError(t, err, "Error Dequeing Updates for CDN %s: %v", cdn.Name, err)
}
}
func validateUpdPendingSpecificServers(expected map[string]bool) utils.CkReqFunc {
return func(t *testing.T, _ toclientlib.ReqInf, _ interface{}, _ tc.Alerts, _ error) {
for expectedServer, expectedUpdPending := range expected {
resp, _, err := TOSession.GetServerUpdateStatus(expectedServer, client.RequestOptions{})
assert.RequireNoError(t, err, "Expected no error when getting server's update status: %v", err)
assert.RequireEqual(t, 1, len(resp.Response), "Expected exactly one server's update status returned from response, Got: %d", len(resp.Response))
actualUpdPending := resp.Response[0].UpdatePending
assert.Equal(t, expectedUpdPending, actualUpdPending, "Expected Update Pending for server: %s to be %t, Got: %t", expectedServer, expectedUpdPending, actualUpdPending)
}
}
}
func validateParentPendingSpecificServers(expected map[string]bool) utils.CkReqFunc {
return func(t *testing.T, _ toclientlib.ReqInf, _ interface{}, _ tc.Alerts, _ error) {
for expectedServer, expectedParentPending := range expected {
resp, _, err := TOSession.GetServerUpdateStatus(expectedServer, client.RequestOptions{})
assert.RequireNoError(t, err, "Expected no error when getting server's update status: %v", err)
assert.RequireEqual(t, 1, len(resp.Response), "Expected exactly one server's update status returned from response, Got: %d", len(resp.Response))
actualParentPending := resp.Response[0].ParentPending
assert.Equal(t, expectedParentPending, actualParentPending, "Expected Parent Pending for server: %s to be %t, Got: %t", expectedServer, expectedParentPending, actualParentPending)
}
}
}