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

shamrick 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 b9cdfd5b9d Remove `/servers/details` from APIv4 (#6819)
b9cdfd5b9d is described below

commit b9cdfd5b9d150a17437c4c22d6cbdcd0a26066cb
Author: ocket8888 <[email protected]>
AuthorDate: Tue May 17 15:25:17 2022 -0600

    Remove `/servers/details` from APIv4 (#6819)
    
    * Add deprecation notices, remove v4 handling, fix #6800
    
    * Update documentation - add deprecation notices to v3, remove from v4
    
    * Update clients - remove from v4, add deprecation notices to v3
    
    * Update CHANGELOG
    
    * fix unable to get server details by hostname if physloc not also provided
    
    * Fix embedded 'response' object
---
 CHANGELOG.md                                       |   4 +-
 docs/source/api/v3/servers_details.rst             |   3 +
 docs/source/api/v4/servers_details.rst             | 192 ---------------------
 .../clients/python/trafficops/tosession.py         |   7 +-
 traffic_ops/testing/api/v4/servers_test.go         |  33 ----
 traffic_ops/traffic_ops_golang/routing/routes.go   |   3 -
 traffic_ops/traffic_ops_golang/server/detail.go    | 175 +++++++++++--------
 traffic_ops/v3-client/server.go                    |   4 +
 traffic_ops/v4-client/server.go                    |  11 --
 9 files changed, 114 insertions(+), 318 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 768c04969c..e8e85c9a58 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,11 +17,10 @@ The format is based on [Keep a 
Changelog](http://keepachangelog.com/en/1.0.0/).
 - Traffic Monitor: Add support for `access.log` to TM.
 - Added functionality for login to provide a Bearer token and for that token 
to be later used for authorization.
 - [Traffic Ops] Added support for backend configurations so that Traffic Ops 
can act as a reverse proxy for these services 
[#6754](https://github.com/apache/trafficcontrol/pull/6754).
-- Added functionality for CDN locks, so that they can be shared amongst a list 
of specified usernames. 
+- Added functionality for CDN locks, so that they can be shared amongst a list 
of specified usernames.
 - [Traffic Ops | Traffic Go Clients | T3C] Add additional timestamp fields to 
server for queuing and dequeueing config and revalidate updates.
 - Added layered profile feature to 4.0 for `GET` /servers/, `POST` /servers/, 
`PUT` /servers/{id} and `DELETE` /servers/{id}.
 - Added a Traffic Ops endpoint and Traffic Portal page to view all CDNi 
configuration update requests and approve or deny.
-- Added layered profile feature to 4.0 for `GET` /servers/details.
 - Added layered profile feature to 4.0 for `GET` 
/deliveryservices/{id}/servers/ and /deliveryservices/{id}/servers/eligible.
 
 ### Fixed
@@ -57,6 +56,7 @@ The format is based on [Keep a 
Changelog](http://keepachangelog.com/en/1.0.0/).
 - Replace `forever` with `pm2` for process management of the traffic portal 
node server to remediate security issues.
 - Removed the Traffic Monitor `peer_polling_protocol` option. Traffic Monitor 
now just uses hostnames to request peer states, which can be handled via IPv4 
or IPv6 depending on the underlying IP version in use.
 - Dropped CentOS 8 support
+- The `/servers/details` endpoint of the Traffic Ops API has been dropped in 
version 4.0, and marked deprecated in earlier versions.
 
 ### Changed
 - [#6654](https://github.com/apache/trafficcontrol/issues/6654) Traffic 
Monitor now uses the TO API 4.0 by default and falls back to 3.1
diff --git a/docs/source/api/v3/servers_details.rst 
b/docs/source/api/v3/servers_details.rst
index 4dfe2e96ec..f1e746aa45 100644
--- a/docs/source/api/v3/servers_details.rst
+++ b/docs/source/api/v3/servers_details.rst
@@ -20,6 +20,9 @@
 *******************
 Retrieves details of :ref:`tp-configure-servers`.
 
+.. deprecated:: 3.1
+       This endpoint has been removed from the latest version of the API, and 
clients are advised to use :ref:`to-api-v3-servers` instead.
+
 
 ``GET``
 =======
diff --git a/docs/source/api/v4/servers_details.rst 
b/docs/source/api/v4/servers_details.rst
deleted file mode 100644
index dc0a917b50..0000000000
--- a/docs/source/api/v4/servers_details.rst
+++ /dev/null
@@ -1,192 +0,0 @@
-..
-..
-.. 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.
-..
-
-.. _to-api-servers-details:
-
-*******************
-``servers/details``
-*******************
-Retrieves details of :ref:`tp-configure-servers`.
-
-
-``GET``
-=======
-:Auth. Required: Yes
-:Roles Required: None
-:Permissions Required: SERVER:READ, DELIVERY-SERVICE:READ, CDN:READ, 
PHYSICAL-LOCATION:READ, CACHE-GROUP:READ, TYPE:READ, PROFILE:READ
-:Response Type:  Array
-
-.. note:: On top of the response including the response key that is of type 
array it will also include the keys ``limit``, ``orderby``, and ``size``.
-
-Request Structure
------------------
-.. table:: Request Query Parameters
-
-       
+----------------+----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------+
-       | Name           | Required                               | Description 
                                                                                
                                                                   |
-       
+================+========================================+================================================================================================================================================================+
-       | hostName       | Required if no physLocationID provided | Return only 
the servers with this (short) hostname. Capitalization of "hostName" is 
important.                                                                 |
-       
+----------------+----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------+
-       | physLocationID | Required if no hostName provided       | Return only 
servers with this integral, unique identifier for the physical location where 
the server resides. Capitalization of "physLocationID" is important. |
-       
+----------------+----------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------+
-
-.. code-block:: http
-       :caption: Request Example
-
-       GET /api/4.0/servers/details?hostName=edge HTTP/1.1
-       User-Agent: python-requests/2.22.0
-       Accept-Encoding: gzip, deflate
-       Accept: */*
-       Connection: keep-alive
-       Cookie: mojolicious=...
-
-Response Structure
-------------------
-:limit:         The maximum size of the ``response`` array, also indicative of 
the number of results per page using the pagination requested by the query 
parameters (if any) - this should be the same as the ``limit`` query parameter 
(if given)
-:orderby:       A string that names the field by which the elements of the 
``response`` array are ordered - should be the same as the ``orderby`` request 
query parameter (if given)
-:response:      An array of objects, each of which represents the details of a 
given :ref:`Server <tp-configure-servers>`.
-
-       :cachegroup:            A string that is the :ref:`name of the Cache 
Group <cache-group-name>` to which the server belongs
-       :cdnName:               Name of the CDN to which the server belongs
-       :deliveryservices:      An array of integral, unique identifiers for 
:term:`Delivery Services` to which this server belongs
-       :domainName:            The domain part of the server's :abbr:`FQDN 
(Fully Qualified Domain Name)`
-       :guid:                  An identifier used to uniquely identify the 
server
-
-               .. note::       This is a legacy key which only still exists 
for compatibility reasons - it should always be ``null``
-
-       :hostName:              The (short) hostname of the server
-       :httpsPort:             The port on which the server listens for 
incoming HTTPS connections/requests
-       :id:                    An integral, unique identifier for this server
-       :iloIpAddress:          The IPv4 address of the server's :abbr:`ILO 
(Integrated Lights-Out)` service\ [1]_
-       :iloIpGateway:          The IPv4 gateway address of the server's 
:abbr:`ILO (Integrated Lights-Out)` service\ [1]_
-       :iloIpNetmask:          The IPv4 subnet mask of the server's :abbr:`ILO 
(Integrated Lights-Out)` service\ [1]_
-       :iloPassword:           The password of the of the server's :abbr:`ILO 
(Integrated Lights-Out)` service user\ [1]_ - displays as simply ``******`` if 
the currently logged-in user does not have the 'admin' or 'operations' 
:term:`Role(s) <Role>`
-       :iloUsername:           The user name for the server's :abbr:`ILO 
(Integrated Lights-Out)` service\ [1]_
-       :interfaces:     An array of interface and IP address information
-
-               :max_bandwidth:        The maximum allowed bandwidth for this 
interface to be considered "healthy" by Traffic Monitor. This has no effect if 
`monitor` is not true. Values are in kb/s. The value `0` means "no limit".
-               :monitor:              A boolean indicating if Traffic Monitor 
should monitor this interface
-               :mtu:                  The :abbr:`MTU (Maximum Transmission 
Unit)` to configure for ``interfaceName``
-
-                       .. seealso:: `The Wikipedia article on Maximum 
Transmission Unit <https://en.wikipedia.org/wiki/Maximum_transmission_unit>`_
-
-               :name:                      The network interface name used by 
the server.
-
-               :ipAddresses:          An array of the IP address information 
for the interface
-
-                       :address:          The IPv4 or IPv6 address and subnet 
mask of the server - applicable for the interface ``name``
-                       :gateway:          The IPv4 or IPv6 gateway address of 
the server - applicable for the interface ``name``
-                       :service_address:  A boolean determining if content 
will be routed to the IP address
-
-               :routerHostName:       The human-readable name of the router 
responsible for reaching this server's interface.
-               :routerPortName:       The human-readable name of the port used 
by the router responsible for reaching this server's interface.
-
-       :mgmtIpAddress:  The IPv4 address of the server's management port
-       :mgmtIpGateway:  The IPv4 gateway of the server's management port
-       :mgmtIpNetmask:  The IPv4 subnet mask of the server's management port
-       :offlineReason:         A user-entered reason why the server is in 
ADMIN_DOWN or OFFLINE status
-       :physLocation:          The name of the physical location where the 
server resides
-       :profileNames:          List of :ref:`profile-name` of the 
:term:`Profiles` used by this server
-       :rack:  A string indicating "server rack" location
-       :status:                The status of the server
-
-               .. seealso::    :ref:`health-proto`
-
-       :tcpPort: The port on which this server listens for incoming TCP 
connections
-
-               .. note::       This is typically thought of as synonymous with 
"HTTP port", as the port specified by ``httpsPort`` may also be used for 
incoming TCP connections.
-
-       :type:                  The name of the 'type' of this server
-       :xmppId:                A system-generated UUID used to generate a 
server hashId for use in Traffic Router's consistent hashing algorithm. This 
value is set when a server is created and cannot be changed afterwards.
-       :xmppPasswd:            The password used in XMPP communications with 
the server
-
-:size:          The page number - if pagination was requested in the query 
parameters, else ``0`` to indicate no pagination - of the results represented 
by the ``response`` array. This is named "size" for legacy reasons
-
-.. code-block:: http
-       :caption: Response Example
-
-       HTTP/1.1 200 OK
-       Access-Control-Allow-Credentials: true
-       Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, 
Accept, Set-Cookie, Cookie
-       Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE
-       Access-Control-Allow-Origin: *
-       Content-Encoding: gzip
-       Content-Type: application/json
-       Set-Cookie: mojolicious=...; Path=/; Expires=Mon, 24 Feb 2020 01:27:36 
GMT; Max-Age=3600; HttpOnly
-       Whole-Content-Sha512: 
HW2F3CEpohNAvNlEDhUfXmtwpEka4dwUWFVUSSjW98aXiv10vI6ysRIcC2P9huabCz5fdHqY3tp0LR4ekwEHqw==
-       X-Server-Name: traffic_ops_golang/
-       Date: Mon, 24 Feb 2020 00:27:36 GMT
-       Content-Length: 493
-
-       {
-               "limit": 1000,
-               "orderby": "hostName",
-               "response": [
-                       {
-                               "cachegroup": "CDN_in_a_Box_Edge",
-                               "cdnName": "CDN-in-a-Box",
-                               "deliveryservices": [
-                                       1
-                               ],
-                               "domainName": "infra.ciab.test",
-                               "guid": null,
-                               "hardwareInfo": null,
-                               "hostName": "edge",
-                               "httpsPort": 443,
-                               "id": 5,
-                               "iloIpAddress": "",
-                               "iloIpGateway": "",
-                               "iloIpNetmask": "",
-                               "iloPassword": "",
-                               "iloUsername": "",
-                               "mgmtIpAddress": "",
-                               "mgmtIpGateway": "",
-                               "mgmtIpNetmask": "",
-                               "offlineReason": "",
-                               "physLocation": "Apachecon North America 2018",
-                               "profileNames": ["ATS_EDGE_TIER_CACHE"],
-                               "rack": "",
-                               "status": "REPORTED",
-                               "tcpPort": 80,
-                               "type": "EDGE",
-                               "xmppId": "edge",
-                               "xmppPasswd": "",
-                               "interfaces": [
-                                       { "ipAddresses": [
-                                                       {
-                                                               "address": 
"172.16.239.100",
-                                                               "gateway": 
"172.16.239.1",
-                                                               
"service_address": true
-                                                       },
-                                                       {
-                                                               "address": 
"fc01:9400:1000:8::100",
-                                                               "gateway": 
"fc01:9400:1000:8::1",
-                                                               
"service_address": true
-                                                       }
-                                               ],
-                                               "max_bandwidth": 0,
-                                               "monitor": true,
-                                               "mtu": 1500,
-                                               "name": "eth0",
-                                               "routerHostName": "",
-                                               "routerPortName": ""
-                                       }
-                               ]
-                       }
-               ],
-               "size": 1
-       }
-
-.. [1] For more information see the `Wikipedia page on Lights-Out management 
<https://en.wikipedia.org/wiki/Out-of-band_management>`_\ .
diff --git a/traffic_control/clients/python/trafficops/tosession.py 
b/traffic_control/clients/python/trafficops/tosession.py
index f907911f04..150b51311d 100644
--- a/traffic_control/clients/python/trafficops/tosession.py
+++ b/traffic_control/clients/python/trafficops/tosession.py
@@ -1648,13 +1648,16 @@ class TOSession(RestApiSession):
                :raises: Union[LoginError, OperationError]
                """
 
-       @api_request('get', 'servers/details?hostName={name}', ('3.0','4.0',))
+       @api_request('get', 'servers/details?hostName={name}', ('3.0',))
        def get_server_details(self, name=None):
                """
                Get servers/details
-               :ref:`to-api-servers-details`
+               :ref:`to-api-v3-servers-details`
                :rtype: Tuple[Union[Dict[str, Any], List[Dict[str, Any]]], 
requests.Response]
                :raises: Union[LoginError, OperationError]
+
+               .. deprecated:: 3.0
+                       The endpoint this represents has been removed from 
APIv4 and clients should use get_servers instead.
                """
 
        @api_request('post', 'servercheck', ('3.0',))
diff --git a/traffic_ops/testing/api/v4/servers_test.go 
b/traffic_ops/testing/api/v4/servers_test.go
index 4adf0d8345..a6f1680a4d 100644
--- a/traffic_ops/testing/api/v4/servers_test.go
+++ b/traffic_ops/testing/api/v4/servers_test.go
@@ -42,7 +42,6 @@ func TestServers(t *testing.T) {
                header.Set(rfc.IfUnmodifiedSince, time)
                UpdateTestServers(t)
                UpdateTestServersWithHeaders(t, header)
-               GetTestServersDetails(t)
                GetTestServers(t)
                GetTestServersIMSAfterChange(t, header)
                GetTestServersQueryParameters(t)
@@ -657,38 +656,6 @@ func GetTestServers(t *testing.T) {
        }
 }
 
-func GetTestServersDetails(t *testing.T) {
-       opts := client.NewRequestOptions()
-       for _, server := range testData.Servers {
-               if server.HostName == nil {
-                       t.Errorf("found server with nil hostname: %+v", server)
-                       continue
-               }
-               opts.QueryParameters.Set("hostName", *server.HostName)
-               resp, _, err := TOSession.GetServersDetails(opts)
-               if err != nil {
-                       t.Errorf("cannot get Server Details: %v - alerts: %+v", 
err, resp.Alerts)
-               }
-               if len(resp.Response) == 0 {
-                       t.Fatal("no servers in response, quitting")
-               }
-               if len(resp.Response[0].ServerInterfaces) == 0 {
-                       t.Fatalf("no interfaces to check, quitting")
-               }
-               if len(server.Interfaces) == 0 {
-                       t.Fatalf("no interfaces to check, quitting")
-               }
-
-               // just check the first interface for noe
-               if resp.Response[0].ServerInterfaces[0].RouterHostName != 
server.Interfaces[0].RouterHostName {
-                       t.Errorf("expected router host name to be %s, but got 
%s", server.Interfaces[0].RouterHostName, 
resp.Response[0].ServerInterfaces[0].RouterHostName)
-               }
-               if resp.Response[0].ServerInterfaces[0].RouterPortName != 
server.Interfaces[0].RouterPortName {
-                       t.Errorf("expected router port to be %s, but got %s", 
server.Interfaces[0].RouterPortName, 
resp.Response[0].ServerInterfaces[0].RouterPortName)
-               }
-       }
-}
-
 func GetTestServersQueryParameters(t *testing.T) {
        dses, _, err := 
TOSession.GetDeliveryServices(client.RequestOptions{QueryParameters: 
url.Values{"xmlId": []string{"ds1"}}})
        if err != nil {
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go 
b/traffic_ops/traffic_ops_golang/routing/routes.go
index cbfa250307..452ab76157 100644
--- a/traffic_ops/traffic_ops_golang/routing/routes.go
+++ b/traffic_ops/traffic_ops_golang/routing/routes.go
@@ -311,9 +311,6 @@ func Routes(d ServerData) ([]Route, http.Handler, error) {
                {Version: api.Version{Major: 4, Minor: 0}, Method: 
http.MethodGet, Path: `servercheck/extensions$`, Handler: extensions.Get, 
RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: 
[]string{"SERVER-CHECK:READ", "SERVER:READ"}, Authenticated: Authenticated, 
Middlewares: nil, ID: 4834985993},
                {Version: api.Version{Major: 4, Minor: 0}, Method: 
http.MethodDelete, Path: `servercheck/extensions/{id}$`, Handler: 
extensions.Delete, RequiredPrivLevel: auth.PrivLevelReadOnly, 
RequiredPermissions: []string{"SERVER-CHECK:DELETE", "SERVER-CHECK:READ", 
"SERVER:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 4804982993},
 
-               //Server Details
-               {Version: api.Version{Major: 4, Minor: 0}, Method: 
http.MethodGet, Path: `servers/details/?$`, Handler: 
server.GetDetailParamHandler, RequiredPrivLevel: auth.PrivLevelReadOnly, 
RequiredPermissions: []string{"SERVER:READ", "DELIVERY-SERVICE:READ", 
"CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", 
"PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 
42612647143},
-
                //Server status
                {Version: api.Version{Major: 4, Minor: 0}, Method: 
http.MethodPut, Path: `servers/{id}/status$`, Handler: 
server.UpdateStatusHandler, RequiredPrivLevel: auth.PrivLevelOperations, 
RequiredPermissions: []string{"SERVER:UPDATE", "SERVER:READ", "STATUS:READ"}, 
Authenticated: Authenticated, Middlewares: nil, ID: 4766638513},
                {Version: api.Version{Major: 4, Minor: 0}, Method: 
http.MethodPost, Path: `servers/{id}/queue_update$`, Handler: 
server.QueueUpdateHandler, RequiredPrivLevel: auth.PrivLevelOperations, 
RequiredPermissions: []string{"SERVER:QUEUE", "SERVER:READ"}, Authenticated: 
Authenticated, Middlewares: nil, ID: 41894713},
diff --git a/traffic_ops/traffic_ops_golang/server/detail.go 
b/traffic_ops/traffic_ops_golang/server/detail.go
index 287e0e4011..a327a40595 100644
--- a/traffic_ops/traffic_ops_golang/server/detail.go
+++ b/traffic_ops/traffic_ops_golang/server/detail.go
@@ -28,6 +28,7 @@ import (
 
        
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
 
+       "github.com/apache/trafficcontrol/lib/go-log"
        "github.com/apache/trafficcontrol/lib/go-tc"
        "github.com/apache/trafficcontrol/lib/go-util"
        "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
@@ -36,27 +37,32 @@ import (
        "github.com/lib/pq"
 )
 
+// GetDetailParamHandler handles GET requests to /servers/details (the name
+// includes "Param" for legacy reasons).
+//
+// Deprecated: This endpoint has been removed from APIv4.
 func GetDetailParamHandler(w http.ResponseWriter, r *http.Request) {
+       alt := "/servers"
        inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
        if userErr != nil || sysErr != nil {
-               api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+               api.HandleDeprecatedErr(w, r, inf.Tx.Tx, errCode, userErr, 
sysErr, &alt)
                return
        }
        defer inf.Close()
 
        hostName := inf.Params["hostName"]
        physLocationIDStr := inf.Params["physLocationID"]
-       physLocationID := -1
+       var physLocationID int
        if physLocationIDStr != "" {
-               err := error(nil)
+               var err error
                physLocationID, err = strconv.Atoi(physLocationIDStr)
                if err != nil {
-                       api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, 
errors.New("physLocationID parameter is not an integer"), nil)
+                       api.HandleDeprecatedErr(w, r, inf.Tx.Tx, 
http.StatusBadRequest, errors.New("physLocationID parameter is not an 
integer"), err, &alt)
                        return
                }
        }
        if hostName == "" && physLocationIDStr == "" {
-               api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, 
errors.New("missing required fields: 'hostName' or 'physLocationID'"), nil)
+               api.HandleDeprecatedErr(w, r, inf.Tx.Tx, http.StatusBadRequest, 
errors.New("missing required fields: 'hostName' or 'physLocationID'"), nil, 
&alt)
                return
        }
        orderBy := "hostName"
@@ -65,22 +71,22 @@ func GetDetailParamHandler(w http.ResponseWriter, r 
*http.Request) {
        }
        limit := 1000
        if limitStr, ok := inf.Params["limit"]; ok {
-               err := error(nil)
+               var err error
                limit, err = strconv.Atoi(limitStr)
                if err != nil {
-                       api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, 
errors.New("limit parameter is not an integer"), nil)
+                       api.HandleDeprecatedErr(w, r, inf.Tx.Tx, 
http.StatusBadRequest, errors.New("limit parameter is not an integer"), err, 
&alt)
                        return
                }
        }
        servers, err := getDetailServers(inf.Tx.Tx, inf.User, hostName, 
physLocationID, util.CamelToSnakeCase(orderBy), limit, *inf.Version)
-       respVals := map[string]interface{}{
-               "orderby": orderBy,
-               "limit":   limit,
-               "size":    len(servers),
+       if err != nil {
+               api.HandleDeprecatedErr(w, r, inf.Tx.Tx, 
http.StatusInternalServerError, nil, err, &alt)
        }
+       var resp interface{}
+       size := len(servers)
 
        if inf.Version.Major <= 2 {
-               v11ServerList := []tc.ServerDetailV11{}
+               v11ServerList := make([]tc.ServerDetailV11, 0, size)
                for _, server := range servers {
                        interfaces := server.ServerInterfaces
                        routerHostName := ""
@@ -93,24 +99,23 @@ func GetDetailParamHandler(w http.ResponseWriter, r 
*http.Request) {
                        v11server := tc.ServerDetailV11{}
                        v11server.ServerDetail, err = 
dbhelpers.GetServerDetailFromV4(server, inf.Tx.Tx)
                        if err != nil {
-                               api.HandleErr(w, r, inf.Tx.Tx, 
http.StatusInternalServerError, nil, fmt.Errorf("failed to 
GetServerDetailFromV4: %w", err))
+                               api.HandleDeprecatedErr(w, r, inf.Tx.Tx, 
http.StatusInternalServerError, nil, fmt.Errorf("failed to 
GetServerDetailFromV4: %w", err), &alt)
                                return
                        }
                        v11server.RouterHostName = &routerHostName
                        v11server.RouterPortName = &routerPortName
                        legacyInterface, err := 
tc.V4InterfaceInfoToLegacyInterfaces(interfaces)
                        if err != nil {
-                               api.HandleErr(w, r, inf.Tx.Tx, 
http.StatusInternalServerError, nil, errors.New("converting to server detail 
v11: "+err.Error()))
+                               api.HandleDeprecatedErr(w, r, inf.Tx.Tx, 
http.StatusInternalServerError, nil, fmt.Errorf("converting to server detail 
v11: %w", err), &alt)
                                return
                        }
                        v11server.LegacyInterfaceDetails = legacyInterface
 
                        v11ServerList = append(v11ServerList, v11server)
                }
-               api.RespWriterVals(w, r, inf.Tx.Tx, respVals)(v11ServerList, 
err)
-               return
+               resp = v11ServerList
        } else if inf.Version.Major <= 3 {
-               v3ServerList := []tc.ServerDetailV30{}
+               v3ServerList := make([]tc.ServerDetailV30, 0, size)
                for _, server := range servers {
                        v3Server := tc.ServerDetailV30{}
                        interfaces := server.ServerInterfaces
@@ -123,33 +128,49 @@ func GetDetailParamHandler(w http.ResponseWriter, r 
*http.Request) {
                        }
                        v3Server.ServerDetail, err = 
dbhelpers.GetServerDetailFromV4(server, inf.Tx.Tx)
                        if err != nil {
-                               api.HandleErr(w, r, inf.Tx.Tx, 
http.StatusInternalServerError, nil, fmt.Errorf("failed to 
GetServerDetailFromV4: %w", err))
+                               api.HandleDeprecatedErr(w, r, inf.Tx.Tx, 
http.StatusInternalServerError, nil, fmt.Errorf("failed to 
GetServerDetailFromV4: %w", err), &alt)
                                return
                        }
                        v3Server.RouterHostName = &routerHostName
                        v3Server.RouterPortName = &routerPortName
                        v3Interfaces, err := 
tc.V4InterfaceInfoToV3Interfaces(interfaces)
                        if err != nil {
-                               api.HandleErr(w, r, inf.Tx.Tx, 
http.StatusInternalServerError, nil, errors.New("converting to server detail 
v3: "+err.Error()))
+                               api.HandleDeprecatedErr(w, r, inf.Tx.Tx, 
http.StatusInternalServerError, nil, fmt.Errorf("converting to server detail 
v3: %w", err), &alt)
                                return
                        }
                        v3Server.ServerInterfaces = &v3Interfaces
                        v3ServerList = append(v3ServerList, v3Server)
                }
-               api.RespWriterVals(w, r, inf.Tx.Tx, respVals)(v3ServerList, err)
+               resp = v3ServerList
+       } else {
+               api.WriteRespAlertNotFound(w, r)
                return
        }
-       api.RespWriterVals(w, r, inf.Tx.Tx, respVals)(servers, err)
+
+       api.WriteRespVals(w, r, resp, map[string]interface{}{
+               "alerts":  api.CreateDeprecationAlerts(&alt).Alerts,
+               "limit":   limit,
+               "orderby": orderBy,
+               "size":    size,
+       })
 }
 
+// AddWhereClauseAndQuery adds a WHERE clause to the query given in `q` (does
+// NOT check for existing WHERE clauses or that the end of the string is the
+// proper place to put one!) that limits the query results to those with the
+// given hostname and/or Physical Location ID and, with orderByStr and limitStr
+// appended (in that order), returns the result of querying the given
+// transaction.
+// Use an empty string for the hostname to not filter by hostname, use -1 as
+// physLocationID to not filter by Physical Location.
 func AddWhereClauseAndQuery(tx *sql.Tx, q string, hostName string, 
physLocationID int, orderByStr string, limitStr string) (*sql.Rows, error) {
-       if hostName != "" && physLocationID != -1 {
+       if hostName != "" && physLocationID != 0 {
                q += ` WHERE server.host_name = $1::text AND 
server.phys_location = $2::bigint` + orderByStr + limitStr
                return tx.Query(q, hostName, physLocationID)
        } else if hostName != "" {
                q += ` WHERE server.host_name = $1::text` + orderByStr + 
limitStr
                return tx.Query(q, hostName)
-       } else if physLocationID != -1 {
+       } else if physLocationID != 0 {
                q += ` WHERE server.phys_location = $1::int` + orderByStr + 
limitStr
                return tx.Query(q, physLocationID)
        } else {
@@ -158,6 +179,40 @@ func AddWhereClauseAndQuery(tx *sql.Tx, q string, hostName 
string, physLocationI
        }
 }
 
+const dataFetchQuery = `,
+cg.name AS cachegroup,
+cdn.name AS cdn_name,
+ARRAY(select deliveryservice from deliveryservice_server where server = 
server.id),
+server.domain_name,
+server.guid,
+server.host_name,
+server.https_port,
+server.ilo_ip_address,
+server.ilo_ip_gateway,
+server.ilo_ip_netmask,
+server.ilo_password,
+server.ilo_username,
+(SELECT address FROM ip_address WHERE service_address = true AND 
family(address) = 4 AND server = server.id) AS service_ip,
+(SELECT address FROM ip_address WHERE service_address = true AND 
family(address) = 6 AND server = server.id) AS service_ip6,
+(SELECT gateway FROM ip_address WHERE service_address = true AND 
family(address) = 4 AND server = server.id) AS service_gateway,
+(SELECT gateway FROM ip_address WHERE service_address = true AND 
family(address) = 6 AND server = server.id) AS service_gateway6,
+(SELECT host(netmask(ip_address.address)) FROM ip_address WHERE 
service_address = true AND family(address) = 4 AND server = server.id) AS 
service_netmask,
+(SELECT interface FROM ip_address WHERE service_address = true AND 
family(address) = 4 AND server = server.id) AS interface_name,
+(SELECT mtu FROM interface WHERE server.id = interface.server AND 
interface.name = (SELECT interface FROM ip_address WHERE service_address = true 
AND family(address) = 4 AND server = server.id)) AS interface_mtu,
+server.mgmt_ip_address,
+server.mgmt_ip_gateway,
+server.mgmt_ip_netmask,
+server.offline_reason,
+pl.name as phys_location,
+(SELECT ARRAY_AGG(profile_name) FROM server_profile WHERE 
server_profile.server=server.id) AS profile_name,
+server.rack,
+st.name as status,
+server.tcp_port,
+t.name as server_type,
+server.xmpp_id,
+server.xmpp_passwd
+`
+
 func getDetailServers(tx *sql.Tx, user *auth.CurrentUser, hostName string, 
physLocationID int, orderBy string, limit int, reqVersion api.Version) 
([]tc.ServerDetailV40, error) {
        allowedOrderByCols := map[string]string{
                "":                "",
@@ -192,39 +247,6 @@ func getDetailServers(tx *sql.Tx, user *auth.CurrentUser, 
hostName string, physL
                return nil, errors.New("orderBy '" + orderBy + "' not 
permitted")
        }
 
-       dataFetchQuery := `,
-cg.name AS cachegroup,
-cdn.name AS cdn_name,
-ARRAY(select deliveryservice from deliveryservice_server where server = 
server.id),
-server.domain_name,
-server.guid,
-server.host_name,
-server.https_port,
-server.ilo_ip_address,
-server.ilo_ip_gateway,
-server.ilo_ip_netmask,
-server.ilo_password,
-server.ilo_username,
-(SELECT address FROM ip_address WHERE service_address = true AND 
family(address) = 4 AND server = server.id) AS service_ip,
-(SELECT address FROM ip_address WHERE service_address = true AND 
family(address) = 6 AND server = server.id) AS service_ip6,
-(SELECT gateway FROM ip_address WHERE service_address = true AND 
family(address) = 4 AND server = server.id) AS service_gateway,
-(SELECT gateway FROM ip_address WHERE service_address = true AND 
family(address) = 6 AND server = server.id) AS service_gateway6,
-(SELECT host(netmask(ip_address.address)) FROM ip_address WHERE 
service_address = true AND family(address) = 4 AND server = server.id) AS 
service_netmask,
-(SELECT interface FROM ip_address WHERE service_address = true AND 
family(address) = 4 AND server = server.id) AS interface_name,
-(SELECT mtu FROM interface WHERE server.id = interface.server AND 
interface.name = (SELECT interface FROM ip_address WHERE service_address = true 
AND family(address) = 4 AND server = server.id)) AS interface_mtu,
-server.mgmt_ip_address,
-server.mgmt_ip_gateway,
-server.mgmt_ip_netmask,
-server.offline_reason,
-pl.name as phys_location,
-(SELECT ARRAY_AGG(profile_name) FROM server_profile WHERE 
server_profile.server=server.id) AS profile_name,
-server.rack,
-st.name as status,
-server.tcp_port,
-t.name as server_type,
-server.xmpp_id,
-server.xmpp_passwd
-`
        queryFormatString := `
 SELECT
        server.id
@@ -247,42 +269,43 @@ JOIN type t ON server.type = t.id
        }
        idRows, err := AddWhereClauseAndQuery(tx, 
fmt.Sprintf(queryFormatString, ""), hostName, physLocationID, orderByStr, 
limitStr)
        if err != nil {
-               return nil, errors.New("querying delivery service eligible 
servers: " + err.Error())
+               return nil, fmt.Errorf("querying delivery service eligible 
servers: %w", err)
        }
-       defer idRows.Close()
+       defer log.Close(idRows, "getting IDs for server details names")
        var serverIDs []int
        for idRows.Next() {
                var serverID *int
                err := idRows.Scan(&serverID)
                if err != nil {
-                       return nil, errors.New("querying delivery service 
eligible server ids: " + err.Error())
+                       return nil, fmt.Errorf("querying delivery service 
eligible server ids: %w", err)
                }
                serverIDs = append(serverIDs, *serverID)
        }
        serversMap, err := dbhelpers.GetServersInterfaces(serverIDs, tx)
        if err != nil {
-               return nil, errors.New("unable to get server interfaces: " + 
err.Error())
+               return nil, fmt.Errorf("unable to get server interfaces: %w", 
err)
        }
        rows, err := AddWhereClauseAndQuery(tx, fmt.Sprintf(queryFormatString, 
dataFetchQuery), hostName, physLocationID, orderByStr, limitStr)
        if err != nil {
-               return nil, errors.New("Error querying detail servers: " + 
err.Error())
+               return nil, fmt.Errorf("querying detail servers: %w", err)
        }
 
-       defer rows.Close()
+       defer log.Close(rows, "getting server details data")
        sIDs := []int{}
        servers := []tc.ServerDetailV40{}
 
-       serviceAddress := util.StrPtr("")
-       service6Address := util.StrPtr("")
-       serviceGateway := util.StrPtr("")
-       service6Gateway := util.StrPtr("")
-       serviceNetmask := util.StrPtr("")
-       serviceInterface := util.StrPtr("")
-       serviceMtu := util.StrPtr("")
+       serviceAddress := new(string)
+       service6Address := new(string)
+       serviceGateway := new(string)
+       service6Gateway := new(string)
+       serviceNetmask := new(string)
+       serviceInterface := new(string)
+       serviceMtu := new(string)
 
        for rows.Next() {
                s := tc.ServerDetailV40{}
-               if err := rows.Scan(&s.ID,
+               err = rows.Scan(
+                       &s.ID,
                        &s.CacheGroup,
                        &s.CDNName,
                        pq.Array(&s.DeliveryServiceIDs),
@@ -313,8 +336,10 @@ JOIN type t ON server.type = t.id
                        &s.TCPPort,
                        &s.Type,
                        &s.XMPPID,
-                       &s.XMPPPasswd); err != nil {
-                       return nil, errors.New("Error scanning detail server: " 
+ err.Error())
+                       &s.XMPPPasswd,
+               )
+               if err != nil {
+                       return nil, fmt.Errorf("scanning detail server: %w", 
err)
                }
                s.ServerInterfaces = []tc.ServerInterfaceInfoV40{}
                if interfacesMap, ok := serversMap[*s.ID]; ok {
@@ -335,16 +360,16 @@ JOIN type t ON server.type = t.id
 
        rows, err = tx.Query(`SELECT serverid, description, val from hwinfo 
where serverid = ANY($1);`, pq.Array(sIDs))
        if err != nil {
-               return nil, errors.New("Error querying detail servers hardware 
info: " + err.Error())
+               return nil, fmt.Errorf("querying detail servers hardware info: 
%w", err)
        }
-       defer rows.Close()
+       defer log.Close(rows, "getting hwinfo data")
        hwInfos := map[int]map[string]string{}
        for rows.Next() {
                serverID := 0
                desc := ""
                val := ""
                if err := rows.Scan(&serverID, &desc, &val); err != nil {
-                       return nil, errors.New("Error scanning detail server 
hardware info: " + err.Error())
+                       return nil, fmt.Errorf("scanning detail server hardware 
info: %w", err)
                }
 
                hwInfo, ok := hwInfos[serverID]
diff --git a/traffic_ops/v3-client/server.go b/traffic_ops/v3-client/server.go
index 13b2fad97c..c7d026c3e6 100644
--- a/traffic_ops/v3-client/server.go
+++ b/traffic_ops/v3-client/server.go
@@ -204,6 +204,10 @@ func (to *Session) GetFirstServer(params *url.Values, 
header http.Header) (tc.Se
        return firstServer, reqInf, err
 }
 
+// GetServerDetailsByHostNameWithHdr retrieves the "details" of all servers 
with
+// the given hostname.
+// Deprecated: Server "details" as a concept have been removed from the latest
+// version of the API, and clients should use GetServersWithHdr instead.
 func (to *Session) GetServerDetailsByHostNameWithHdr(hostName string, header 
http.Header) ([]tc.ServerDetailV30, toclientlib.ReqInf, error) {
        v := url.Values{}
        v.Add("hostName", hostName)
diff --git a/traffic_ops/v4-client/server.go b/traffic_ops/v4-client/server.go
index 59d3009488..ab56c42c47 100644
--- a/traffic_ops/v4-client/server.go
+++ b/traffic_ops/v4-client/server.go
@@ -29,9 +29,6 @@ const (
        // apiServers is the API version-relative path to the /servers API
        // endpoint.
        apiServers = "/servers"
-       // apiServersDetails is the API version-relative path to the
-       // /servers/details API endpoint.
-       apiServersDetails = "/servers/details"
 )
 
 func needAndCanFetch(id *int, name *string) bool {
@@ -127,14 +124,6 @@ func (to *Session) GetServers(opts RequestOptions) 
(tc.ServersV4Response, toclie
        return data, reqInf, err
 }
 
-// GetServersDetails retrieves the Server Details of the Server with the given
-// (short) Hostname.
-func (to *Session) GetServersDetails(opts RequestOptions) 
(tc.ServersV4DetailResponse, toclientlib.ReqInf, error) {
-       var data tc.ServersV4DetailResponse
-       reqInf, err := to.get(apiServersDetails, opts, &data)
-       return data, reqInf, err
-}
-
 // DeleteServer deletes the Server with the given ID.
 func (to *Session) DeleteServer(id int, opts RequestOptions) (tc.Alerts, 
toclientlib.ReqInf, error) {
        route := fmt.Sprintf("%s/%d", apiServers, id)

Reply via email to