Skip to content
This repository was archived by the owner on Nov 24, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ad4c170
Reorder objects to be consistent with other tests
zrhoffman Jul 19, 2020
851ffe0
When the status of a server changes, queue updates on servers within
zrhoffman Jul 21, 2020
43d3d8a
Reformat update_status query for readability and add language injection
zrhoffman Jul 6, 2020
bf6782d
Add legacy severs{host_name}/update_status handler to continue
zrhoffman Jul 21, 2020
f7e3b7e
When the status of a server in an ancestor topology cachegroup changes.
zrhoffman Jul 21, 2020
6407610
Separate API v1 and v2 handlers
zrhoffman Jul 24, 2020
e583203
Remove unused function queueUpdatesOnChildNodes()
zrhoffman Jul 30, 2020
f97f121
Comment Postgres queries
zrhoffman Jul 30, 2020
94d4d98
Allow mids to be child cachegroups in TO API v3
zrhoffman Jul 30, 2020
2ea1793
Make GoDoc a complete sentence
zrhoffman Jul 30, 2020
1c6326a
log.Error.Printf() -> log.Errorf()
zrhoffman Jul 30, 2020
73ff6a1
Add function name to error message for context
zrhoffman Jul 30, 2020
29c188b
serverUpdateStatus -> us for query result scanning
zrhoffman Jul 30, 2020
b8ddc95
Bind rows and err at their first usage instead of predeclaring
zrhoffman Jul 30, 2020
5ef79b2
Use tc.CacheStatusOffline instead of hard-coded 'OFFLINE'
zrhoffman Jul 30, 2020
ccb4194
Use tc.UseRevalPendingParameterName instead of hard-coded 'use_reval_…
zrhoffman Jul 30, 2020
b1e9124
Use tc.GlobalConfigFileName instead of hard-coded 'global'
zrhoffman Jul 30, 2020
fe0fc4e
Update test SQL regex to reference parameter
zrhoffman Jul 30, 2020
8f9914f
Rename function to getServerUpdateStatusV1() since it is called from
zrhoffman Jul 31, 2020
c3d4a4c
Include prepared statement parameters in legacy query commands
zrhoffman Jul 31, 2020
f1da3ab
Filter out server's cachegroup, not just the server itself
zrhoffman Jul 31, 2020
f99605c
Fix parent upd_pending to return an accurate value if topology and
zrhoffman Aug 3, 2020
7809db6
Revalidation, not updates
zrhoffman Aug 3, 2020
14f17f4
whitespace
zrhoffman Aug 3, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
- Traffic Ops: Added support for `topology` query parameter to `GET /api/3.0/deliveryservices` to return all delivery services that employ a given topology.
- Traffic Ops: Added new topology-based delivery service fields for header rewrites: `firstHeaderRewrite`, `innerHeaderRewrite`, `lastHeaderRewrite`
- Traffic Ops: Added validation to prohibit assigning caches to topology-based delivery services
- Traffic Ops: Consider Topologies parentage when queueing or checking server updates
- Traffic Portal: Added the ability to create, read, update and delete flexible topologies.
- Traffic Portal: Added the ability to assign topologies to delivery services.
- Traffic Portal: Added the ability to view all delivery services and cache groups associated with a topology.
Expand Down
4 changes: 2 additions & 2 deletions docs/source/api/v3/servers_hostname_update_status.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ Each object in the returned array\ [1]_ will contain the following fields:

:host_id: The integral, unique identifier for the server for which the other fields in this object represent the pending updates and revalidation status
:host_name: The (short) hostname of the server for which the other fields in this object represent the pending updates and revalidation status
:parent_pending: A boolean telling whether or not the :term:`parents` of this server have pending updates
:parent_reval_pending: A boolean telling whether or not the :term:`parents` of this server have pending revalidation jobs
:parent_pending: A boolean telling whether or not any :ref:`Topology` ancestor or :term:`parent` of this server has pending updates
:parent_reval_pending: A boolean telling whether or not any :ref:`Topology` ancestor or :term:`parent` of this server has pending revalidation jobs
:reval_pending: ``true`` if the server has pending revalidation jobs, ``false`` otherwise
:status: The name of the status of this server

Expand Down
2 changes: 1 addition & 1 deletion docs/source/api/v3/servers_id_status.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

``PUT``
=======
Updates server status and queues updates on all child caches if server type is EDGE or MID. Also, captures offline reason if status is set to ADMIN_DOWN or OFFLINE and prepends offline reason with the user that initiated the status change.
Updates server status and queues updates on all descendant :ref:`Topology` nodes or child caches if server type is EDGE or MID. Also, captures offline reason if status is set to ADMIN_DOWN or OFFLINE and prepends offline reason with the user that initiated the status change.

:Auth. Required: Yes
:Roles Required: "admin" or "operations"
Expand Down
12 changes: 12 additions & 0 deletions lib/go-tc/enum.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,18 @@ const (

const GlobalProfileName = "GLOBAL"

// ParameterName represents the name of a Traffic Ops parameter meant to belong in a Traffic Ops config file.
type ParameterName string

// UseRevalPendingParameterName is the name of a parameter which tells whether or not Traffic Ops should use pending revalidation jobs.
const UseRevalPendingParameterName = ParameterName("use_reval_pending")

// ConfigFileName represents the name of a Traffic Ops config file.
type ConfigFileName string

// GlobalConfigFileName is the name of the global Traffic Ops config file.
const GlobalConfigFileName = ConfigFileName("global")

func (c CacheName) String() string {
return string(c)
}
Expand Down
21 changes: 21 additions & 0 deletions traffic_ops/client/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,27 @@ func (to *Session) GetServers(params *url.Values, header http.Header) (tc.Server
return data, reqInf, err
}

// GetFirstServer returns the first server in a servers GET response.
// If no servers match, an error is returned.
// The 'params' parameter can be used to optionally pass URL "query string
// parameters" in the request.
// It returns, in order, the API response that Traffic Ops returned, a request
// info object, and any error that occurred.
func (to *Session) GetFirstServer(params *url.Values, header http.Header) (tc.ServerNullable, ReqInf, error) {
serversResponse, reqInf, err := to.GetServers(params, header)
var firstServer tc.ServerNullable
if err != nil || reqInf.StatusCode == http.StatusNotModified {
return firstServer, reqInf, err
}
for _, firstServer = range serversResponse.Response {
return firstServer, reqInf, err
}

err = fmt.Errorf("unable to find server matching params %v", *params)
return firstServer, reqInf, err
}


// GetServerDetailsByHostName GETs Servers by the Server hostname.
func (to *Session) GetServerDetailsByHostName(hostName string, header http.Header) ([]tc.ServerDetailV30, ReqInf, error) {
v := url.Values{}
Expand Down
109 changes: 108 additions & 1 deletion traffic_ops/testing/api/v3/serverupdatestatus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"
"net/http"
"net/url"
"strconv"
"testing"

"github.com/apache/trafficcontrol/lib/go-tc"
Expand Down Expand Up @@ -196,7 +197,7 @@ func TestServerUpdateStatus(t *testing.T) {
}

func TestServerQueueUpdate(t *testing.T) {
WithObjs(t, []TCObj{Divisions, Regions, PhysLocations, Statuses, Types, CacheGroups, CDNs, Profiles, Servers}, func() {
WithObjs(t, []TCObj{CDNs, Types, Profiles, Statuses, Divisions, Regions, PhysLocations, CacheGroups, Servers}, func() {
// TODO: DON'T hard-code server hostnames!
const serverName = "atlanta-edge-01"

Expand Down Expand Up @@ -377,3 +378,109 @@ func TestSetServerUpdateStatuses(t *testing.T) {
}
})
}

func TestSetTopologiesServerUpdateStatuses(t *testing.T) {
WithObjs(t, []TCObj{CDNs, Types, Parameters, Profiles, Statuses, Divisions, Regions, PhysLocations, CacheGroups, Servers, Topologies}, func() {
const (
topologyName = "forked-topology"
edgeCacheGroup = "topology-edge-cg-01"
otherEdgeCacheGroup = "topology-edge-cg-02"
midCacheGroup = "topology-mid-cg-04"
)
cacheGroupNames := []string{edgeCacheGroup, otherEdgeCacheGroup, midCacheGroup}
cachesByCacheGroup := map[string]tc.ServerNullable{}
updateStatusByCacheGroup := map[string]tc.ServerUpdateStatus{}

forkedTopology, _, err := TOSession.GetTopology(topologyName, nil)
if err != nil {
t.Fatalf("topology %s was not found", topologyName)
}
for _, cacheGroupName := range cacheGroupNames {
foundNode := false
for _, node := range forkedTopology.Nodes {
if node.Cachegroup == cacheGroupName {
foundNode = true
break
}
}
if !foundNode {
t.Fatalf("unable to find topology node with cachegroup %s", cacheGroupName)
}

cacheGroups, _, err := TOSession.GetCacheGroupNullableByName(cacheGroupName, nil)
if err != nil {
t.Fatalf("unable to get cachegroup %s: %s", cacheGroupName, err.Error())
}
if len(cacheGroups) != 1 {
t.Fatalf("incorrect number of cachegroups. expected: %d actual: %d", 1, len(cacheGroups))
}
cacheGroup := cacheGroups[0]

params := url.Values{"cachegroup": []string{strconv.Itoa(*cacheGroup.ID)}}
cachesByCacheGroup[cacheGroupName], _, err = TOSession.GetFirstServer(&params, nil)
if err != nil {
t.Fatalf("unable to get a server from cachegroup %s: %s", cacheGroupName, err.Error())
}
}

// update status of MID server to OFFLINE
_, _, err = TOSession.UpdateServerStatus(*cachesByCacheGroup[midCacheGroup].ID, tc.ServerPutStatus{
Status: util.JSONNameOrIDStr{Name: util.StrPtr("OFFLINE")},
OfflineReason: util.StrPtr("testing")})
if err != nil {
t.Fatalf("cannot update server status: %s", err.Error())
}

for _, cacheGroupName := range cacheGroupNames {
params := url.Values{"cachegroup": []string{strconv.Itoa(*cachesByCacheGroup[cacheGroupName].CachegroupID)}}
cachesByCacheGroup[cacheGroupName], _, err = TOSession.GetFirstServer(&params, nil)
if err != nil {
t.Fatalf("unable to get a server from cachegroup %s: %s", cacheGroupName, err.Error())
}
}
for _, cacheGroupName := range cacheGroupNames {
updateStatusByCacheGroup[cacheGroupName], _, err = TOSession.GetServerUpdateStatus(*cachesByCacheGroup[cacheGroupName].HostName, nil)
if err != nil {
t.Fatalf("unable to get a server from cachegroup %s: %s", cacheGroupName, err.Error())
}
}
// updating the server status does not queue updates within the same cachegroup
if *cachesByCacheGroup[midCacheGroup].UpdPending {
t.Fatalf("expected UpdPending: %t, actual: %t", false, *cachesByCacheGroup[midCacheGroup].UpdPending)
}
// edgeCacheGroup is a descendant of midCacheGroup
if !*cachesByCacheGroup[edgeCacheGroup].UpdPending {
t.Fatalf("expected UpdPending: %t, actual: %t", true, *cachesByCacheGroup[edgeCacheGroup].UpdPending)
}
if !updateStatusByCacheGroup[edgeCacheGroup].UpdatePending {
t.Fatalf("expected UpdPending: %t, actual: %t", true, updateStatusByCacheGroup[edgeCacheGroup].UpdatePending)
}
// otherEdgeCacheGroup is not a descendant of midCacheGroup but is still in the same topology
if *cachesByCacheGroup[otherEdgeCacheGroup].UpdPending {
t.Fatalf("expected UpdPending: %t, actual: %t", false, *cachesByCacheGroup[otherEdgeCacheGroup].UpdPending)
}
if updateStatusByCacheGroup[otherEdgeCacheGroup].UpdatePending {
t.Fatalf("expected UpdPending: %t, actual: %t", false, updateStatusByCacheGroup[otherEdgeCacheGroup].UpdatePending)
}

_, _, err = TOSession.SetServerQueueUpdate(*cachesByCacheGroup[midCacheGroup].ID, true)
if err != nil {
t.Fatalf("cannot update server status on %s: %s", *cachesByCacheGroup[midCacheGroup].HostName, err.Error())
}
for _, cacheGroupName := range cacheGroupNames {
updateStatusByCacheGroup[cacheGroupName], _, err = TOSession.GetServerUpdateStatus(*cachesByCacheGroup[cacheGroupName].HostName, nil)
if err != nil {
t.Fatalf("unable to get a server from cachegroup %s: %s", cacheGroupName, err.Error())
}
}

// edgeCacheGroup is a descendant of midCacheGroup
if !updateStatusByCacheGroup[edgeCacheGroup].ParentPending {
t.Fatalf("expected UpdPending: %t, actual: %t", true, updateStatusByCacheGroup[edgeCacheGroup].ParentPending)
}
// otherEdgeCacheGroup is not a descendant of midCacheGroup but is still in the same topology
if updateStatusByCacheGroup[otherEdgeCacheGroup].ParentPending {
t.Fatalf("expected UpdPending: %t, actual: %t", false, updateStatusByCacheGroup[otherEdgeCacheGroup].ParentPending)
}
})
}
Loading