From affde0b786b8abc665af55b9285fda47252ba60b Mon Sep 17 00:00:00 2001 From: Srijeet Chatterjee Date: Mon, 15 Aug 2022 17:10:03 -0600 Subject: [PATCH 1/8] Adding ASN to Server struct --- CHANGELOG.md | 1 + cache-config/t3cutil/toreq/clientfuncs.go | 2 +- cache-config/t3cutil/toreq/conversions.go | 8 +- lib/go-tc/servers.go | 9 +- traffic_monitor/towrap/towrap.go | 2 +- traffic_ops/testing/api/v4/servers_test.go | 55 +++++++++-- .../testing/api/v4/serverupdatestatus_test.go | 10 +- traffic_ops/testing/api/v4/tc-fixtures.json | 4 +- .../dbhelpers/db_helpers.go | 2 +- .../traffic_ops_golang/routing/routes.go | 2 + .../traffic_ops_golang/server/servers.go | 94 +++++++++++++------ .../traffic_ops_golang/server/servers_test.go | 29 ++++-- .../table/servers/TableServersController.js | 5 + .../ultimate-test-harness/http_test.go | 8 +- 14 files changed, 170 insertions(+), 61 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c2624abe6..c74a5b183a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - [#7032](https://github.com/apache/trafficcontrol/issues/7032) Add t3c-apply flag to use local ATS version for config generation rather than Server package Parameter, to allow managing the ATS OS package via external tools. See 'man t3c-apply' and 'man t3c-generate' for details. - [Traffic Monitor] Added logging for `ipv4Availability` and `ipv6Availability` in TM. +- [Traffic Ops] Added the `ASN` field in TO Server struct, which provides the ability to query servers by `ASN`. ### Changed - Traffic Portal now obscures sensitive text in Delivery Service "Raw Remap" fields, private SSL keys, "Header Rewrite" rules, and ILO interface passwords by default. diff --git a/cache-config/t3cutil/toreq/clientfuncs.go b/cache-config/t3cutil/toreq/clientfuncs.go index 9c5e335ce5..f7a9ca15af 100644 --- a/cache-config/t3cutil/toreq/clientfuncs.go +++ b/cache-config/t3cutil/toreq/clientfuncs.go @@ -169,7 +169,7 @@ func (cl *TOClient) GetServerByHostName(serverHostName string, reqHdr http.Heade if len(toServers.Response) < 1 { return errors.New("getting server name '" + serverHostName + "' from Traffic Ops '" + torequtil.MaybeIPStr(reqInf.RemoteAddr) + "': no servers returned") } - asv, err := serverToLatest(&toServers.Response[0]) + asv, err := serverToLatest(&toServers.Response[0].ServerV40) if err != nil { return errors.New("converting server to latest version: " + err.Error()) } diff --git a/cache-config/t3cutil/toreq/conversions.go b/cache-config/t3cutil/toreq/conversions.go index ab098a5eaa..f480377fbe 100644 --- a/cache-config/t3cutil/toreq/conversions.go +++ b/cache-config/t3cutil/toreq/conversions.go @@ -34,7 +34,11 @@ import ( ) func serversToLatest(svs tc.ServersV4Response) ([]atscfg.Server, error) { - return atscfg.ToServers(svs.Response), nil + serversV40 := make([]tc.ServerV40, 0) + for _, srv := range svs.Response { + serversV40 = append(serversV40, srv.ServerV40) + } + return atscfg.ToServers(serversV40), nil } func serverToLatest(oldSv *tc.ServerV40) (*atscfg.Server, error) { @@ -218,7 +222,7 @@ func (cl *TOClient) GetServersCompat(opts toclient.RequestOptions) (tc.ServersV4 if err != nil { return tc.ServersV4Response{}, reqInf, errors.New("converting server from possible legacy format: " + err.Error()) } - resp.Response = append(resp.Response, newSv) + resp.Response = append(resp.Response, tc.ServerV41{ServerV40: newSv}) } return resp, reqInf, nil } diff --git a/lib/go-tc/servers.go b/lib/go-tc/servers.go index 2f435cdb3f..a08fa9dfea 100644 --- a/lib/go-tc/servers.go +++ b/lib/go-tc/servers.go @@ -30,11 +30,13 @@ import ( "time" "github.com/apache/trafficcontrol/lib/go-util" + + "github.com/lib/pq" ) // ServersV4Response is the format of a response to a GET request for API v4.x /servers. type ServersV4Response struct { - Response []ServerV40 `json:"response"` + Response []ServerV41 `json:"response"` Summary struct { Count uint64 `json:"count"` } `json:"summary"` @@ -1019,6 +1021,11 @@ func UpdateServerPropertiesV40(profileNames []string, properties CommonServerPro } } +type ServerV41 struct { + ServerV40 + ASNs pq.Int32Array `json:"asns"` +} + // ServerV40 is the representation of a Server in version 4.0 of the Traffic Ops API. type ServerV40 struct { Cachegroup *string `json:"cachegroup" db:"cachegroup"` diff --git a/traffic_monitor/towrap/towrap.go b/traffic_monitor/towrap/towrap.go index c24ea22fc3..48f0f66cd3 100644 --- a/traffic_monitor/towrap/towrap.go +++ b/traffic_monitor/towrap/towrap.go @@ -587,7 +587,7 @@ func (s TrafficOpsSessionThreadsafe) fetchServerByHostname(hostName string) (tc. for i, srv := range resp.Response { num = i if srv.CDNName != nil && srv.HostName != nil && *srv.HostName == hostName { - server = srv + server = srv.ServerV40 found = true break } diff --git a/traffic_ops/testing/api/v4/servers_test.go b/traffic_ops/testing/api/v4/servers_test.go index e9e2ab649b..2e70319b1e 100644 --- a/traffic_ops/testing/api/v4/servers_test.go +++ b/traffic_ops/testing/api/v4/servers_test.go @@ -33,12 +33,35 @@ import ( ) func TestServers(t *testing.T) { - WithObjs(t, []TCObj{CDNs, Types, Tenants, Users, Parameters, Profiles, Statuses, Divisions, Regions, PhysLocations, CacheGroups, Servers, Topologies, ServiceCategories, DeliveryServices, DeliveryServiceServerAssignments}, func() { + WithObjs(t, []TCObj{CDNs, Types, Tenants, Users, Parameters, Profiles, Statuses, Divisions, Regions, PhysLocations, CacheGroups, Servers, Topologies, ServiceCategories, DeliveryServices, DeliveryServiceServerAssignments, ASN}, func() { currentTime := time.Now().UTC().Add(-15 * time.Second) currentTimeRFC := currentTime.Format(time.RFC1123) tomorrow := currentTime.AddDate(0, 0, 1).Format(time.RFC1123) + // Add a new asn for one of the cachegroups + cgResp, _, err := TOSession.GetCacheGroups(client.RequestOptions{QueryParameters: url.Values{"name": {"topology-mid-cg-01"}}}) + if err != nil { + t.Fatalf("couldn't get cachegroups: %v", err) + } + if len(cgResp.Response) != 1 { + t.Fatalf("expected 1 cachegroup, but got %d", len(cgResp.Response)) + } + if cgResp.Response[0].ID == nil { + t.Fatalf("ID of cachegroup is nil") + } + + asn := tc.ASN{ + ASN: 1111, + Cachegroup: "topology-mid-cg-01", + CachegroupID: *cgResp.Response[0].ID, + } + + _, _, err = TOSession.CreateASN(asn, client.NewRequestOptions()) + if err != nil { + t.Fatalf("couldn't create ASN: %v", err) + } + methodTests := utils.V4TestCase{ "GET": { "NOT MODIFIED when NO CHANGES made": { @@ -46,6 +69,11 @@ func TestServers(t *testing.T) { RequestOpts: client.RequestOptions{Header: http.Header{rfc.IfModifiedSince: {tomorrow}}}, Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusNotModified)), }, + "OK when CORRECT ASN parameter": { + ClientSession: TOSession, + RequestOpts: client.RequestOptions{QueryParameters: url.Values{"asn": {"1111"}}}, + Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseLengthGreaterOrEqual(1)), + }, "OK when VALID HOSTNAME parameter": { ClientSession: TOSession, RequestOpts: client.RequestOptions{QueryParameters: url.Values{"hostName": {"atlanta-edge-01"}}}, @@ -58,6 +86,17 @@ func TestServers(t *testing.T) { Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseLengthGreaterOrEqual(1), validateServerFields(map[string]interface{}{"CachegroupID": GetCacheGroupId(t, "cachegroup1")()})), }, + "OK when VALID ASN parameter": { + ClientSession: TOSession, + RequestOpts: client.RequestOptions{QueryParameters: url.Values{"asn": {"1111"}}}, + Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseLengthGreaterOrEqual(1), + validateServerFields(map[string]interface{}{"Cachegroup": "topology-mid-cg-01"})), + }, + "EMPTY RESPONSE when INVALID ASN parameter": { + ClientSession: TOSession, + RequestOpts: client.RequestOptions{QueryParameters: url.Values{"asn": {"5555"}}}, + Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseHasLength(0)), + }, "OK when VALID CACHEGROUPNAME parameter": { ClientSession: TOSession, RequestOpts: client.RequestOptions{QueryParameters: url.Values{"cachegroupName": {"topology-mid-cg-01"}}}, @@ -387,7 +426,7 @@ func TestServers(t *testing.T) { func validateServerFields(expectedResp map[string]interface{}) utils.CkReqFunc { return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) { assert.RequireNotNil(t, resp, "Expected response to not be nil.") - serverResp := resp.([]tc.ServerV40) + serverResp := resp.([]tc.ServerV41) for field, expected := range expectedResp { for _, server := range serverResp { switch field { @@ -460,7 +499,7 @@ func validateServerFieldsForUpdate(hostname string, expectedResp map[string]inte func validateExpectedServers(expectedHostnames []string) utils.CkReqFunc { return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) { assert.RequireNotNil(t, resp, "Expected response to not be nil.") - serverResp := resp.([]tc.ServerV40) + serverResp := resp.([]tc.ServerV41) var notInResponse []string serverMap := make(map[string]struct{}) for _, server := range serverResp { @@ -479,7 +518,7 @@ func validateExpectedServers(expectedHostnames []string) utils.CkReqFunc { func validateServerTypeIsNotMid() utils.CkReqFunc { return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) { assert.RequireNotNil(t, resp, "Expected response to not be nil.") - serverResp := resp.([]tc.ServerV40) + serverResp := resp.([]tc.ServerV41) for _, server := range serverResp { assert.RequireNotNil(t, server.HostName, "Expected server host name to not be nil.") assert.NotEqual(t, server.Type, tc.CacheTypeMid.String(), "Expected to find no %s-typed servers but found server %s", tc.CacheTypeMid, *server.HostName) @@ -490,7 +529,7 @@ func validateServerTypeIsNotMid() utils.CkReqFunc { func validateServerPagination(paginationParam string) utils.CkReqFunc { return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) { assert.RequireNotNil(t, resp, "Expected response to not be nil.") - paginationResp := resp.([]tc.ServerV40) + paginationResp := resp.([]tc.ServerV41) opts := client.NewRequestOptions() opts.QueryParameters.Set("orderby", "id") respBase, _, err := TOSession.GetServers(opts) @@ -556,7 +595,7 @@ func UpdateTestServerStatusLastUpdated(t *testing.T) { assert.RequireNoError(t, err, "Cannot get Server by hostname '%s': %v - alerts %+v", hostName, err, resp.Alerts) assert.RequireGreaterOrEqual(t, len(resp.Response), 1, "Expected at least one server to exist by hostname '%s'", hostName) assert.RequireNotNil(t, resp.Response[0].StatusLastUpdated, "Traffic Ops returned a representation for a server with null or undefined Status Last Updated time") - originalServer := resp.Response[0] + originalServer := resp.Response[0].ServerV40 // Perform an update with no changes to status alerts, _, err := TOSession.UpdateServer(*originalServer.ID, originalServer, client.RequestOptions{}) @@ -565,7 +604,7 @@ func UpdateTestServerStatusLastUpdated(t *testing.T) { resp, _, err = TOSession.GetServers(opts) assert.RequireNoError(t, err, "Cannot get Server by hostname '%s': %v - alerts %+v", hostName, err, resp.Alerts) assert.RequireGreaterOrEqual(t, len(resp.Response), 1, "Expected at least one server to exist by hostname '%s'", hostName) - respServer := resp.Response[0] + respServer := resp.Response[0].ServerV40 assert.RequireNotNil(t, respServer.StatusLastUpdated, "Traffic Ops returned a representation for a server with null or undefined Status Last Updated time") assert.Equal(t, *originalServer.StatusLastUpdated, *respServer.StatusLastUpdated, "Since status didnt change, no change in 'StatusLastUpdated' time was expected. "+ "old value: %v, new value: %v", *originalServer.StatusLastUpdated, *respServer.StatusLastUpdated) @@ -580,7 +619,7 @@ func UpdateTestServerStatusLastUpdated(t *testing.T) { resp, _, err = TOSession.GetServers(opts) assert.RequireNoError(t, err, "Cannot get Server by hostname '%s': %v - alerts %+v", hostName, err, resp.Alerts) assert.RequireGreaterOrEqual(t, len(resp.Response), 1, "Expected at least one server to exist by hostname '%s'", hostName) - respServer = resp.Response[0] + respServer = resp.Response[0].ServerV40 assert.RequireNotNil(t, respServer.StatusLastUpdated, "Traffic Ops returned a representation for a server with null or undefined Status Last Updated time") assert.NotEqual(t, *originalServer.StatusLastUpdated, *respServer.StatusLastUpdated, "Since status changed, expected 'StatusLastUpdated' to change. "+ "old value: %v, new value: %v", *originalServer.StatusLastUpdated, *respServer.StatusLastUpdated) diff --git a/traffic_ops/testing/api/v4/serverupdatestatus_test.go b/traffic_ops/testing/api/v4/serverupdatestatus_test.go index e4dc57ffd5..5a63e9adc6 100644 --- a/traffic_ops/testing/api/v4/serverupdatestatus_test.go +++ b/traffic_ops/testing/api/v4/serverupdatestatus_test.go @@ -130,7 +130,7 @@ func TestServerUpdateStatus(t *testing.T) { t.Errorf("Expected exactly one server named '%s' to exist - actual: %d", s.name, len(resp.Response)) t.Logf("Testing will proceed with server: %+v", resp.Response[0]) } - *s.server = resp.Response[0] + *s.server = resp.Response[0].ServerV40 if s.server.ID == nil { t.Fatalf("server '%s' was returned with nil ID", s.name) } @@ -289,7 +289,7 @@ func TestServerQueueUpdate(t *testing.T) { t.Errorf("Expected exactly one server named '%s' to exist", serverName) t.Logf("Testing will proceed with server: %+v", resp.Response[0]) } - s = resp.Response[0] + s = resp.Response[0].ServerV40 // assert that servers don't have updates pending if s.UpdPending == nil { @@ -329,7 +329,7 @@ func TestServerQueueUpdate(t *testing.T) { t.Errorf("Expected exactly one server named '%s' to exist", serverName) t.Logf("Testing will proceed with server: %+v", resp.Response[0]) } - s = resp.Response[0] + s = resp.Response[0].ServerV40 if s.UpdPending == nil { t.Fatalf("Server '%s' had null (or missing) updPending property", serverName) } @@ -515,7 +515,7 @@ func TestSetTopologiesServerUpdateStatuses(t *testing.T) { if _, ok := cachesByCDNCacheGroup[*s.CDNName]; !ok { cachesByCDNCacheGroup[*s.CDNName] = make(map[string][]tc.ServerV4) } - cachesByCDNCacheGroup[*s.CDNName][cacheGroupName] = append(cachesByCDNCacheGroup[*s.CDNName][cacheGroupName], s) + cachesByCDNCacheGroup[*s.CDNName][cacheGroupName] = append(cachesByCDNCacheGroup[*s.CDNName][cacheGroupName], s.ServerV40) } } cdnNames := make([]string, 0, len(cachesByCDNCacheGroup)) @@ -554,7 +554,7 @@ func TestSetTopologiesServerUpdateStatuses(t *testing.T) { if len(cachesByCDNCacheGroup[*s.CDNName][cacheGroupName]) > 0 { cachesByCDNCacheGroup[*s.CDNName][cacheGroupName] = []tc.ServerV4{} } - cachesByCDNCacheGroup[*s.CDNName][cacheGroupName] = append(cachesByCDNCacheGroup[*s.CDNName][cacheGroupName], s) + cachesByCDNCacheGroup[*s.CDNName][cacheGroupName] = append(cachesByCDNCacheGroup[*s.CDNName][cacheGroupName], s.ServerV40) } } for _, cacheGroupName := range cacheGroupNames { diff --git a/traffic_ops/testing/api/v4/tc-fixtures.json b/traffic_ops/testing/api/v4/tc-fixtures.json index 8f34bb26fc..3b4e823fa3 100644 --- a/traffic_ops/testing/api/v4/tc-fixtures.json +++ b/traffic_ops/testing/api/v4/tc-fixtures.json @@ -2,11 +2,11 @@ "asns": [ { "asn": 8888, - "cachegroupName": "originCachegroup" + "cachegroup": "originCachegroup" }, { "asn": 9999, - "cachegroupName": "multiOriginCachegroup" + "cachegroup": "multiOriginCachegroup" } ], "cachegroups": [ diff --git a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go index e70713899a..ed078e6ba2 100644 --- a/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go +++ b/traffic_ops/traffic_ops_golang/dbhelpers/db_helpers.go @@ -2024,7 +2024,7 @@ WHERE server.id = $2;` } // GetCommonServerPropertiesFromV4 converts ServerV40 to CommonServerProperties struct. -func GetCommonServerPropertiesFromV4(s tc.ServerV40, tx *sql.Tx) (tc.CommonServerProperties, error) { +func GetCommonServerPropertiesFromV4(s tc.ServerV41, tx *sql.Tx) (tc.CommonServerProperties, error) { var id int var desc string if len(s.ProfileNames) == 0 { diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go b/traffic_ops/traffic_ops_golang/routing/routes.go index 7089c120bf..032c3b5640 100644 --- a/traffic_ops/traffic_ops_golang/routing/routes.go +++ b/traffic_ops/traffic_ops_golang/routing/routes.go @@ -131,6 +131,8 @@ func Routes(d ServerData) ([]Route, http.Handler, error) { * 4.x API */ + // 4.1 GET servers + {Version: api.Version{Major: 4, Minor: 1}, Method: http.MethodGet, Path: `servers/?$`, Handler: server.Read, 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: 47219592853}, // Assign Multiple Server Capabilities {Version: api.Version{Major: 4, Minor: 1}, Method: http.MethodPut, Path: `multiple_server_capabilities/?$`, Handler: server.AssignMultipleServerCapabilities, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:UPDATE", "SERVER:READ", "SERVER-CAPABILITY:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 40792419258}, diff --git a/traffic_ops/traffic_ops_golang/server/servers.go b/traffic_ops/traffic_ops_golang/server/servers.go index e9404fa333..cb49d5aa74 100644 --- a/traffic_ops/traffic_ops_golang/server/servers.go +++ b/traffic_ops/traffic_ops_golang/server/servers.go @@ -152,7 +152,8 @@ SELECT s.config_apply_time, s.xmpp_id, s.xmpp_passwd, - s.status_last_updated + s.status_last_updated, + (SELECT ARRAY_AGG(asn) AS asns FROM asn a WHERE a.cachegroup = s.cachegroup) AS asns ` + serversFromAndJoin const selectIDQuery = ` @@ -688,7 +689,7 @@ func Read(w http.ResponseWriter, r *http.Request) { return } - servers := []tc.ServerV40{} + servers := []tc.ServerV41{} var serverCount uint64 cfg, e := api.GetConfig(r.Context()) useIMS := false @@ -704,16 +705,23 @@ func Read(w http.ResponseWriter, r *http.Request) { } if errCode == http.StatusNotModified { w.WriteHeader(errCode) - api.WriteResp(w, r, []tc.ServerV40{}) + api.WriteResp(w, r, []tc.ServerV41{}) return } if userErr != nil || sysErr != nil { api.HandleErr(w, r, tx, errCode, userErr, sysErr) return } - if version.Major >= 4 { - api.WriteRespWithSummary(w, r, servers, serverCount) + if version.Minor >= 1 { + api.WriteRespWithSummary(w, r, servers, serverCount) + return + } + v40Servers := make([]tc.ServerV40, 0) + for _, server := range servers { + v40Servers = append(v40Servers, server.ServerV40) + } + api.WriteRespWithSummary(w, r, v40Servers, serverCount) return } v3Servers := make([]tc.ServerV30, 0) @@ -760,7 +768,7 @@ func getServerCount(tx *sqlx.Tx, query string, queryValues map[string]interface{ return serverCount, nil } -func getServers(h http.Header, params map[string]string, tx *sqlx.Tx, user *auth.CurrentUser, useIMS bool, version api.Version) ([]tc.ServerV40, uint64, error, error, int, *time.Time) { +func getServers(h http.Header, params map[string]string, tx *sqlx.Tx, user *auth.CurrentUser, useIMS bool, version api.Version) ([]tc.ServerV41, uint64, error, error, int, *time.Time) { var maxTime time.Time var runSecond bool // Query Parameters to Database Query column mappings @@ -787,6 +795,12 @@ func getServers(h http.Header, params map[string]string, tx *sqlx.Tx, user *auth } if version.Major >= 4 { + if version.Minor >= 1 { + queryParamsToSQLCols["asn"] = dbhelpers.WhereColumnInfo{ + Column: "a.asn", + Checker: api.IsInt, + } + } queryParamsToSQLCols["profileName"] = dbhelpers.WhereColumnInfo{ Column: "sp.profile_name", Checker: nil, @@ -854,12 +868,22 @@ func getServers(h http.Header, params map[string]string, tx *sqlx.Tx, user *auth queryString = selectQuery countQueryString = serverCountQuery if version.Major >= 4 { + if version.Minor >= 1 { + if _, ok := params["asn"]; ok { + queryString = selectQuery + ` +JOIN asn a ON s.cachegroup = a.cachegroup` + countQueryString = serverCountQuery + ` +JOIN asn a ON s.cachegroup = a.cachegroup` + } + } if _, ok := params["profileName"]; ok { - queryString = selectQuery + `JOIN server_profile sp ON s.id = sp.server` - countQueryString = serverCountQuery + `JOIN server_profile sp ON s.id = sp.server` + queryString = queryString + ` +JOIN server_profile sp ON s.id = sp.server` + countQueryString = countQueryString + ` +JOIN server_profile sp ON s.id = sp.server` } else { - queryString = selectQuery + joinProfileV4 - countQueryString = serverCountQuery + joinProfileV4 + queryString = queryString + ` ` + joinProfileV4 + countQueryString = countQueryString + ` ` + joinProfileV4 } } countQuery := countQueryString + queryAddition + where @@ -872,7 +896,7 @@ func getServers(h http.Header, params map[string]string, tx *sqlx.Tx, user *auth return nil, 0, nil, fmt.Errorf("failed to get servers count: %v", err), http.StatusInternalServerError, nil } - serversList := []tc.ServerV40{} + serversList := []tc.ServerV41{} if useIMS { runSecond, maxTime = ims.TryIfModifiedSinceQuery(tx, h, queryValues, selectMaxLastUpdatedQuery(queryAddition, where)) if !runSecond { @@ -899,10 +923,11 @@ func getServers(h http.Header, params map[string]string, tx *sqlx.Tx, user *auth HiddenField := "********" - servers := make(map[int]tc.ServerV40) + servers := make(map[int]tc.ServerV41) ids := []int{} + var asns pq.Int32Array for rows.Next() { - s := tc.ServerV40{} + s := tc.ServerV41{} err := rows.Scan(&s.Cachegroup, &s.CachegroupID, &s.CDNID, @@ -939,7 +964,9 @@ func getServers(h http.Header, params map[string]string, tx *sqlx.Tx, user *auth &s.ConfigApplyTime, &s.XMPPID, &s.XMPPPasswd, - &s.StatusLastUpdated) + &s.StatusLastUpdated, + &asns) + s.ASNs = asns if err != nil { return nil, serverCount, nil, errors.New("getting servers: " + err.Error()), http.StatusInternalServerError, nil } @@ -972,7 +999,7 @@ func getServers(h http.Header, params map[string]string, tx *sqlx.Tx, user *auth } if len(ids) < 1 { - return []tc.ServerV40{}, serverCount, nil, nil, http.StatusOK, nil + return []tc.ServerV41{}, serverCount, nil, nil, http.StatusOK, nil } query, args, err := sqlx.In(`SELECT max_bandwidth, monitor, mtu, name, server, router_host_name, router_port_name FROM interface WHERE server IN (?)`, ids) @@ -1043,7 +1070,7 @@ func getServers(h http.Header, params map[string]string, tx *sqlx.Tx, user *auth } } - returnable := make([]tc.ServerV40, 0, len(ids)) + returnable := make([]tc.ServerV41, 0, len(ids)) for _, id := range ids { server := servers[id] @@ -1057,7 +1084,7 @@ func getServers(h http.Header, params map[string]string, tx *sqlx.Tx, user *auth } // getMidServers gets the mids used by the edges provided with an option to filter for a given cdn -func getMidServers(edgeIDs []int, servers map[int]tc.ServerV40, dsID int, cdnID int, tx *sqlx.Tx, includeCapabilities bool) ([]int, error, error, int) { +func getMidServers(edgeIDs []int, servers map[int]tc.ServerV41, dsID int, cdnID int, tx *sqlx.Tx, includeCapabilities bool) ([]int, error, error, int) { if len(edgeIDs) == 0 { return nil, nil, nil, http.StatusOK } @@ -1123,8 +1150,10 @@ func getMidServers(edgeIDs []int, servers map[int]tc.ServerV40, dsID int, cdnID defer rows.Close() ids := []int{} + for rows.Next() { - var s tc.ServerV40 + var s tc.ServerV41 + var asns pq.Int32Array if err := rows.Scan(&s.Cachegroup, &s.CachegroupID, &s.CDNID, @@ -1161,10 +1190,12 @@ func getMidServers(edgeIDs []int, servers map[int]tc.ServerV40, dsID int, cdnID &s.ConfigApplyTime, &s.XMPPID, &s.XMPPPasswd, - &s.StatusLastUpdated); err != nil { + &s.StatusLastUpdated, + &asns); err != nil { log.Errorf("could not scan mid servers: %s\n", err) return nil, nil, err, http.StatusInternalServerError } + s.ASNs = asns if s.ID == nil { return nil, nil, errors.New("found server with nil ID"), http.StatusInternalServerError } @@ -1322,7 +1353,7 @@ func Update(w http.ResponseWriter, r *http.Request) { return } - original := originals[0] + original := originals[0].ServerV40 if original.XMPPID == nil || *original.XMPPID == "" { log.Warnf("original server %s had no XMPPID\n", *original.HostName) } @@ -1545,7 +1576,7 @@ func Update(w http.ResponseWriter, r *http.Request) { } else { selquery = selectQuery + where } - var srvr tc.ServerV40 + var srvr tc.ServerV41 err = inf.Tx.QueryRow(selquery, serverID).Scan(&srvr.Cachegroup, &srvr.CachegroupID, &srvr.CDNID, @@ -1582,7 +1613,8 @@ func Update(w http.ResponseWriter, r *http.Request) { &srvr.ConfigApplyTime, &srvr.XMPPID, &srvr.XMPPPasswd, - &srvr.StatusLastUpdated) + &srvr.StatusLastUpdated, + pq.Array(&srvr.ASNs)) if err != nil { api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err) return @@ -1603,7 +1635,7 @@ func Update(w http.ResponseWriter, r *http.Request) { api.HandleErr(w, r, tx, errCode, userErr, sysErr) return } - api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Server updated", srvr) + api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Server updated", srvr.ServerV40) changeLogMsg := fmt.Sprintf("SERVER: %s.%s, ID: %d, ACTION: updated", *srvr.HostName, *srvr.DomainName, *srvr.ID) api.CreateChangeLogRawTx(api.ApiChange, changeLogMsg, inf.User, tx) @@ -1769,7 +1801,7 @@ func createV3(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) { where := `WHERE s.id = $1` selquery := selectQuery + where - var s4 tc.ServerV40 + var s4 tc.ServerV41 err = inf.Tx.QueryRow(selquery, serverID).Scan(&s4.Cachegroup, &s4.CachegroupID, &s4.CDNID, @@ -1806,7 +1838,8 @@ func createV3(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) { &s4.ConfigApplyTime, &s4.XMPPID, &s4.XMPPPasswd, - &s4.StatusLastUpdated) + &s4.StatusLastUpdated, + pq.Array(&s4.ASNs)) if err != nil { api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err) return @@ -1903,7 +1936,7 @@ func createV4(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) { where := `WHERE s.id = $1` selquery := selectQuery + joinProfileV4 + where - var srvr tc.ServerV40 + var srvr tc.ServerV41 err = inf.Tx.QueryRow(selquery, serverID).Scan(&srvr.Cachegroup, &srvr.CachegroupID, &srvr.CDNID, @@ -1940,7 +1973,8 @@ func createV4(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) { &srvr.ConfigApplyTime, &srvr.XMPPID, &srvr.XMPPPasswd, - &srvr.StatusLastUpdated) + &srvr.StatusLastUpdated, + pq.Array(&srvr.ASNs)) if err != nil { api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err) return @@ -1950,7 +1984,7 @@ func createV4(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) { srvr.Interfaces = server.Interfaces alerts := tc.CreateAlerts(tc.SuccessLevel, "Server created") - api.WriteAlertsObj(w, r, http.StatusCreated, alerts, srvr) + api.WriteAlertsObj(w, r, http.StatusCreated, alerts, srvr.ServerV40) changeLogMsg := fmt.Sprintf("SERVER: %s.%s, ID: %d, ACTION: created", *srvr.HostName, *srvr.DomainName, *srvr.ID) api.CreateChangeLogRawTx(api.ApiChange, changeLogMsg, inf.User, inf.Tx.Tx) @@ -2154,7 +2188,7 @@ func Delete(w http.ResponseWriter, r *http.Request) { return } - var servers []tc.ServerV40 + var servers []tc.ServerV41 servers, _, userErr, sysErr, errCode, _ = getServers(r.Header, map[string]string{"id": inf.Params["id"]}, inf.Tx, inf.User, false, *version) if userErr != nil || sysErr != nil { api.HandleErr(w, r, tx, errCode, userErr, sysErr) @@ -2213,7 +2247,7 @@ func Delete(w http.ResponseWriter, r *http.Request) { } if inf.Version.Major >= 4 { - api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Server deleted", server) + api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Server deleted", server.ServerV40) } else { csp, err := dbhelpers.GetCommonServerPropertiesFromV4(server, tx) if err != nil { diff --git a/traffic_ops/traffic_ops_golang/server/servers_test.go b/traffic_ops/traffic_ops_golang/server/servers_test.go index 8283284143..e7f3ee6f5f 100644 --- a/traffic_ops/traffic_ops_golang/server/servers_test.go +++ b/traffic_ops/traffic_ops_golang/server/servers_test.go @@ -32,17 +32,18 @@ import ( "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api" "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/auth" "github.com/jmoiron/sqlx" + "github.com/lib/pq" "gopkg.in/DATA-DOG/go-sqlmock.v1" ) type ServerAndInterfaces struct { - Server tc.ServerV40 + Server tc.ServerV41 Interface tc.ServerInterfaceInfoV40 } func getTestServers() []ServerAndInterfaces { servers := []ServerAndInterfaces{} - testServer := tc.ServerV40{ + testServerV40 := tc.ServerV40{ Cachegroup: util.StrPtr("Cachegroup"), CachegroupID: util.IntPtr(1), CDNID: util.IntPtr(1), @@ -81,7 +82,10 @@ func getTestServers() []ServerAndInterfaces { RevalUpdateTime: &(time.Time{}), RevalApplyTime: &(time.Time{}), } - + testServer := tc.ServerV41{ + ServerV40: testServerV40, + ASNs: pq.Int32Array{1, 2}, + } mtu := uint64(9500) iface := tc.ServerInterfaceInfoV40{ @@ -179,7 +183,7 @@ func TestGetServersByCachegroup(t *testing.T) { "last_updated", "mgmt_ip_address", "mgmt_ip_gateway", "mgmt_ip_netmask", "offline_reason", "phys_location", "phys_location_id", "profile_name", "rack", "reval_pending", "revalidate_update_time", "revalidate_apply_time", "status", "status_id", "tcp_port", "server_type", "server_type_id", "upd_pending", "config_update_time", - "config_apply_time", "xmpp_id", "xmpp_passwd", "status_last_updated"} + "config_apply_time", "xmpp_id", "xmpp_passwd", "status_last_updated", "asns"} interfaceCols := []string{"max_bandwidth", "monitor", "mtu", "name", "server", "router_host_name", "router_port_name"} rows := sqlmock.NewRows(cols) interfaceRows := sqlmock.NewRows(interfaceCols) @@ -191,6 +195,8 @@ func TestGetServersByCachegroup(t *testing.T) { // or by CSV if types get in the way for _, srv := range testServers { ts := srv.Server + asns := &pq.Int32Array{} + v, _ := asns.Value() rows = rows.AddRow( *ts.Cachegroup, *ts.CachegroupID, @@ -229,6 +235,7 @@ func TestGetServersByCachegroup(t *testing.T) { *ts.XMPPID, *ts.XMPPPasswd, *ts.StatusLastUpdated, + v, ) interfaceRows = interfaceRows.AddRow( srv.Interface.MaxBandwidth, @@ -300,7 +307,7 @@ func TestGetMidServers(t *testing.T) { "last_updated", "mgmt_ip_address", "mgmt_ip_gateway", "mgmt_ip_netmask", "offline_reason", "phys_location", "phys_location_id", "profile_name", "rack", "reval_pending", "revalidate_update_time", "revalidate_apply_time", "status", "status_id", "tcp_port", "server_type", "server_type_id", "upd_pending", "config_update_time", - "config_apply_time", "xmpp_id", "xmpp_passwd", "status_last_updated"} + "config_apply_time", "xmpp_id", "xmpp_passwd", "status_last_updated", "asns"} interfaceCols := []string{"max_bandwidth", "monitor", "mtu", "name", "server", "router_host_name", "router_port_name"} rows := sqlmock.NewRows(cols) interfaceRows := sqlmock.NewRows(interfaceCols) @@ -310,6 +317,8 @@ func TestGetMidServers(t *testing.T) { for _, srv := range testServers { ts := srv.Server + asns := &pq.Int32Array{} + asnValue, _ := asns.Value() rows = rows.AddRow( *ts.Cachegroup, *ts.CachegroupID, @@ -348,6 +357,7 @@ func TestGetMidServers(t *testing.T) { *ts.XMPPID, *ts.XMPPPasswd, *ts.StatusLastUpdated, + asnValue, ) interfaceRows = interfaceRows.AddRow( srv.Interface.MaxBandwidth, @@ -391,7 +401,7 @@ func TestGetMidServers(t *testing.T) { "last_updated", "mgmt_ip_address", "mgmt_ip_gateway", "mgmt_ip_netmask", "offline_reason", "phys_location", "phys_location_id", "profile_name", "rack", "reval_pending", "revalidate_update_time", "revalidate_apply_time", "status", "status_id", "tcp_port", "server_type", "server_type_id", "upd_pending", "config_update_time", - "config_apply_time", "xmpp_id", "xmpp_passwd", "status_last_updated"} + "config_apply_time", "xmpp_id", "xmpp_passwd", "status_last_updated", "asns"} rows2 := sqlmock.NewRows(cols2) cgs := []tc.CacheGroup{} @@ -437,7 +447,7 @@ func TestGetMidServers(t *testing.T) { } cgs = append(cgs, testCG2) - serverMap := make(map[int]tc.ServerV40, len(servers)) + serverMap := make(map[int]tc.ServerV41, len(servers)) serverIDs := make([]int, 0, len(servers)) for _, server := range servers { if server.ID == nil { @@ -447,13 +457,15 @@ func TestGetMidServers(t *testing.T) { serverMap[*server.ID] = server } - var ts tc.ServerV40 + var ts tc.ServerV41 for _, s := range servers { if s.HostName != nil && *s.HostName == "server2" { ts = s break } } + asns := &pq.Int32Array{} + asnValue, _ := asns.Value() *ts.ID = *ts.ID + 1 rows2 = rows2.AddRow( *ts.Cachegroup, @@ -493,6 +505,7 @@ func TestGetMidServers(t *testing.T) { *ts.XMPPID, *ts.XMPPPasswd, *ts.StatusLastUpdated, + asnValue, ) mock.ExpectBegin() diff --git a/traffic_portal/app/src/common/modules/table/servers/TableServersController.js b/traffic_portal/app/src/common/modules/table/servers/TableServersController.js index 761f54cb36..7c8e994195 100644 --- a/traffic_portal/app/src/common/modules/table/servers/TableServersController.js +++ b/traffic_portal/app/src/common/modules/table/servers/TableServersController.js @@ -155,6 +155,11 @@ var TableServersController = function(tableName, servers, filter, $scope, $state field: "offlineReason", hide: true }, + { + headerName: "ASNs", + field: "asns", + hide: true + }, { headerName: "Phys Location", field: "physLocation", diff --git a/traffic_router/ultimate-test-harness/http_test.go b/traffic_router/ultimate-test-harness/http_test.go index 9b9fa8ce3e..ed67bc2262 100644 --- a/traffic_router/ultimate-test-harness/http_test.go +++ b/traffic_router/ultimate-test-harness/http_test.go @@ -337,10 +337,14 @@ func getTrafficRouters(trafficRouterName string, cdnName tc.CDNName) ([]tc.Serve return nil, fmt.Errorf("requesting %s-status Traffic Routers: %s", requestOptions.QueryParameters["status"], err.Error()) } trafficRouters := response.Response + trafficRoutersV40 := make([]tc.ServerV40, 0) + for _, tr := range trafficRouters { + trafficRoutersV40 = append(trafficRoutersV40, tr.ServerV40) + } if len(trafficRouters) < 1 { - return trafficRouters, fmt.Errorf("no Traffic Routers were found with these criteria: %v", requestOptions.QueryParameters) + return trafficRoutersV40, fmt.Errorf("no Traffic Routers were found with these criteria: %v", requestOptions.QueryParameters) } - return trafficRouters, nil + return trafficRoutersV40, nil } func getDSes(t *testing.T, cdnId int, dsTypeName tc.DSType, dsName tc.DeliveryServiceName) []tc.DeliveryServiceV40 { From 4a770f5773c358454ffc1a568ca58c3021a7216f Mon Sep 17 00:00:00 2001 From: Srijeet Chatterjee Date: Tue, 16 Aug 2022 09:57:56 -0600 Subject: [PATCH 2/8] doc changes --- docs/source/api/v4/servers.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/source/api/v4/servers.rst b/docs/source/api/v4/servers.rst index 78e5d1f397..54f7eee038 100644 --- a/docs/source/api/v4/servers.rst +++ b/docs/source/api/v4/servers.rst @@ -69,6 +69,10 @@ Request Structure | | | the first page is 1. If ``offset`` was defined, this query parameter has no effect. ``limit`` must be defined to | | | | make use of ``page``. | +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ + | asn | no | Return only the servers that have a cachegroup matching the provided ASN. | + | | | .. versionadded:: 4.1 | + +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ + .. code-block:: http :caption: Request Example @@ -159,6 +163,8 @@ Response Structure :updPending: A boolean value which, if ``true``, indicates that the server has updates of some kind pending, typically to be acted upon by Traffic Control Cache Config (:term:`t3c`, formerly ORT) :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 +:asns: The :abbr:`ASN (Autonomous System Number)` associated with the cachegroups of the current server. + .. versionadded:: 4.0 .. code-block:: http :caption: Response Example @@ -225,6 +231,10 @@ Response Structure "routerHostName": "", "routerPortName": "" } + ], + "asns": [ + 1, + 2 ] }], "summary": { From a13614ada12696f519f81a2c1c0ad07669536d17 Mon Sep 17 00:00:00 2001 From: Srijeet Chatterjee Date: Tue, 16 Aug 2022 10:05:36 -0600 Subject: [PATCH 3/8] fix typo --- docs/source/api/v4/servers.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/api/v4/servers.rst b/docs/source/api/v4/servers.rst index 54f7eee038..949944da39 100644 --- a/docs/source/api/v4/servers.rst +++ b/docs/source/api/v4/servers.rst @@ -164,7 +164,7 @@ Response Structure :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 :asns: The :abbr:`ASN (Autonomous System Number)` associated with the cachegroups of the current server. - .. versionadded:: 4.0 + .. versionadded:: 4.1 .. code-block:: http :caption: Response Example From c91f54fc90340ce7310e939cc520b60fbc2d1f87 Mon Sep 17 00:00:00 2001 From: Srijeet Chatterjee Date: Tue, 16 Aug 2022 10:11:01 -0600 Subject: [PATCH 4/8] add godoc --- lib/go-tc/servers.go | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/go-tc/servers.go b/lib/go-tc/servers.go index a08fa9dfea..7dbcca5cde 100644 --- a/lib/go-tc/servers.go +++ b/lib/go-tc/servers.go @@ -1021,6 +1021,7 @@ func UpdateServerPropertiesV40(profileNames []string, properties CommonServerPro } } +// ServerV41 is the representation of a Server in version 4.1 of the Traffic Ops API. type ServerV41 struct { ServerV40 ASNs pq.Int32Array `json:"asns"` From 26ebe783bd0514b77607e42f5b1a088931af173f Mon Sep 17 00:00:00 2001 From: Srijeet Chatterjee Date: Thu, 18 Aug 2022 12:29:18 -0600 Subject: [PATCH 5/8] address code review comments --- docs/source/api/v4/servers.rst | 3 ++- lib/go-tc/servers.go | 4 +--- traffic_ops/traffic_ops_golang/server/servers.go | 8 ++------ traffic_ops/traffic_ops_golang/server/servers_test.go | 8 ++++---- 4 files changed, 9 insertions(+), 14 deletions(-) diff --git a/docs/source/api/v4/servers.rst b/docs/source/api/v4/servers.rst index 949944da39..91c3892317 100644 --- a/docs/source/api/v4/servers.rst +++ b/docs/source/api/v4/servers.rst @@ -70,7 +70,8 @@ Request Structure | | | make use of ``page``. | +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ | asn | no | Return only the servers that have a cachegroup matching the provided ASN. | - | | | .. versionadded:: 4.1 | + | | | | + | | |.. versionadded:: 4.1 | +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ diff --git a/lib/go-tc/servers.go b/lib/go-tc/servers.go index 7dbcca5cde..9a02cbcfb9 100644 --- a/lib/go-tc/servers.go +++ b/lib/go-tc/servers.go @@ -30,8 +30,6 @@ import ( "time" "github.com/apache/trafficcontrol/lib/go-util" - - "github.com/lib/pq" ) // ServersV4Response is the format of a response to a GET request for API v4.x /servers. @@ -1024,7 +1022,7 @@ func UpdateServerPropertiesV40(profileNames []string, properties CommonServerPro // ServerV41 is the representation of a Server in version 4.1 of the Traffic Ops API. type ServerV41 struct { ServerV40 - ASNs pq.Int32Array `json:"asns"` + ASNs []int64 `json:"asns"` } // ServerV40 is the representation of a Server in version 4.0 of the Traffic Ops API. diff --git a/traffic_ops/traffic_ops_golang/server/servers.go b/traffic_ops/traffic_ops_golang/server/servers.go index cb49d5aa74..e57d161258 100644 --- a/traffic_ops/traffic_ops_golang/server/servers.go +++ b/traffic_ops/traffic_ops_golang/server/servers.go @@ -925,7 +925,6 @@ JOIN server_profile sp ON s.id = sp.server` servers := make(map[int]tc.ServerV41) ids := []int{} - var asns pq.Int32Array for rows.Next() { s := tc.ServerV41{} err := rows.Scan(&s.Cachegroup, @@ -965,8 +964,7 @@ JOIN server_profile sp ON s.id = sp.server` &s.XMPPID, &s.XMPPPasswd, &s.StatusLastUpdated, - &asns) - s.ASNs = asns + pq.Array(&s.ASNs)) if err != nil { return nil, serverCount, nil, errors.New("getting servers: " + err.Error()), http.StatusInternalServerError, nil } @@ -1153,7 +1151,6 @@ func getMidServers(edgeIDs []int, servers map[int]tc.ServerV41, dsID int, cdnID for rows.Next() { var s tc.ServerV41 - var asns pq.Int32Array if err := rows.Scan(&s.Cachegroup, &s.CachegroupID, &s.CDNID, @@ -1191,11 +1188,10 @@ func getMidServers(edgeIDs []int, servers map[int]tc.ServerV41, dsID int, cdnID &s.XMPPID, &s.XMPPPasswd, &s.StatusLastUpdated, - &asns); err != nil { + pq.Array(&s.ASNs)); err != nil { log.Errorf("could not scan mid servers: %s\n", err) return nil, nil, err, http.StatusInternalServerError } - s.ASNs = asns if s.ID == nil { return nil, nil, errors.New("found server with nil ID"), http.StatusInternalServerError } diff --git a/traffic_ops/traffic_ops_golang/server/servers_test.go b/traffic_ops/traffic_ops_golang/server/servers_test.go index e7f3ee6f5f..c832e9d000 100644 --- a/traffic_ops/traffic_ops_golang/server/servers_test.go +++ b/traffic_ops/traffic_ops_golang/server/servers_test.go @@ -84,7 +84,7 @@ func getTestServers() []ServerAndInterfaces { } testServer := tc.ServerV41{ ServerV40: testServerV40, - ASNs: pq.Int32Array{1, 2}, + ASNs: pq.Int64Array{1, 2}, } mtu := uint64(9500) @@ -195,7 +195,7 @@ func TestGetServersByCachegroup(t *testing.T) { // or by CSV if types get in the way for _, srv := range testServers { ts := srv.Server - asns := &pq.Int32Array{} + asns := &pq.Int64Array{} v, _ := asns.Value() rows = rows.AddRow( *ts.Cachegroup, @@ -317,7 +317,7 @@ func TestGetMidServers(t *testing.T) { for _, srv := range testServers { ts := srv.Server - asns := &pq.Int32Array{} + asns := &pq.Int64Array{} asnValue, _ := asns.Value() rows = rows.AddRow( *ts.Cachegroup, @@ -464,7 +464,7 @@ func TestGetMidServers(t *testing.T) { break } } - asns := &pq.Int32Array{} + asns := &pq.Int64Array{} asnValue, _ := asns.Value() *ts.ID = *ts.ID + 1 rows2 = rows2.AddRow( From fcf5071e2967d02f5eb8423a50b8816fc922da03 Mon Sep 17 00:00:00 2001 From: Srijeet Chatterjee Date: Mon, 22 Aug 2022 17:33:25 -0600 Subject: [PATCH 6/8] address code review changes --- traffic_ops/testing/api/v4/servers_test.go | 50 ++++++++++--------- traffic_ops/testing/api/v4/tc-fixtures.json | 4 +- .../traffic_ops_golang/routing/routes.go | 2 +- .../traffic_ops_golang/server/servers_test.go | 12 ++--- 4 files changed, 32 insertions(+), 36 deletions(-) diff --git a/traffic_ops/testing/api/v4/servers_test.go b/traffic_ops/testing/api/v4/servers_test.go index 2e70319b1e..a9defc9684 100644 --- a/traffic_ops/testing/api/v4/servers_test.go +++ b/traffic_ops/testing/api/v4/servers_test.go @@ -38,30 +38,7 @@ func TestServers(t *testing.T) { currentTime := time.Now().UTC().Add(-15 * time.Second) currentTimeRFC := currentTime.Format(time.RFC1123) tomorrow := currentTime.AddDate(0, 0, 1).Format(time.RFC1123) - - // Add a new asn for one of the cachegroups - cgResp, _, err := TOSession.GetCacheGroups(client.RequestOptions{QueryParameters: url.Values{"name": {"topology-mid-cg-01"}}}) - if err != nil { - t.Fatalf("couldn't get cachegroups: %v", err) - } - if len(cgResp.Response) != 1 { - t.Fatalf("expected 1 cachegroup, but got %d", len(cgResp.Response)) - } - if cgResp.Response[0].ID == nil { - t.Fatalf("ID of cachegroup is nil") - } - - asn := tc.ASN{ - ASN: 1111, - Cachegroup: "topology-mid-cg-01", - CachegroupID: *cgResp.Response[0].ID, - } - - _, _, err = TOSession.CreateASN(asn, client.NewRequestOptions()) - if err != nil { - t.Fatalf("couldn't create ASN: %v", err) - } - + setupCacheGroupWithASN(t) methodTests := utils.V4TestCase{ "GET": { "NOT MODIFIED when NO CHANGES made": { @@ -423,6 +400,31 @@ func TestServers(t *testing.T) { }) } +func setupCacheGroupWithASN(t *testing.T) { + // Add a new asn for one of the cachegroups + cgResp, _, err := TOSession.GetCacheGroups(client.RequestOptions{QueryParameters: url.Values{"name": {"topology-mid-cg-01"}}}) + if err != nil { + t.Fatalf("couldn't get cachegroups: %v", err) + } + if len(cgResp.Response) != 1 { + t.Fatalf("expected 1 cachegroup, but got %d", len(cgResp.Response)) + } + if cgResp.Response[0].ID == nil { + t.Fatalf("ID of cachegroup is nil") + } + + asn := tc.ASN{ + ASN: 1111, + Cachegroup: "topology-mid-cg-01", + CachegroupID: *cgResp.Response[0].ID, + } + + _, _, err = TOSession.CreateASN(asn, client.NewRequestOptions()) + if err != nil { + t.Fatalf("couldn't create ASN: %v", err) + } +} + func validateServerFields(expectedResp map[string]interface{}) utils.CkReqFunc { return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ tc.Alerts, _ error) { assert.RequireNotNil(t, resp, "Expected response to not be nil.") diff --git a/traffic_ops/testing/api/v4/tc-fixtures.json b/traffic_ops/testing/api/v4/tc-fixtures.json index 3b4e823fa3..8f34bb26fc 100644 --- a/traffic_ops/testing/api/v4/tc-fixtures.json +++ b/traffic_ops/testing/api/v4/tc-fixtures.json @@ -2,11 +2,11 @@ "asns": [ { "asn": 8888, - "cachegroup": "originCachegroup" + "cachegroupName": "originCachegroup" }, { "asn": 9999, - "cachegroup": "multiOriginCachegroup" + "cachegroupName": "multiOriginCachegroup" } ], "cachegroups": [ diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go b/traffic_ops/traffic_ops_golang/routing/routes.go index 032c3b5640..98e833408d 100644 --- a/traffic_ops/traffic_ops_golang/routing/routes.go +++ b/traffic_ops/traffic_ops_golang/routing/routes.go @@ -131,7 +131,7 @@ func Routes(d ServerData) ([]Route, http.Handler, error) { * 4.x API */ - // 4.1 GET servers + // GET servers {Version: api.Version{Major: 4, Minor: 1}, Method: http.MethodGet, Path: `servers/?$`, Handler: server.Read, 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: 47219592853}, // Assign Multiple Server Capabilities {Version: api.Version{Major: 4, Minor: 1}, Method: http.MethodPut, Path: `multiple_server_capabilities/?$`, Handler: server.AssignMultipleServerCapabilities, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:UPDATE", "SERVER:READ", "SERVER-CAPABILITY:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 40792419258}, diff --git a/traffic_ops/traffic_ops_golang/server/servers_test.go b/traffic_ops/traffic_ops_golang/server/servers_test.go index c832e9d000..a7243f38c5 100644 --- a/traffic_ops/traffic_ops_golang/server/servers_test.go +++ b/traffic_ops/traffic_ops_golang/server/servers_test.go @@ -195,8 +195,6 @@ func TestGetServersByCachegroup(t *testing.T) { // or by CSV if types get in the way for _, srv := range testServers { ts := srv.Server - asns := &pq.Int64Array{} - v, _ := asns.Value() rows = rows.AddRow( *ts.Cachegroup, *ts.CachegroupID, @@ -235,7 +233,7 @@ func TestGetServersByCachegroup(t *testing.T) { *ts.XMPPID, *ts.XMPPPasswd, *ts.StatusLastUpdated, - v, + []byte(`{1,2}`), ) interfaceRows = interfaceRows.AddRow( srv.Interface.MaxBandwidth, @@ -317,8 +315,6 @@ func TestGetMidServers(t *testing.T) { for _, srv := range testServers { ts := srv.Server - asns := &pq.Int64Array{} - asnValue, _ := asns.Value() rows = rows.AddRow( *ts.Cachegroup, *ts.CachegroupID, @@ -357,7 +353,7 @@ func TestGetMidServers(t *testing.T) { *ts.XMPPID, *ts.XMPPPasswd, *ts.StatusLastUpdated, - asnValue, + []byte(`{1,2}`), ) interfaceRows = interfaceRows.AddRow( srv.Interface.MaxBandwidth, @@ -464,8 +460,6 @@ func TestGetMidServers(t *testing.T) { break } } - asns := &pq.Int64Array{} - asnValue, _ := asns.Value() *ts.ID = *ts.ID + 1 rows2 = rows2.AddRow( *ts.Cachegroup, @@ -505,7 +499,7 @@ func TestGetMidServers(t *testing.T) { *ts.XMPPID, *ts.XMPPPasswd, *ts.StatusLastUpdated, - asnValue, + []byte(`{1,2}`), ) mock.ExpectBegin() From e34b0dad84df2262d6fe4cd7b616431cc7e80906 Mon Sep 17 00:00:00 2001 From: Srijeet Chatterjee Date: Wed, 24 Aug 2022 17:51:17 -0600 Subject: [PATCH 7/8] address code review comments --- docs/source/api/v4/servers.rst | 5 ++++- infrastructure/cdn-in-a-box/enroller/enroller.go | 2 +- lib/go-tc/servers.go | 2 +- traffic_ops/testing/api/v4/servers_test.go | 6 +++--- .../testing/api/v4/serverupdatestatus_test.go | 16 ++++++++-------- 5 files changed, 17 insertions(+), 14 deletions(-) diff --git a/docs/source/api/v4/servers.rst b/docs/source/api/v4/servers.rst index 91c3892317..b350ab78b4 100644 --- a/docs/source/api/v4/servers.rst +++ b/docs/source/api/v4/servers.rst @@ -71,7 +71,7 @@ Request Structure +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ | asn | no | Return only the servers that have a cachegroup matching the provided ASN. | | | | | - | | |.. versionadded:: 4.1 | + | | | .. versionadded:: 4.1 | +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ @@ -86,6 +86,9 @@ Request Structure Response Structure ------------------ +:asns: The :abbr:`ASN (Autonomous System Number)` associated with the cachegroups of the current server. + + .. versionadded:: 4.1 :cachegroup: A string that is the :ref:`name of the Cache Group ` to which the server belongs :cachegroupId: An integer that is the :ref:`ID of the Cache Group ` to which the server belongs :cdnId: The integral, unique identifier of the CDN to which the server belongs diff --git a/infrastructure/cdn-in-a-box/enroller/enroller.go b/infrastructure/cdn-in-a-box/enroller/enroller.go index 0633ab51bb..f8b61407ce 100644 --- a/infrastructure/cdn-in-a-box/enroller/enroller.go +++ b/infrastructure/cdn-in-a-box/enroller/enroller.go @@ -757,7 +757,7 @@ func enrollServer(toSession *session, r io.Reader) error { return err } - alerts, _, err := toSession.CreateServer(s, client.RequestOptions{}) + alerts, _, err := toSession.CreateServer(tc.ServerV4{ServerV40: s}, client.RequestOptions{}) if err != nil { err = fmt.Errorf("error creating Server: %v - alerts: %+v", err, alerts.Alerts) log.Infoln(err) diff --git a/lib/go-tc/servers.go b/lib/go-tc/servers.go index 9a02cbcfb9..8ef5f24e91 100644 --- a/lib/go-tc/servers.go +++ b/lib/go-tc/servers.go @@ -1072,7 +1072,7 @@ type ServerV40 struct { // ServerV4 is the representation of a Server in the latest minor version of // version 4 of the Traffic Ops API. -type ServerV4 = ServerV40 +type ServerV4 = ServerV41 // ServerV30 is the representation of a Server in version 3 of the Traffic Ops API. type ServerV30 struct { diff --git a/traffic_ops/testing/api/v4/servers_test.go b/traffic_ops/testing/api/v4/servers_test.go index a9defc9684..61857b5ec2 100644 --- a/traffic_ops/testing/api/v4/servers_test.go +++ b/traffic_ops/testing/api/v4/servers_test.go @@ -600,7 +600,7 @@ func UpdateTestServerStatusLastUpdated(t *testing.T) { originalServer := resp.Response[0].ServerV40 // Perform an update with no changes to status - alerts, _, err := TOSession.UpdateServer(*originalServer.ID, originalServer, client.RequestOptions{}) + alerts, _, err := TOSession.UpdateServer(*originalServer.ID, tc.ServerV4{ServerV40: originalServer}, client.RequestOptions{}) assert.RequireNoError(t, err, "Cannot UPDATE Server by ID %d (hostname '%s'): %v - alerts: %+v", *originalServer.ID, hostName, err, alerts) resp, _, err = TOSession.GetServers(opts) @@ -615,7 +615,7 @@ func UpdateTestServerStatusLastUpdated(t *testing.T) { newStatusID := GetStatusID(t, "ONLINE")() originalServer.StatusID = &newStatusID - alerts, _, err = TOSession.UpdateServer(*originalServer.ID, originalServer, client.RequestOptions{}) + alerts, _, err = TOSession.UpdateServer(*originalServer.ID, tc.ServerV4{ServerV40: originalServer}, client.RequestOptions{}) assert.RequireNoError(t, err, "Cannot UPDATE Server by ID %d (hostname '%s'): %v - alerts: %+v", *originalServer.ID, hostName, err, alerts) resp, _, err = TOSession.GetServers(opts) @@ -669,7 +669,7 @@ func UpdateDSGetServerDSID(t *testing.T) { func CreateTestServers(t *testing.T) { for _, server := range testData.Servers { - resp, _, err := TOSession.CreateServer(server, client.RequestOptions{}) + resp, _, err := TOSession.CreateServer(tc.ServerV4{ServerV40: server}, client.RequestOptions{}) assert.RequireNoError(t, err, "Could not create server '%s': %v - alerts: %+v", *server.HostName, err, resp.Alerts) } } diff --git a/traffic_ops/testing/api/v4/serverupdatestatus_test.go b/traffic_ops/testing/api/v4/serverupdatestatus_test.go index 5a63e9adc6..efb83c63a2 100644 --- a/traffic_ops/testing/api/v4/serverupdatestatus_test.go +++ b/traffic_ops/testing/api/v4/serverupdatestatus_test.go @@ -103,19 +103,19 @@ func TestServerUpdateStatus(t *testing.T) { }{ { "atlanta-edge-01", - &edge1cdn1, + &edge1cdn1.ServerV40, }, { "atlanta-edge-03", - &edge2cdn1, + &edge2cdn1.ServerV40, }, { "atlanta-mid-16", - &mid1cdn1, + &mid1cdn1.ServerV40, }, { "edge1-cdn2", - &edge1cdn2, + &edge1cdn2.ServerV40, }, } { opts.QueryParameters.Set("hostName", s.name) @@ -289,7 +289,7 @@ func TestServerQueueUpdate(t *testing.T) { t.Errorf("Expected exactly one server named '%s' to exist", serverName) t.Logf("Testing will proceed with server: %+v", resp.Response[0]) } - s = resp.Response[0].ServerV40 + s = resp.Response[0] // assert that servers don't have updates pending if s.UpdPending == nil { @@ -329,7 +329,7 @@ func TestServerQueueUpdate(t *testing.T) { t.Errorf("Expected exactly one server named '%s' to exist", serverName) t.Logf("Testing will proceed with server: %+v", resp.Response[0]) } - s = resp.Response[0].ServerV40 + s = resp.Response[0] if s.UpdPending == nil { t.Fatalf("Server '%s' had null (or missing) updPending property", serverName) } @@ -515,7 +515,7 @@ func TestSetTopologiesServerUpdateStatuses(t *testing.T) { if _, ok := cachesByCDNCacheGroup[*s.CDNName]; !ok { cachesByCDNCacheGroup[*s.CDNName] = make(map[string][]tc.ServerV4) } - cachesByCDNCacheGroup[*s.CDNName][cacheGroupName] = append(cachesByCDNCacheGroup[*s.CDNName][cacheGroupName], s.ServerV40) + cachesByCDNCacheGroup[*s.CDNName][cacheGroupName] = append(cachesByCDNCacheGroup[*s.CDNName][cacheGroupName], s) } } cdnNames := make([]string, 0, len(cachesByCDNCacheGroup)) @@ -554,7 +554,7 @@ func TestSetTopologiesServerUpdateStatuses(t *testing.T) { if len(cachesByCDNCacheGroup[*s.CDNName][cacheGroupName]) > 0 { cachesByCDNCacheGroup[*s.CDNName][cacheGroupName] = []tc.ServerV4{} } - cachesByCDNCacheGroup[*s.CDNName][cacheGroupName] = append(cachesByCDNCacheGroup[*s.CDNName][cacheGroupName], s.ServerV40) + cachesByCDNCacheGroup[*s.CDNName][cacheGroupName] = append(cachesByCDNCacheGroup[*s.CDNName][cacheGroupName], s) } } for _, cacheGroupName := range cacheGroupNames { From 21e1d71168e31f502f05b377aebfaff2c79ee015 Mon Sep 17 00:00:00 2001 From: Srijeet Chatterjee Date: Wed, 24 Aug 2022 22:55:27 -0600 Subject: [PATCH 8/8] remove extra doc line --- docs/source/api/v4/servers.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/source/api/v4/servers.rst b/docs/source/api/v4/servers.rst index b350ab78b4..1a57951d0a 100644 --- a/docs/source/api/v4/servers.rst +++ b/docs/source/api/v4/servers.rst @@ -167,8 +167,6 @@ Response Structure :updPending: A boolean value which, if ``true``, indicates that the server has updates of some kind pending, typically to be acted upon by Traffic Control Cache Config (:term:`t3c`, formerly ORT) :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 -:asns: The :abbr:`ASN (Autonomous System Number)` associated with the cachegroups of the current server. - .. versionadded:: 4.1 .. code-block:: http :caption: Response Example