From b1502aebf0d7d9c2ae95710bb671b5d2f85b65e1 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Wed, 2 Aug 2023 17:49:28 -0600 Subject: [PATCH 01/31] Add some missing deprecation notices --- lib/go-tc/servers.go | 112 +++++++++++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 41 deletions(-) diff --git a/lib/go-tc/servers.go b/lib/go-tc/servers.go index 2f435cdb3f..ce286f4afe 100644 --- a/lib/go-tc/servers.go +++ b/lib/go-tc/servers.go @@ -704,25 +704,39 @@ type CommonServerProperties struct { ILOPassword *string `json:"iloPassword" db:"ilo_password"` ILOUsername *string `json:"iloUsername" db:"ilo_username"` LastUpdated *TimeNoMod `json:"lastUpdated" db:"last_updated"` - MgmtIPAddress *string `json:"mgmtIpAddress" db:"mgmt_ip_address"` - MgmtIPGateway *string `json:"mgmtIpGateway" db:"mgmt_ip_gateway"` - MgmtIPNetmask *string `json:"mgmtIpNetmask" db:"mgmt_ip_netmask"` - OfflineReason *string `json:"offlineReason" db:"offline_reason"` - PhysLocation *string `json:"physLocation" db:"phys_location"` - PhysLocationID *int `json:"physLocationId" db:"phys_location_id"` - Profile *string `json:"profile" db:"profile"` - ProfileDesc *string `json:"profileDesc" db:"profile_desc"` - ProfileID *int `json:"profileId" db:"profile_id"` - Rack *string `json:"rack" db:"rack"` - RevalPending *bool `json:"revalPending" db:"reval_pending"` - Status *string `json:"status" db:"status"` - StatusID *int `json:"statusId" db:"status_id"` - TCPPort *int `json:"tcpPort" db:"tcp_port"` - Type string `json:"type" db:"server_type"` - TypeID *int `json:"typeId" db:"server_type_id"` - UpdPending *bool `json:"updPending" db:"upd_pending"` - XMPPID *string `json:"xmppId" db:"xmpp_id"` - XMPPPasswd *string `json:"xmppPasswd" db:"xmpp_passwd"` + // Deprecated: In the future, management interfaces must be configured as + // interfaces within the Interfaces of the server, not separately on these + // properties. + MgmtIPAddress *string `json:"mgmtIpAddress" db:"mgmt_ip_address"` + // Deprecated: In the future, management interfaces must be configured as + // interfaces within the Interfaces of the server, not separately on these + // properties. + MgmtIPGateway *string `json:"mgmtIpGateway" db:"mgmt_ip_gateway"` + // Deprecated: In the future, management interfaces must be configured as + // interfaces within the Interfaces of the server, not separately on these + // properties. + MgmtIPNetmask *string `json:"mgmtIpNetmask" db:"mgmt_ip_netmask"` + OfflineReason *string `json:"offlineReason" db:"offline_reason"` + PhysLocation *string `json:"physLocation" db:"phys_location"` + PhysLocationID *int `json:"physLocationId" db:"phys_location_id"` + Profile *string `json:"profile" db:"profile"` + // Deprecated: In API versions 4 and later, Profile descriptions must be + // taken from the Profiles themselves, and Servers only contain identifying + // information for their Profiles. + ProfileDesc *string `json:"profileDesc" db:"profile_desc"` + // Deprecated: In API versions 4 and later, Servers identify their Profiles + // by Name, not ID. + ProfileID *int `json:"profileId" db:"profile_id"` + Rack *string `json:"rack" db:"rack"` + RevalPending *bool `json:"revalPending" db:"reval_pending"` + Status *string `json:"status" db:"status"` + StatusID *int `json:"statusId" db:"status_id"` + TCPPort *int `json:"tcpPort" db:"tcp_port"` + Type string `json:"type" db:"server_type"` + TypeID *int `json:"typeId" db:"server_type_id"` + UpdPending *bool `json:"updPending" db:"upd_pending"` + XMPPID *string `json:"xmppId" db:"xmpp_id"` + XMPPPasswd *string `json:"xmppPasswd" db:"xmpp_passwd"` } // ServerNullableV11 is a server as it appeared in API version 1.1. @@ -957,7 +971,7 @@ func (s ServerV30) UpgradeToV40(profileNames []string) (ServerV40, error) { // // This makes a "shallow" copy of the structure's properties. // -// Deprecated: Traffic Ops API version 2 is deprecated, new code should use +// Deprecated: Traffic Ops API version 2 is gone, new code should use // ServerV40 or newer structures. func (s ServerNullableV2) UpgradeToV40(profileNames []string) (ServerV40, error) { ipv4IsService := false @@ -978,7 +992,11 @@ func (s ServerNullableV2) UpgradeToV40(profileNames []string) (ServerV40, error) return upgraded, nil } -// UpdateServerPropertiesV40 updates CommonServerProperties of V2 and V3 to ServerV40 +// UpdateServerPropertiesV40 updates CommonServerProperties of V2 and V3 to +// ServerV40. +// +// Deprecated: Traffic Ops API version 3 is deprecated, new code should use +// ServerV40 or newer structures. func UpdateServerPropertiesV40(profileNames []string, properties CommonServerProperties) ServerV40 { return ServerV40{ Cachegroup: properties.Cachegroup, @@ -1021,26 +1039,35 @@ func UpdateServerPropertiesV40(profileNames []string, properties CommonServerPro // 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"` - CachegroupID *int `json:"cachegroupId" db:"cachegroup_id"` - CDNID *int `json:"cdnId" db:"cdn_id"` - CDNName *string `json:"cdnName" db:"cdn_name"` - DeliveryServices *map[string][]string `json:"deliveryServices,omitempty"` - DomainName *string `json:"domainName" db:"domain_name"` - FQDN *string `json:"fqdn,omitempty"` - FqdnTime time.Time `json:"-"` - GUID *string `json:"guid" db:"guid"` - HostName *string `json:"hostName" db:"host_name"` - HTTPSPort *int `json:"httpsPort" db:"https_port"` - ID *int `json:"id" db:"id"` - ILOIPAddress *string `json:"iloIpAddress" db:"ilo_ip_address"` - ILOIPGateway *string `json:"iloIpGateway" db:"ilo_ip_gateway"` - ILOIPNetmask *string `json:"iloIpNetmask" db:"ilo_ip_netmask"` - ILOPassword *string `json:"iloPassword" db:"ilo_password"` - ILOUsername *string `json:"iloUsername" db:"ilo_username"` - LastUpdated *TimeNoMod `json:"lastUpdated" db:"last_updated"` - MgmtIPAddress *string `json:"mgmtIpAddress" db:"mgmt_ip_address"` - MgmtIPGateway *string `json:"mgmtIpGateway" db:"mgmt_ip_gateway"` + Cachegroup *string `json:"cachegroup" db:"cachegroup"` + CachegroupID *int `json:"cachegroupId" db:"cachegroup_id"` + CDNID *int `json:"cdnId" db:"cdn_id"` + CDNName *string `json:"cdnName" db:"cdn_name"` + DeliveryServices *map[string][]string `json:"deliveryServices,omitempty"` + DomainName *string `json:"domainName" db:"domain_name"` + FQDN *string `json:"fqdn,omitempty"` + FqdnTime time.Time `json:"-"` + GUID *string `json:"guid" db:"guid"` + HostName *string `json:"hostName" db:"host_name"` + HTTPSPort *int `json:"httpsPort" db:"https_port"` + ID *int `json:"id" db:"id"` + ILOIPAddress *string `json:"iloIpAddress" db:"ilo_ip_address"` + ILOIPGateway *string `json:"iloIpGateway" db:"ilo_ip_gateway"` + ILOIPNetmask *string `json:"iloIpNetmask" db:"ilo_ip_netmask"` + ILOPassword *string `json:"iloPassword" db:"ilo_password"` + ILOUsername *string `json:"iloUsername" db:"ilo_username"` + LastUpdated *TimeNoMod `json:"lastUpdated" db:"last_updated"` + // Deprecated: In the future, management interfaces must be configured as + // interfaces within the Interfaces of the server, not separately on these + // properties. + MgmtIPAddress *string `json:"mgmtIpAddress" db:"mgmt_ip_address"` + // Deprecated: In the future, management interfaces must be configured as + // interfaces within the Interfaces of the server, not separately on these + // properties. + MgmtIPGateway *string `json:"mgmtIpGateway" db:"mgmt_ip_gateway"` + // Deprecated: In the future, management interfaces must be configured as + // interfaces within the Interfaces of the server, not separately on these + // properties. MgmtIPNetmask *string `json:"mgmtIpNetmask" db:"mgmt_ip_netmask"` OfflineReason *string `json:"offlineReason" db:"offline_reason"` PhysLocation *string `json:"physLocation" db:"phys_location"` @@ -1069,6 +1096,9 @@ type ServerV40 struct { type ServerV4 = ServerV40 // ServerV30 is the representation of a Server in version 3 of the Traffic Ops API. +// +// Deprecated: Traffic Ops API version 3 is deprecated, new code should use +// ServerV40 or newer structures. type ServerV30 struct { CommonServerProperties RouterHostName *string `json:"routerHostName" db:"router_host_name"` From cf7c7642c92461c86cfdb754cee14dc38d751120 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Wed, 2 Aug 2023 17:49:41 -0600 Subject: [PATCH 02/31] Remove duplicated functionality --- lib/go-tc/servers.go | 107 +++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 64 deletions(-) diff --git a/lib/go-tc/servers.go b/lib/go-tc/servers.go index ce286f4afe..16739097e8 100644 --- a/lib/go-tc/servers.go +++ b/lib/go-tc/servers.go @@ -829,79 +829,58 @@ func (s Server) ToNullable() ServerNullableV2 { } } -func coerceBool(b *bool) bool { - if b == nil { - return false - } - return *b -} - -func coerceInt(i *int) int { - if i == nil { - return 0 - } - return *i -} - -func coerceString(s *string) string { - if s == nil { - return "" - } - return *s -} - // ToNonNullable converts the ServerNullableV2 safely to a Server structure. // // Deprecated: Traffic Ops API version 2 is deprecated, new code should use // ServerV40 or newer structures. func (s ServerNullableV2) ToNonNullable() Server { ret := Server{ - Cachegroup: coerceString(s.Cachegroup), - CachegroupID: coerceInt(s.CachegroupID), - CDNID: coerceInt((s.CDNID)), - CDNName: coerceString(s.CDNName), - DomainName: coerceString(s.DomainName), + Cachegroup: util.CoalesceToDefault(s.Cachegroup), + CachegroupID: util.CoalesceToDefault(s.CachegroupID), + CDNID: util.CoalesceToDefault((s.CDNID)), + CDNName: util.CoalesceToDefault(s.CDNName), + DomainName: util.CoalesceToDefault(s.DomainName), FQDN: s.FQDN, FqdnTime: s.FqdnTime, - GUID: coerceString(s.GUID), - HostName: coerceString(s.HostName), - HTTPSPort: coerceInt(s.HTTPSPort), - ID: coerceInt(s.ID), - ILOIPAddress: coerceString(s.ILOIPAddress), - ILOIPGateway: coerceString(s.ILOIPGateway), - ILOIPNetmask: coerceString(s.ILOIPNetmask), - ILOPassword: coerceString(s.ILOPassword), - ILOUsername: coerceString(s.ILOUsername), - InterfaceMtu: coerceInt(s.InterfaceMtu), - InterfaceName: coerceString(s.InterfaceName), - IP6Address: coerceString(s.IP6Address), - IP6IsService: coerceBool(s.IP6IsService), - IP6Gateway: coerceString(s.IP6Gateway), - IPAddress: coerceString(s.IPAddress), - IPIsService: coerceBool(s.IPIsService), - IPGateway: coerceString(s.IPGateway), - IPNetmask: coerceString(s.IPNetmask), - MgmtIPAddress: coerceString(s.MgmtIPAddress), - MgmtIPGateway: coerceString(s.MgmtIPGateway), - MgmtIPNetmask: coerceString(s.MgmtIPNetmask), - OfflineReason: coerceString(s.OfflineReason), - PhysLocation: coerceString(s.PhysLocation), - PhysLocationID: coerceInt(s.PhysLocationID), - Profile: coerceString(s.Profile), - ProfileDesc: coerceString(s.ProfileDesc), - ProfileID: coerceInt(s.ProfileID), - Rack: coerceString(s.Rack), - RevalPending: coerceBool(s.RevalPending), - RouterHostName: coerceString(s.RouterHostName), - RouterPortName: coerceString(s.RouterPortName), - Status: coerceString(s.Status), - StatusID: coerceInt(s.StatusID), - TCPPort: coerceInt(s.TCPPort), + GUID: util.CoalesceToDefault(s.GUID), + HostName: util.CoalesceToDefault(s.HostName), + HTTPSPort: util.CoalesceToDefault(s.HTTPSPort), + ID: util.CoalesceToDefault(s.ID), + ILOIPAddress: util.CoalesceToDefault(s.ILOIPAddress), + ILOIPGateway: util.CoalesceToDefault(s.ILOIPGateway), + ILOIPNetmask: util.CoalesceToDefault(s.ILOIPNetmask), + ILOPassword: util.CoalesceToDefault(s.ILOPassword), + ILOUsername: util.CoalesceToDefault(s.ILOUsername), + InterfaceMtu: util.CoalesceToDefault(s.InterfaceMtu), + InterfaceName: util.CoalesceToDefault(s.InterfaceName), + IP6Address: util.CoalesceToDefault(s.IP6Address), + IP6IsService: util.CoalesceToDefault(s.IP6IsService), + IP6Gateway: util.CoalesceToDefault(s.IP6Gateway), + IPAddress: util.CoalesceToDefault(s.IPAddress), + IPIsService: util.CoalesceToDefault(s.IPIsService), + IPGateway: util.CoalesceToDefault(s.IPGateway), + IPNetmask: util.CoalesceToDefault(s.IPNetmask), + MgmtIPAddress: util.CoalesceToDefault(s.MgmtIPAddress), + MgmtIPGateway: util.CoalesceToDefault(s.MgmtIPGateway), + MgmtIPNetmask: util.CoalesceToDefault(s.MgmtIPNetmask), + OfflineReason: util.CoalesceToDefault(s.OfflineReason), + PhysLocation: util.CoalesceToDefault(s.PhysLocation), + PhysLocationID: util.CoalesceToDefault(s.PhysLocationID), + Profile: util.CoalesceToDefault(s.Profile), + ProfileDesc: util.CoalesceToDefault(s.ProfileDesc), + ProfileID: util.CoalesceToDefault(s.ProfileID), + Rack: util.CoalesceToDefault(s.Rack), + RevalPending: util.CoalesceToDefault(s.RevalPending), + RouterHostName: util.CoalesceToDefault(s.RouterHostName), + RouterPortName: util.CoalesceToDefault(s.RouterPortName), + Status: util.CoalesceToDefault(s.Status), + StatusID: util.CoalesceToDefault(s.StatusID), + TCPPort: util.CoalesceToDefault(s.TCPPort), Type: s.Type, - TypeID: coerceInt(s.TypeID), - UpdPending: coerceBool(s.UpdPending), - XMPPID: coerceString(s.XMPPID), - XMPPPasswd: coerceString(s.XMPPPasswd), + TypeID: util.CoalesceToDefault(s.TypeID), + UpdPending: util.CoalesceToDefault(s.UpdPending), + XMPPID: util.CoalesceToDefault(s.XMPPID), + XMPPPasswd: util.CoalesceToDefault(s.XMPPPasswd), } if s.DeliveryServices == nil { From 83f3cb6ccdb21291707e51e25f030828d9513d73 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Wed, 2 Aug 2023 22:42:41 -0600 Subject: [PATCH 03/31] add a utility function to deep copy arbitrary maps --- lib/go-util/collections.go | 32 ++++++++++++++++++++++++ lib/go-util/collections_test.go | 43 +++++++++++++++++++++++++++++++++ lib/go-util/ptr.go | 2 +- 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 lib/go-util/collections.go create mode 100644 lib/go-util/collections_test.go diff --git a/lib/go-util/collections.go b/lib/go-util/collections.go new file mode 100644 index 0000000000..a93757c4c4 --- /dev/null +++ b/lib/go-util/collections.go @@ -0,0 +1,32 @@ +package util + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// CopyMap makes a "deep-ish" copy of the map passed to it. This will only +// deeply copy the map itself; this means that if the map values are references +// or structures containing references, those are only being shallowly copied! +func CopyMap[T comparable, U any](original map[T]U) map[T]U { + newMap := make(map[T]U, len(original)) + for k, v := range original { + newMap[k] = v + } + + return newMap +} diff --git a/lib/go-util/collections_test.go b/lib/go-util/collections_test.go new file mode 100644 index 0000000000..7dee6bf9ab --- /dev/null +++ b/lib/go-util/collections_test.go @@ -0,0 +1,43 @@ +package util + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import ( + "fmt" +) + +func ExampleCopyMap() { + original := map[string]string{ + "foo": "bar", + } + + copied := CopyMap(original) + fmt.Println(copied["foo"]) + + original["foo"] = "foo" + fmt.Println(copied["foo"]) + + original["test"] = "quest" + fmt.Println(len(copied)) + + // Output: bar + // bar + // 1 +} diff --git a/lib/go-util/ptr.go b/lib/go-util/ptr.go index 2517a77a91..49774fe71b 100644 --- a/lib/go-util/ptr.go +++ b/lib/go-util/ptr.go @@ -127,7 +127,7 @@ func Coalesce[T any](p *T, def T) T { // nil. This is equivalent to: // // var x T -// result := CoalesceToDefault(p, x) +// result := Coalesce(p, x) // // ... but can be done on one line without knowing the type of `p`. func CoalesceToDefault[T any](p *T) T { From ecab2db03d7cfb38d39a60e722b99a61e1cd3246 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Wed, 2 Aug 2023 22:44:33 -0600 Subject: [PATCH 04/31] add deep copy methods for server interface data --- lib/go-tc/servers.go | 34 ++++++++++++++++++++++++++++ lib/go-tc/servers_test.go | 47 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/lib/go-tc/servers.go b/lib/go-tc/servers.go index 16739097e8..0aa15d569c 100644 --- a/lib/go-tc/servers.go +++ b/lib/go-tc/servers.go @@ -141,6 +141,15 @@ type ServerIPAddress struct { ServiceAddress bool `json:"serviceAddress" db:"service_address"` } +// Copy creates a deep copy of the IP address. +func (ip ServerIPAddress) Copy() ServerIPAddress { + return ServerIPAddress{ + Address: ip.Address, + Gateway: util.CopyIfNotNil(ip.Gateway), + ServiceAddress: ip.ServiceAddress, + } +} + // ServerInterfaceInfo is the data associated with a server's interface. type ServerInterfaceInfo struct { IPAddresses []ServerIPAddress `json:"ipAddresses" db:"ip_addresses"` @@ -150,6 +159,22 @@ type ServerInterfaceInfo struct { Name string `json:"name" db:"name"` } +// Copy creates a deep copy of the Server Interface. +func (inf ServerInterfaceInfo) Copy() ServerInterfaceInfo { + newInf := ServerInterfaceInfo{ + IPAddresses: make([]ServerIPAddress, len(inf.IPAddresses)), + MaxBandwidth: util.CopyIfNotNil(inf.MaxBandwidth), + Monitor: inf.Monitor, + MTU: util.CopyIfNotNil(inf.MTU), + Name: inf.Name, + } + for i, ip := range inf.IPAddresses { + newInf.IPAddresses[i] = ip.Copy() + } + + return newInf +} + // ServerInterfaceInfoV40 is the data associated with a V40 server's interface. type ServerInterfaceInfoV40 struct { ServerInterfaceInfo @@ -157,6 +182,15 @@ type ServerInterfaceInfoV40 struct { RouterPortName string `json:"routerPortName" db:"router_port_name"` } +// Copy creates a deep copy of the Server Interface. +func (inf ServerInterfaceInfoV40) Copy() ServerInterfaceInfoV40 { + return ServerInterfaceInfoV40{ + ServerInterfaceInfo: inf.ServerInterfaceInfo.Copy(), + RouterHostName: inf.RouterHostName, + RouterPortName: inf.RouterPortName, + } +} + // GetDefaultAddressOrCIDR returns the IPv4 and IPv6 service addresses of the interface. func (i *ServerInterfaceInfo) GetDefaultAddress() (string, string) { ipv4, ipv6 := i.GetDefaultAddressOrCIDR() diff --git a/lib/go-tc/servers_test.go b/lib/go-tc/servers_test.go index 9641813ba3..805afbe1ee 100644 --- a/lib/go-tc/servers_test.go +++ b/lib/go-tc/servers_test.go @@ -83,6 +83,53 @@ func ExampleLegacyInterfaceDetails_String() { // Output: LegacyInterfaceDetails(InterfaceMtu=9000, InterfaceName='test', IP6Address='2001:DB8::/64', IP6Gateway=nil, IPAddress='192.0.2.0', IPGateway=nil, IPNetmask=nil) } +func ExampleServerIPAddress_Copy() { + ip := ServerIPAddress{ + Address: "test", + Gateway: new(string), + ServiceAddress: false, + } + + *ip.Gateway = "not a gateway, but who cares?" + ip2 := ip.Copy() + fmt.Println(*ip.Gateway == *ip2.Gateway) + + *ip.Gateway = "something different" + fmt.Println(*ip.Gateway == *ip2.Gateway) + + // Output: true + // false +} + +func ExampleServerInterfaceInfoV40_Copy() { + inf := ServerInterfaceInfoV40{ + ServerInterfaceInfo: ServerInterfaceInfo{ + IPAddresses: []ServerIPAddress{ + { + Address: "test", + Gateway: new(string), + ServiceAddress: false, + }, + }, + MaxBandwidth: new(uint64), + Monitor: false, + MTU: new(uint64), + Name: "eth0", + }, + RouterHostName: "router host", + RouterPortName: "router port", + } + + *inf.IPAddresses[0].Gateway = "not a gateway, but who cares?" + inf2 := inf.Copy() + + fmt.Println(*inf.IPAddresses[0].Gateway == *inf2.IPAddresses[0].Gateway) + *inf.IPAddresses[0].Gateway = "something different" + fmt.Println(*inf.IPAddresses[0].Gateway == *inf2.IPAddresses[0].Gateway) + + // Output: true + // false +} type interfaceTest struct { ExpectedIPv4 string ExpectedIPv4Gateway string From b967e080b446fedc053c660996426b940d7f513e Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Wed, 2 Aug 2023 22:50:20 -0600 Subject: [PATCH 05/31] Add v5 server types --- lib/go-tc/servers.go | 255 +++++++++++++++++++++++++++++++++++--- lib/go-tc/servers_test.go | 84 +++++++++++++ 2 files changed, 320 insertions(+), 19 deletions(-) diff --git a/lib/go-tc/servers.go b/lib/go-tc/servers.go index 0aa15d569c..ea0fbfccf0 100644 --- a/lib/go-tc/servers.go +++ b/lib/go-tc/servers.go @@ -760,17 +760,23 @@ type CommonServerProperties struct { ProfileDesc *string `json:"profileDesc" db:"profile_desc"` // Deprecated: In API versions 4 and later, Servers identify their Profiles // by Name, not ID. - ProfileID *int `json:"profileId" db:"profile_id"` - Rack *string `json:"rack" db:"rack"` + ProfileID *int `json:"profileId" db:"profile_id"` + Rack *string `json:"rack" db:"rack"` + // Deprecated: In APIv5 and later, this extraneous field is not calculated + // by Traffic Ops; the information is available by comparing RevalUpdateTime + // to RevalApplyTime. RevalPending *bool `json:"revalPending" db:"reval_pending"` Status *string `json:"status" db:"status"` StatusID *int `json:"statusId" db:"status_id"` TCPPort *int `json:"tcpPort" db:"tcp_port"` Type string `json:"type" db:"server_type"` TypeID *int `json:"typeId" db:"server_type_id"` - UpdPending *bool `json:"updPending" db:"upd_pending"` - XMPPID *string `json:"xmppId" db:"xmpp_id"` - XMPPPasswd *string `json:"xmppPasswd" db:"xmpp_passwd"` + // Deprecated: In APIv5 and later, this extraneous field is not calculated + // by Traffic Ops; the information is available by comparing + // ConfigUpdateTime to ConfigApplyTime. + UpdPending *bool `json:"updPending" db:"upd_pending"` + XMPPID *string `json:"xmppId" db:"xmpp_id"` + XMPPPasswd *string `json:"xmppPasswd" db:"xmpp_passwd"` } // ServerNullableV11 is a server as it appeared in API version 1.1. @@ -1081,18 +1087,24 @@ type ServerV40 struct { // Deprecated: In the future, management interfaces must be configured as // interfaces within the Interfaces of the server, not separately on these // properties. - MgmtIPNetmask *string `json:"mgmtIpNetmask" db:"mgmt_ip_netmask"` - OfflineReason *string `json:"offlineReason" db:"offline_reason"` - PhysLocation *string `json:"physLocation" db:"phys_location"` - PhysLocationID *int `json:"physLocationId" db:"phys_location_id"` - ProfileNames []string `json:"profileNames" db:"profile_name"` - Rack *string `json:"rack" db:"rack"` - RevalPending *bool `json:"revalPending" db:"reval_pending"` - Status *string `json:"status" db:"status"` - StatusID *int `json:"statusId" db:"status_id"` - TCPPort *int `json:"tcpPort" db:"tcp_port"` - Type string `json:"type" db:"server_type"` - TypeID *int `json:"typeId" db:"server_type_id"` + MgmtIPNetmask *string `json:"mgmtIpNetmask" db:"mgmt_ip_netmask"` + OfflineReason *string `json:"offlineReason" db:"offline_reason"` + PhysLocation *string `json:"physLocation" db:"phys_location"` + PhysLocationID *int `json:"physLocationId" db:"phys_location_id"` + ProfileNames []string `json:"profileNames" db:"profile_name"` + Rack *string `json:"rack" db:"rack"` + // Deprecated: In APIv5 and later, this extraneous field is not calculated + // by Traffic Ops; the information is available by comparing RevalUpdateTime + // to RevalApplyTime. + RevalPending *bool `json:"revalPending" db:"reval_pending"` + Status *string `json:"status" db:"status"` + StatusID *int `json:"statusId" db:"status_id"` + TCPPort *int `json:"tcpPort" db:"tcp_port"` + Type string `json:"type" db:"server_type"` + TypeID *int `json:"typeId" db:"server_type_id"` + // Deprecated: In APIv5 and later, this extraneous field is not calculated + // by Traffic Ops; the information is available by comparing + // ConfigUpdateTime to ConfigApplyTime. UpdPending *bool `json:"updPending" db:"upd_pending"` XMPPID *string `json:"xmppId" db:"xmpp_id"` XMPPPasswd *string `json:"xmppPasswd" db:"xmpp_passwd"` @@ -1108,6 +1120,63 @@ type ServerV40 struct { // version 4 of the Traffic Ops API. type ServerV4 = ServerV40 +// Upgrade upgrades to an APIv5 representation of a Server. +func (s ServerV4) Upgrade() ServerV50 { + upgraded := ServerV50{ + CacheGroup: util.CoalesceToDefault(s.Cachegroup), + CacheGroupID: util.CoalesceToDefault(s.CachegroupID), + CDNID: util.CoalesceToDefault(s.CDNID), + CDN: util.CoalesceToDefault(s.CDNName), + DomainName: util.CoalesceToDefault(s.DomainName), + GUID: util.CopyIfNotNil(s.GUID), + HostName: util.CoalesceToDefault(s.HostName), + HTTPSPort: util.CopyIfNotNil(s.HTTPSPort), + ID: util.CoalesceToDefault(s.ID), + ILOIPAddress: util.CopyIfNotNil(s.ILOIPAddress), + ILOIPGateway: util.CopyIfNotNil(s.ILOIPGateway), + ILOIPNetmask: util.CopyIfNotNil(s.ILOIPNetmask), + ILOPassword: util.CopyIfNotNil(s.ILOPassword), + ILOUsername: util.CopyIfNotNil(s.ILOUsername), + LastUpdated: util.CoalesceToDefault(s.LastUpdated).Time, + MgmtIPAddress: util.CopyIfNotNil(s.MgmtIPAddress), + MgmtIPGateway: util.CopyIfNotNil(s.MgmtIPGateway), + MgmtIPNetmask: util.CopyIfNotNil(s.MgmtIPNetmask), + OfflineReason: util.CopyIfNotNil(s.OfflineReason), + PhysicalLocation: util.CoalesceToDefault(s.PhysLocation), + PhysicalLocationID: util.CoalesceToDefault(s.PhysLocationID), + Profiles: make([]string, len(s.ProfileNames)), + Rack: util.CopyIfNotNil(s.Rack), + Status: util.CoalesceToDefault(s.Status), + StatusID: util.CoalesceToDefault(s.StatusID), + TCPPort: util.CopyIfNotNil(s.TCPPort), + Topologies: []string{}, + Type: s.Type, + TypeID: util.CoalesceToDefault(s.TypeID), + XMPPID: util.CopyIfNotNil(s.XMPPID), + XMPPPasswd: util.CopyIfNotNil(s.XMPPPasswd), + Interfaces: make([]ServerInterfaceInfoV40, len(s.Interfaces)), + StatusLastUpdated: util.CopyIfNotNil(s.StatusLastUpdated), + ConfigUpdateTime: util.CopyIfNotNil(s.ConfigUpdateTime), + ConfigApplyTime: util.CopyIfNotNil(s.ConfigApplyTime), + RevalUpdateTime: util.CopyIfNotNil(s.RevalUpdateTime), + RevalApplyTime: util.CopyIfNotNil(s.RevalApplyTime), + } + + copy(upgraded.Profiles, s.ProfileNames) + + if s.DeliveryServices != nil { + upgraded.DeliveryServices = util.CopyMap(*s.DeliveryServices) + } else { + upgraded.DeliveryServices = map[string][]string{} + } + + for i, inf := range s.Interfaces { + upgraded.Interfaces[i] = inf.Copy() + } + + return upgraded +} + // ServerV30 is the representation of a Server in version 3 of the Traffic Ops API. // // Deprecated: Traffic Ops API version 3 is deprecated, new code should use @@ -1244,6 +1313,131 @@ func (s *ServerV40) ToServerV2FromV4(csp CommonServerProperties) (ServerNullable return legacyServer, nil } +// ServerV50 is the representation of a Server in version 5.0 of the Traffic Ops +// API. +type ServerV50 struct { + CacheGroup string `json:"cacheGroup" db:"cachegroup"` + CacheGroupID int `json:"cacheGroupID" db:"cachegroup_id"` + CDNID int `json:"cdnID" db:"cdn_id"` + CDN string `json:"cdn" db:"cdn_name"` + ConfigApplyTime *time.Time `json:"configApplyTime" db:"config_apply_time"` + ConfigUpdateTime *time.Time `json:"configUpdateTime" db:"config_update_time"` + // DeliveryServices doesn't include Delivery Services assigned via Topology + // usage (use the Topologies property for that). Read-only. + DeliveryServices map[string][]string `json:"deliveryServices"` + DomainName string `json:"domainName" db:"domain_name"` + // Deprecated: This property has unknown purpose and should not be used so + // that we can get rid of it. + GUID *string `json:"guid" db:"guid"` + HostName string `json:"hostName" db:"host_name"` + HTTPSPort *int `json:"httpsPort" db:"https_port"` + ID int `json:"id" db:"id"` + ILOIPAddress *string `json:"iloIpAddress" db:"ilo_ip_address"` + ILOIPGateway *string `json:"iloIpGateway" db:"ilo_ip_gateway"` + ILOIPNetmask *string `json:"iloIpNetmask" db:"ilo_ip_netmask"` + ILOPassword *string `json:"iloPassword" db:"ilo_password"` + ILOUsername *string `json:"iloUsername" db:"ilo_username"` + Interfaces []ServerInterfaceInfoV40 `json:"interfaces" db:"interfaces"` + LastUpdated time.Time `json:"lastUpdated" db:"last_updated"` + // Deprecated: In the future, management interfaces must be configured as + // interfaces within the Interfaces of the server, not separately on these + // properties. + MgmtIPAddress *string `json:"mgmtIpAddress" db:"mgmt_ip_address"` + // Deprecated: In the future, management interfaces must be configured as + // interfaces within the Interfaces of the server, not separately on these + // properties. + MgmtIPGateway *string `json:"mgmtIpGateway" db:"mgmt_ip_gateway"` + // Deprecated: In the future, management interfaces must be configured as + // interfaces within the Interfaces of the server, not separately on these + // properties. + MgmtIPNetmask *string `json:"mgmtIpNetmask" db:"mgmt_ip_netmask"` + OfflineReason *string `json:"offlineReason" db:"offline_reason"` + PhysicalLocation string `json:"physicalLocation" db:"phys_location"` + PhysicalLocationID int `json:"physicalLocationID" db:"phys_location_id"` + Profiles []string `json:"profiles" db:"profile_name"` + // Deprecated: This property has unknown purpose and should not be used so + // that we can get rid of it. + Rack *string `json:"rack" db:"rack"` + RevalApplyTime *time.Time `json:"revalApplyTime" db:"revalidate_apply_time"` + RevalUpdateTime *time.Time `json:"revalUpdateTime" db:"revalidate_update_time"` + Status string `json:"status" db:"status"` + StatusID int `json:"statusID" db:"status_id"` + StatusLastUpdated *time.Time `json:"statusLastUpdated" db:"status_last_updated"` + TCPPort *int `json:"tcpPort" db:"tcp_port"` + // read-only + Topologies []string `json:"topologies" db:"topologies"` + Type string `json:"type" db:"server_type"` + TypeID int `json:"typeID" db:"server_type_id"` + XMPPID *string `json:"xmppId" db:"xmpp_id"` + // Deprecated: This property has unknown purpose and should not be used so + // that we can get rid of it. + XMPPPasswd *string `json:"xmppPasswd" db:"xmpp_passwd"` +} + +// Downgrade downgrades to a V4 representation of a Server. +func (s ServerV50) Downgrade() ServerV4 { + downgraded := ServerV40{ + Cachegroup: util.Ptr(s.CacheGroup), + CachegroupID: util.Ptr(s.CacheGroupID), + CDNID: util.Ptr(s.CDNID), + CDNName: util.Ptr(s.CDN), + DeliveryServices: nil, + DomainName: util.Ptr(s.DomainName), + FQDN: util.Ptr(s.HostName + "." + s.DomainName), + FqdnTime: time.Time{}, + GUID: util.CopyIfNotNil(s.GUID), + HostName: util.Ptr(s.HostName), + HTTPSPort: util.CopyIfNotNil(s.HTTPSPort), + ID: util.Ptr(s.ID), + ILOIPAddress: util.CopyIfNotNil(s.ILOIPAddress), + ILOIPGateway: util.CopyIfNotNil(s.ILOIPGateway), + ILOIPNetmask: util.CopyIfNotNil(s.ILOIPNetmask), + ILOPassword: util.CopyIfNotNil(s.ILOPassword), + ILOUsername: util.CopyIfNotNil(s.ILOUsername), + LastUpdated: &TimeNoMod{Time: s.LastUpdated}, + MgmtIPAddress: util.CopyIfNotNil(s.MgmtIPAddress), + MgmtIPGateway: util.CopyIfNotNil(s.MgmtIPGateway), + MgmtIPNetmask: util.CopyIfNotNil(s.MgmtIPNetmask), + OfflineReason: util.CopyIfNotNil(s.OfflineReason), + PhysLocation: util.Ptr(s.PhysicalLocation), + PhysLocationID: util.Ptr(s.PhysicalLocationID), + ProfileNames: make([]string, len(s.Profiles)), + Rack: util.CopyIfNotNil(s.Rack), + RevalPending: util.Ptr(s.RevalApplyTime != nil && s.RevalUpdateTime != nil && !s.RevalApplyTime.Before(*s.RevalUpdateTime)), + Status: util.Ptr(s.Status), + StatusID: util.Ptr(s.StatusID), + TCPPort: util.CopyIfNotNil(s.TCPPort), + Type: s.Type, + TypeID: util.Ptr(s.TypeID), + UpdPending: util.Ptr(s.ConfigApplyTime != nil && s.ConfigUpdateTime != nil && !s.ConfigApplyTime.Before(*s.ConfigUpdateTime)), + XMPPID: util.CopyIfNotNil(s.XMPPID), + XMPPPasswd: util.CopyIfNotNil(s.XMPPPasswd), + Interfaces: make([]ServerInterfaceInfoV40, len(s.Interfaces)), + StatusLastUpdated: util.CopyIfNotNil(s.StatusLastUpdated), + ConfigUpdateTime: util.CopyIfNotNil(s.ConfigUpdateTime), + ConfigApplyTime: util.CopyIfNotNil(s.ConfigApplyTime), + RevalUpdateTime: util.CopyIfNotNil(s.RevalUpdateTime), + RevalApplyTime: util.CopyIfNotNil(s.RevalApplyTime), + } + + copy(downgraded.ProfileNames, s.Profiles) + for i, inf := range s.Interfaces { + downgraded.Interfaces[i] = inf.Copy() + } + + // For DeliveryServices, specifically, we have to preserve actual nil values + // because of how omitempty works. + if s.DeliveryServices != nil { + downgraded.DeliveryServices = util.Ptr(util.CopyMap(s.DeliveryServices)) + } + + return downgraded +} + +// ServerV5 is the representation of a Server in the latest minor version of +// version 5 of the Traffic Ops API. +type ServerV5 = ServerV50 + // ServerUpdateStatusV4 is the type of each entry in the `response` property of // the response from Traffic Ops to GET requests made to its // /servers/{{host name}}/update_status in the latest minor API @@ -1254,8 +1448,11 @@ type ServerUpdateStatusV4 ServerUpdateStatusV40 // the response from Traffic Ops to GET requests made to its // /servers/{{host name}}/update_status in API v4.0 endpoint. type ServerUpdateStatusV40 struct { - HostName string `json:"host_name"` - UpdatePending bool `json:"upd_pending"` + HostName string `json:"host_name"` + UpdatePending bool `json:"upd_pending"` + // Deprecated: In APIv5 and later, this extraneous field is not calculated + // by Traffic Ops; the information is available by comparing RevalUpdateTime + // to RevalApplyTime. RevalPending bool `json:"reval_pending"` UseRevalPending bool `json:"use_reval_pending"` HostId int `json:"host_id"` @@ -1418,3 +1615,23 @@ type ServerQueueUpdate struct { ServerID util.JSONIntStr `json:"serverId"` Action string `json:"action"` } + +// ServersV5Response is the format of a response to a GET request to /servers in +// APIv5. +type ServersV5Response struct { + Response []ServerV5 `json:"response"` + Summary struct { + Count uint64 `json:"count"` + } `json:"summary"` + Alerts +} + +// ServerV5Response is the format of a response to single-server operations +// using /servers in APIv5. +type ServerV5Response struct { + Response ServerV5 `json:"response"` + Summary struct { + Count uint64 `json:"count"` + } `json:"summary"` + Alerts +} diff --git a/lib/go-tc/servers_test.go b/lib/go-tc/servers_test.go index 805afbe1ee..c6ba09cfb7 100644 --- a/lib/go-tc/servers_test.go +++ b/lib/go-tc/servers_test.go @@ -21,6 +21,7 @@ package tc import ( "fmt" + "reflect" "strings" "testing" "time" @@ -130,6 +131,89 @@ func ExampleServerInterfaceInfoV40_Copy() { // Output: true // false } + +func TestServerV5DowngradeUpgrade(t *testing.T) { + serverV5 := ServerV50{ + CacheGroup: "Cache Group", + CacheGroupID: 1, + CDNID: 2, + CDN: "CDN", + DeliveryServices: map[string][]string{ + "test": {"foo", "bar"}, + }, + DomainName: "domain", + GUID: nil, + HostName: "host", + HTTPSPort: nil, + ID: 3, + ILOIPAddress: nil, + ILOIPGateway: nil, + ILOIPNetmask: nil, + ILOPassword: nil, + ILOUsername: nil, + LastUpdated: time.Time{}.Add(time.Hour), + MgmtIPAddress: nil, + MgmtIPGateway: nil, + MgmtIPNetmask: nil, + OfflineReason: nil, + PhysicalLocation: "physical location", + PhysicalLocationID: 4, + Profiles: []string{"test", "quest"}, + Rack: nil, + Status: "Status", + StatusID: 5, + TCPPort: nil, + Topologies: []string{}, + Type: "type", + TypeID: 6, + XMPPID: nil, + XMPPPasswd: nil, + Interfaces: []ServerInterfaceInfoV40{ + { + ServerInterfaceInfo: ServerInterfaceInfo{ + IPAddresses: []ServerIPAddress{ + { + Address: "192.0.0.1/12", + Gateway: nil, + ServiceAddress: true, + }, + }, + MaxBandwidth: nil, + Monitor: false, + MTU: nil, + Name: "eth0", + }, + RouterHostName: "router host", + RouterPortName: "router port", + }, + }, + StatusLastUpdated: nil, + ConfigUpdateTime: nil, + ConfigApplyTime: nil, + RevalUpdateTime: nil, + RevalApplyTime: nil, + } + + serverV4 := serverV5.Downgrade() + if fqdn := serverV5.HostName + "." + serverV5.DomainName; serverV4.FQDN == nil || *serverV4.FQDN != fqdn { + t.Errorf("incorrectly calculated FQDN; want: %s, got: %v", fqdn, serverV4.FQDN) + } + + if !reflect.DeepEqual(serverV4.Upgrade(), serverV5) { + t.Error("server not equal after downgrading then upgrading") + } +} + +func TestServerV4_Upgrade(t *testing.T) { + s := ServerV40{ + DeliveryServices: nil, + } + + if s.Upgrade().DeliveryServices == nil { + t.Error("upgrading a nil DS slice shouldn't be nil, but it was") + } +} + type interfaceTest struct { ExpectedIPv4 string ExpectedIPv4Gateway string From b20a53eed2daa3565f22b5369c118940c1847de6 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Wed, 2 Aug 2023 23:07:12 -0600 Subject: [PATCH 06/31] Remove a seemingly unused property --- lib/go-tc/servers.go | 27 +++++++-------------------- lib/go-tc/servers_test.go | 21 ++++----------------- 2 files changed, 11 insertions(+), 37 deletions(-) diff --git a/lib/go-tc/servers.go b/lib/go-tc/servers.go index ea0fbfccf0..509c97a61b 100644 --- a/lib/go-tc/servers.go +++ b/lib/go-tc/servers.go @@ -1058,10 +1058,12 @@ func UpdateServerPropertiesV40(profileNames []string, properties CommonServerPro // 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"` - CachegroupID *int `json:"cachegroupId" db:"cachegroup_id"` - CDNID *int `json:"cdnId" db:"cdn_id"` - CDNName *string `json:"cdnName" db:"cdn_name"` + Cachegroup *string `json:"cachegroup" db:"cachegroup"` + CachegroupID *int `json:"cachegroupId" db:"cachegroup_id"` + CDNID *int `json:"cdnId" db:"cdn_id"` + CDNName *string `json:"cdnName" db:"cdn_name"` + // Deprecated: this has no known purpose, doesn't appear in any known API + // responses, and it doesn't exist in the V5 version of this structure. DeliveryServices *map[string][]string `json:"deliveryServices,omitempty"` DomainName *string `json:"domainName" db:"domain_name"` FQDN *string `json:"fqdn,omitempty"` @@ -1164,12 +1166,6 @@ func (s ServerV4) Upgrade() ServerV50 { copy(upgraded.Profiles, s.ProfileNames) - if s.DeliveryServices != nil { - upgraded.DeliveryServices = util.CopyMap(*s.DeliveryServices) - } else { - upgraded.DeliveryServices = map[string][]string{} - } - for i, inf := range s.Interfaces { upgraded.Interfaces[i] = inf.Copy() } @@ -1322,10 +1318,7 @@ type ServerV50 struct { CDN string `json:"cdn" db:"cdn_name"` ConfigApplyTime *time.Time `json:"configApplyTime" db:"config_apply_time"` ConfigUpdateTime *time.Time `json:"configUpdateTime" db:"config_update_time"` - // DeliveryServices doesn't include Delivery Services assigned via Topology - // usage (use the Topologies property for that). Read-only. - DeliveryServices map[string][]string `json:"deliveryServices"` - DomainName string `json:"domainName" db:"domain_name"` + DomainName string `json:"domainName" db:"domain_name"` // Deprecated: This property has unknown purpose and should not be used so // that we can get rid of it. GUID *string `json:"guid" db:"guid"` @@ -1425,12 +1418,6 @@ func (s ServerV50) Downgrade() ServerV4 { downgraded.Interfaces[i] = inf.Copy() } - // For DeliveryServices, specifically, we have to preserve actual nil values - // because of how omitempty works. - if s.DeliveryServices != nil { - downgraded.DeliveryServices = util.Ptr(util.CopyMap(s.DeliveryServices)) - } - return downgraded } diff --git a/lib/go-tc/servers_test.go b/lib/go-tc/servers_test.go index c6ba09cfb7..7e0c3ee220 100644 --- a/lib/go-tc/servers_test.go +++ b/lib/go-tc/servers_test.go @@ -134,13 +134,10 @@ func ExampleServerInterfaceInfoV40_Copy() { func TestServerV5DowngradeUpgrade(t *testing.T) { serverV5 := ServerV50{ - CacheGroup: "Cache Group", - CacheGroupID: 1, - CDNID: 2, - CDN: "CDN", - DeliveryServices: map[string][]string{ - "test": {"foo", "bar"}, - }, + CacheGroup: "Cache Group", + CacheGroupID: 1, + CDNID: 2, + CDN: "CDN", DomainName: "domain", GUID: nil, HostName: "host", @@ -204,16 +201,6 @@ func TestServerV5DowngradeUpgrade(t *testing.T) { } } -func TestServerV4_Upgrade(t *testing.T) { - s := ServerV40{ - DeliveryServices: nil, - } - - if s.Upgrade().DeliveryServices == nil { - t.Error("upgrading a nil DS slice shouldn't be nil, but it was") - } -} - type interfaceTest struct { ExpectedIPv4 string ExpectedIPv4Gateway string From a8cab2f4ad3bbeb4c71cc9c5cbc0c429a68d55b3 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Mon, 10 Jul 2023 13:24:56 -0600 Subject: [PATCH 07/31] Add a wrapper for eliminating boilerplate when constructing apiinfos (cherry picked from commit eae7f40652d11609a4d1f55b62d88d3cd8bc0732) --- .../traffic_ops_golang/api/shared_handlers.go | 32 ++++++++++ .../api/shared_handlers_test.go | 60 +++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/traffic_ops/traffic_ops_golang/api/shared_handlers.go b/traffic_ops/traffic_ops_golang/api/shared_handlers.go index 594b34a62c..69ecf964ab 100644 --- a/traffic_ops/traffic_ops_golang/api/shared_handlers.go +++ b/traffic_ops/traffic_ops_golang/api/shared_handlers.go @@ -675,3 +675,35 @@ func parseMultipleCreates(data []byte, desiredType reflect.Type, inf *APIInfo) ( return creatorSlice, nil } + +// A Handler is an API endpoint handlers. The take in APIInfo helper objects and +// return - in order - an HTTP response status code, a user-facing error (if one +// occurred), and a system-only error not safe for exposure to clients (if one +// occurred). +type Handler = func(*APIInfo) (int, error, error) + +// Wrap wraps an API endpoint handler in the more generic HTTP request handler +// type from the http package. This constructs and provides the APIInfo for the +// underlying Handler. If the handler requires any request path and/or query +// string parameters, those should be declared in requiredParams. Likewise, if +// any of those parameters are required to be integral, they should be named in +// intParams. +// Note that this will still require the normal routing middleware for +// authentication and context setup. +// Also note that handlers utilizing this need not defer closing of the provided +// APIInfo, as this will handle that for them. +func Wrap(h Handler, requiredParams, intParams []string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + inf, userErr, sysErr, errCode := NewInfo(r, requiredParams, intParams) + if userErr != nil || sysErr != nil { + HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) + return + } + defer inf.Close() + + errCode, userErr, sysErr = h(inf) + if userErr != nil || sysErr != nil { + HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) + } + } +} diff --git a/traffic_ops/traffic_ops_golang/api/shared_handlers_test.go b/traffic_ops/traffic_ops_golang/api/shared_handlers_test.go index 9f9237acc8..b6e375bc10 100644 --- a/traffic_ops/traffic_ops_golang/api/shared_handlers_test.go +++ b/traffic_ops/traffic_ops_golang/api/shared_handlers_test.go @@ -347,3 +347,63 @@ func TestDeleteHandler(t *testing.T) { t.Error("Expected body", body, "got", w.Body.String()) } } + +// The constructed handler will return an error if fail is true, or nothing +// special otherwise. +func testingHandler(fail bool) Handler { + return func(inf *APIInfo) (int, error, error) { + if fail { + return http.StatusBadRequest, errors.New("testing user error"), errors.New("testing system error") + } + return http.StatusOK, nil, nil + } +} + +func wrapContext(r *http.Request, key any, value any) *http.Request { + return r.WithContext(context.WithValue(r.Context(), key, value)) +} + +func TestWrap(t *testing.T) { + h := Wrap(testingHandler(false), nil, nil) + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "/", nil) + + h(w, r) + if w.Code != http.StatusInternalServerError { + t.Errorf("Expected a system-internal error when an API info object can't be constructed, got response status code: %d (expected: %d)", w.Code, http.StatusInternalServerError) + } + + db, mock, err := sqlmock.New() + if err != nil { + t.Fatalf("failed to open a stub database connection: %v", err) + } + + w = httptest.NewRecorder() + r = httptest.NewRequest(http.MethodGet, "/", nil) + r = wrapContext(r, ConfigContextKey, &config.Config{ConfigTrafficOpsGolang: config.ConfigTrafficOpsGolang{DBQueryTimeoutSeconds: 1000}}) + r = wrapContext(r, DBContextKey, &sqlx.DB{DB: db}) + r = wrapContext(r, TrafficVaultContextKey, &disabled.Disabled{}) + r = wrapContext(r, ReqIDContextKey, uint64(0)) + r = wrapContext(r, auth.CurrentUserKey, auth.CurrentUser{}) + r = wrapContext(r, PathParamsKey, make(map[string]string)) + + mock.ExpectBegin() + mock.ExpectCommit() + h(w, r) + if w.Code != http.StatusOK { + t.Errorf("wrong status code from a normal run of the trivial handler; want: %d, got: %d", http.StatusOK, w.Code) + } + + h = Wrap(testingHandler(true), nil, nil) + w = httptest.NewRecorder() + mock.ExpectBegin() + mock.ExpectRollback() + h(w, r) + if w.Code != http.StatusBadRequest { + t.Errorf("wrong status code when the trivial handler is asked to fail; want: %d, got: %d", http.StatusBadRequest, w.Code) + } + + if err = mock.ExpectationsWereMet(); err != nil { + t.Errorf("not all expectations were met: %v", err) + } +} From ff2ab7b4cfd8fb2f9963259da259a3352ec307dc Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Mon, 10 Jul 2023 15:37:40 -0600 Subject: [PATCH 08/31] Add convenience methods on APIInfos for wrapped handlers (cherry picked from commit 887033af01cd8f9a64d29bbcb2d7f816ab8190e9) --- traffic_ops/traffic_ops_golang/api/api.go | 76 +++++++ .../traffic_ops_golang/api/api_test.go | 206 ++++++++++++++++++ .../traffic_ops_golang/api/shared_handlers.go | 1 + 3 files changed, 283 insertions(+) diff --git a/traffic_ops/traffic_ops_golang/api/api.go b/traffic_ops/traffic_ops_golang/api/api.go index 119377404c..3383e21080 100644 --- a/traffic_ops/traffic_ops_golang/api/api.go +++ b/traffic_ops/traffic_ops_golang/api/api.go @@ -515,6 +515,7 @@ type APIInfo struct { Vault trafficvault.TrafficVault Config *config.Config request *http.Request + w http.ResponseWriter } // NewInfo get and returns the context info needed by handlers. It also returns any user error, any system error, and the status code which should be returned to the client if an error occurred. @@ -690,6 +691,81 @@ func (inf *APIInfo) Close() { } } +// WriteOKResponse writes a 200 OK response with the given object as the +// 'response' property of the response body. +// +// This CANNOT be used by any APIInfo that wasn't constructed for the caller by +// Wrap - ing a Handler (yet). +func (inf APIInfo) WriteOKResponse(resp any) (int, error, error) { + WriteResp(inf.w, inf.request, resp) + return http.StatusOK, nil, nil +} + +// WriteOKResponseWithSummary writes a 200 OK response with the given object as +// the 'response' property of the response body, and the given count as the +// `count` property of the response's summary. +// +// This CANNOT be used by any APIInfo that wasn't constructed for the caller by +// Wrap - ing a Handler (yet). +// +// Deprecated: Summary sections on responses were intended to cover up for a +// deficiency in jQuery-based tables on the front-end, so now that we aren't +// using those anymore it serves no purpose. +func (inf APIInfo) WriteOKResponseWithSummary(resp any, count uint64) (int, error, error) { + WriteRespWithSummary(inf.w, inf.request, resp, count) + return http.StatusOK, nil, nil +} + +// WriteNotModifiedResponse writes a 304 Not Modified response with the given +// object as the 'response' property of the response body. +// +// This CANNOT be used by any APIInfo that wasn't constructed for the caller by +// Wrap - ing a Handler (yet). +func (inf APIInfo) WriteNotModifiedResponse(resp any) (int, error, error) { + inf.w.WriteHeader(http.StatusNotModified) + WriteResp(inf.w, inf.request, resp) + return http.StatusNotModified, nil, nil +} + +// WriteSuccessResponse writes the given response object as the `response` +// property of the response body, with the accompanying message as a +// success-level Alert. +func (inf APIInfo) WriteSuccessResponse(resp any, message string) (int, error, error) { + WriteAlertsObj(inf.w, inf.request, http.StatusOK, tc.CreateAlerts(tc.SuccessLevel, message), resp) + return http.StatusOK, nil, nil +} + +// WriteCreatedResponse writes the given response object as the `response` +// property of the response body of a 201 created response, with the +// accompanying message as a success-level Alert. It also sets the Location +// header to the given path. This will be automatically prefaced with the +// correct path to the API version the client requested. +func (inf APIInfo) WriteCreatedResponse(resp any, message, path string) (int, error, error) { + inf.w.Header().Set(rfc.Location, strings.Join([]string{"/api", inf.Version.String(), strings.TrimPrefix(path, "/")}, "/")) + inf.w.WriteHeader(http.StatusCreated) + WriteAlertsObj(inf.w, inf.request, http.StatusCreated, tc.CreateAlerts(tc.SuccessLevel, message), resp) + return http.StatusCreated, nil, nil +} + +// RequestHeaders returns the headers sent by the client in the API request. +func (inf APIInfo) RequestHeaders() http.Header { + return inf.request.Header +} + +// SetLastModified sets the "last modified" header on the response writer. +// +// This CANNOT be used by any APIInfo that wasn't constructed for the caller by +// Wrap - ing a Handler (yet). +func (inf APIInfo) SetLastModified(t time.Time) { + inf.w.Header().Set(rfc.LastModified, FormatLastModified(t)) +} + +// DecodeBody reads the client request's body and attempts to decode it into the +// provided reference. +func (inf APIInfo) DecodeBody(ref any) error { + return json.NewDecoder(inf.request.Body).Decode(ref) +} + // SendMail is a convenience method used to call SendMail using an APIInfo structure's configuration. func (inf *APIInfo) SendMail(to rfc.EmailAddress, msg []byte) (int, error, error) { return SendMail(to, msg, inf.Config) diff --git a/traffic_ops/traffic_ops_golang/api/api_test.go b/traffic_ops/traffic_ops_golang/api/api_test.go index 3118e6d6b5..08d635144f 100644 --- a/traffic_ops/traffic_ops_golang/api/api_test.go +++ b/traffic_ops/traffic_ops_golang/api/api_test.go @@ -26,11 +26,15 @@ import ( "errors" "fmt" "net/http" + "net/http/httptest" "net/url" + "strings" "testing" + "time" "github.com/lib/pq" + "github.com/apache/trafficcontrol/lib/go-rfc" "github.com/apache/trafficcontrol/lib/go-tc" ) @@ -262,3 +266,205 @@ func TestParseRestrictFKConstraint(t *testing.T) { }) } } + +func TestAPIInfo_WriteOKResponse(t *testing.T) { + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "/", nil) + + inf := APIInfo{ + request: r, + w: w, + } + code, userErr, sysErr := inf.WriteOKResponse("test") + if code != http.StatusOK { + t.Errorf("WriteOKResponse should return a %d %s code, got: %d %s", http.StatusOK, http.StatusText(http.StatusOK), code, http.StatusText(code)) + } + if userErr != nil { + t.Errorf("Unexpected user error: %v", userErr) + } + if sysErr != nil { + t.Errorf("Unexpected system error: %v", sysErr) + } + + if w.Code != http.StatusOK { + t.Errorf("incorrect response status code; want: %d, got: %d", http.StatusOK, w.Code) + } +} +func TestAPIInfo_WriteOKResponseWithSummary(t *testing.T) { + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "/", nil) + + inf := APIInfo{ + request: r, + w: w, + } + code, userErr, sysErr := inf.WriteOKResponseWithSummary("test", 42) + if code != http.StatusOK { + t.Errorf("WriteOKResponseWithSummary should return a %d %s code, got: %d %s", http.StatusOK, http.StatusText(http.StatusOK), code, http.StatusText(code)) + } + if userErr != nil { + t.Errorf("Unexpected user error: %v", userErr) + } + if sysErr != nil { + t.Errorf("Unexpected system error: %v", sysErr) + } + + if w.Code != http.StatusOK { + t.Errorf("incorrect response status code; want: %d, got: %d", http.StatusOK, w.Code) + } +} +func TestAPIInfo_WriteNotModifiedResponse(t *testing.T) { + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "/", nil) + + inf := APIInfo{ + request: r, + w: w, + } + code, userErr, sysErr := inf.WriteNotModifiedResponse("test") + if code != http.StatusNotModified { + t.Errorf("WriteNotModifiedResponse should return a %d %s code, got: %d %s", http.StatusNotModified, http.StatusText(http.StatusNotModified), code, http.StatusText(code)) + } + if userErr != nil { + t.Errorf("Unexpected user error: %v", userErr) + } + if sysErr != nil { + t.Errorf("Unexpected system error: %v", sysErr) + } + + if w.Code != http.StatusNotModified { + t.Errorf("incorrect response status code; want: %d, got: %d", http.StatusNotModified, w.Code) + } +} + +func TestAPIInfo_WriteSuccessResponse(t *testing.T) { + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodGet, "/", nil) + + inf := APIInfo{ + request: r, + w: w, + } + code, userErr, sysErr := inf.WriteSuccessResponse("test", "quest") + if code != http.StatusOK { + t.Errorf("WriteSuccessResponse should return a %d %s code, got: %d %s", http.StatusOK, http.StatusText(http.StatusOK), code, http.StatusText(code)) + } + if userErr != nil { + t.Errorf("Unexpected user error: %v", userErr) + } + if sysErr != nil { + t.Errorf("Unexpected system error: %v", sysErr) + } + + if w.Code != http.StatusOK { + t.Errorf("incorrect response status code; want: %d, got: %d", http.StatusOK, w.Code) + } + var alerts tc.Alerts + if err := json.NewDecoder(w.Body).Decode(&alerts); err != nil { + t.Fatalf("couldn't decode response body: %v", err) + } + + if len(alerts.Alerts) != 1 { + t.Fatalf("expected exactly one alert; got: %d", len(alerts.Alerts)) + } + alert := alerts.Alerts[0] + if alert.Level != tc.SuccessLevel.String() { + t.Errorf("Incorrect alert level; want: %s, got: %s", tc.SuccessLevel, alert.Level) + } + if alert.Text != "quest" { + t.Errorf("Incorrect alert text; want: 'quest', got: '%s'", alert.Text) + } +} + +func TestAPIInfo_WriteCreatedResponse(t *testing.T) { + w := httptest.NewRecorder() + r := httptest.NewRequest(http.MethodPost, "/", nil) + + inf := APIInfo{ + request: r, + Version: &Version{Major: 420, Minor: 9001}, + w: w, + } + code, userErr, sysErr := inf.WriteCreatedResponse("test", "quest", "mypath") + if code != http.StatusCreated { + t.Errorf("WriteCreatedResponse should return a %d %s code, got: %d %s", http.StatusCreated, http.StatusText(http.StatusCreated), code, http.StatusText(code)) + } + if userErr != nil { + t.Errorf("Unexpected user error: %v", userErr) + } + if sysErr != nil { + t.Errorf("Unexpected system error: %v", sysErr) + } + + if w.Code != http.StatusCreated { + t.Errorf("incorrect response status code; want: %d, got: %d", http.StatusCreated, w.Code) + } + if locHdr := w.Header().Get(rfc.Location); locHdr != "/api/420.9001/mypath" { + t.Errorf("incorrect '%s' header value; want: '/api/420.9001/mypath', got: '%s'", rfc.Location, locHdr) + } + var alerts tc.Alerts + if err := json.NewDecoder(w.Body).Decode(&alerts); err != nil { + t.Fatalf("couldn't decode response body: %v", err) + } + + if len(alerts.Alerts) != 1 { + t.Fatalf("expected exactly one alert; got: %d", len(alerts.Alerts)) + } + alert := alerts.Alerts[0] + if alert.Level != tc.SuccessLevel.String() { + t.Errorf("Incorrect alert level; want: %s, got: %s", tc.SuccessLevel, alert.Level) + } + if alert.Text != "quest" { + t.Errorf("Incorrect alert text; want: 'quest', got: '%s'", alert.Text) + } +} + +func TestAPIInfo_RequestHeaders(t *testing.T) { + r := httptest.NewRequest(http.MethodGet, "/", nil) + r.Header.Set("test", "quest") + + inf := APIInfo{ + request: r, + } + testHdr := inf.RequestHeaders().Get("test") + if testHdr != "quest" { + t.Errorf("should have retrieved the 'test' header (expected value: 'quest'), but found that header to have value: '%s'", testHdr) + } + +} + +func TestAPIInfo_SetLastModified(t *testing.T) { + w := httptest.NewRecorder() + inf := APIInfo{w: w} + tm := time.Now().Truncate(time.Second) + inf.SetLastModified(tm) + + wLMHdr := w.Header().Get(rfc.LastModified) + lm, err := time.Parse(time.RFC1123, wLMHdr) + if err != nil { + t.Fatalf("Failed to parse the response writer's '%s' header as an RFC1123 timestamp: %v", rfc.LastModified, err) + } + + // For unknown reasons, our API always adds a second to the truncated time + // value for LastModified headers. I suspect it's a poor attempt at rounding + // - for which the `Round` method ought to be used instead. + if expected := tm.Add(time.Second); lm != expected { + t.Errorf("Incorrect time set as '%s' header; want: %v, got: %v", rfc.LastModified, expected, lm) + } +} + +func TestAPIInfo_DecodeBody(t *testing.T) { + inf := APIInfo{ + request: httptest.NewRequest(http.MethodConnect, "/", strings.NewReader(`{"test": "quest"}`)), + } + + var out struct { + Test string `json:"test"` + } + if err := inf.DecodeBody(&out); err != nil { + t.Fatalf("failed to decode body: %v", err) + } + if out.Test != "quest" { + t.Errorf(`incorrect request body parsed; want: {"test": "quest"}, got: {"test": "%s"}`, out.Test) + } +} diff --git a/traffic_ops/traffic_ops_golang/api/shared_handlers.go b/traffic_ops/traffic_ops_golang/api/shared_handlers.go index 69ecf964ab..8ba9087b82 100644 --- a/traffic_ops/traffic_ops_golang/api/shared_handlers.go +++ b/traffic_ops/traffic_ops_golang/api/shared_handlers.go @@ -700,6 +700,7 @@ func Wrap(h Handler, requiredParams, intParams []string) http.HandlerFunc { return } defer inf.Close() + inf.w = w errCode, userErr, sysErr = h(inf) if userErr != nil || sysErr != nil { From de2db962965f99a79607e6842312b50298b87863 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Mon, 10 Jul 2023 13:25:31 -0600 Subject: [PATCH 09/31] fix a bug where error codes weren't being set when they were handled (cherry picked from commit 1c02baea02c23d93a80c850b0eea1f999af7e300) --- traffic_ops/traffic_ops_golang/api/api.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/traffic_ops/traffic_ops_golang/api/api.go b/traffic_ops/traffic_ops_golang/api/api.go index 3383e21080..94a37ff538 100644 --- a/traffic_ops/traffic_ops_golang/api/api.go +++ b/traffic_ops/traffic_ops_golang/api/api.go @@ -215,6 +215,9 @@ func HandleErr(w http.ResponseWriter, r *http.Request, tx *sql.Tx, statusCode in log.Errorln("rolling back transaction: " + err.Error()) } } + + w.Header().Set(rfc.ContentType, rfc.ApplicationJSON) + w.WriteHeader(statusCode) handleSimpleErr(w, r, statusCode, userErr, sysErr) } From d9c4f5f3e0b135b39233916a2a840c1b69643351 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Mon, 14 Aug 2023 13:32:15 -0600 Subject: [PATCH 10/31] Fix debug binaries no longer ignored --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4e14022fa7..223bea41ea 100644 --- a/.gitignore +++ b/.gitignore @@ -61,7 +61,7 @@ local.tar.gz *.sublime-project *.sublime-workspace .vscode/ -__debug_bin +__debug_bin* *.code-workspace *.pydevproject .idea/ From 412b7ce7d1da69b3bf02fa16bd8fde9487ac24d6 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Mon, 14 Aug 2023 13:37:35 -0600 Subject: [PATCH 11/31] add a check for proper api version in wrapper --- traffic_ops/traffic_ops_golang/api/shared_handlers.go | 7 +++++++ .../traffic_ops_golang/api/shared_handlers_test.go | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/traffic_ops/traffic_ops_golang/api/shared_handlers.go b/traffic_ops/traffic_ops_golang/api/shared_handlers.go index 8ba9087b82..211338b43e 100644 --- a/traffic_ops/traffic_ops_golang/api/shared_handlers.go +++ b/traffic_ops/traffic_ops_golang/api/shared_handlers.go @@ -37,6 +37,8 @@ import ( "github.com/apache/trafficcontrol/lib/go-tc" ) +const nilVersionErrorMsg = "a wrapped handler was called without an API version" + type KeyFieldInfo struct { Field string Func func(string) (interface{}, error) @@ -692,6 +694,8 @@ type Handler = func(*APIInfo) (int, error, error) // authentication and context setup. // Also note that handlers utilizing this need not defer closing of the provided // APIInfo, as this will handle that for them. +// Finally, make sure this is ONLY used on versioned endpoints; this will return +// an internal error if there is no associated API version. func Wrap(h Handler, requiredParams, intParams []string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { inf, userErr, sysErr, errCode := NewInfo(r, requiredParams, intParams) @@ -700,6 +704,9 @@ func Wrap(h Handler, requiredParams, intParams []string) http.HandlerFunc { return } defer inf.Close() + if inf.Version == nil { + HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New(nilVersionErrorMsg)) + } inf.w = w errCode, userErr, sysErr = h(inf) diff --git a/traffic_ops/traffic_ops_golang/api/shared_handlers_test.go b/traffic_ops/traffic_ops_golang/api/shared_handlers_test.go index b6e375bc10..8e2c4ae0ea 100644 --- a/traffic_ops/traffic_ops_golang/api/shared_handlers_test.go +++ b/traffic_ops/traffic_ops_golang/api/shared_handlers_test.go @@ -387,8 +387,17 @@ func TestWrap(t *testing.T) { r = wrapContext(r, auth.CurrentUserKey, auth.CurrentUser{}) r = wrapContext(r, PathParamsKey, make(map[string]string)) + mock.ExpectBegin() + mock.ExpectRollback() + h(w, r) + if w.Code != http.StatusInternalServerError { + t.Errorf("wrong status code when the trivial handler is used without an API version; want: %d, got: %d", http.StatusInternalServerError, w.Code) + } + + w = httptest.NewRecorder() mock.ExpectBegin() mock.ExpectCommit() + r.URL.Path = "/api/1.0/something" h(w, r) if w.Code != http.StatusOK { t.Errorf("wrong status code from a normal run of the trivial handler; want: %d, got: %d", http.StatusOK, w.Code) From a3e3ddaf5872dd7f02f2fac3d9d6002b4e529d9b Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Mon, 14 Aug 2023 16:31:21 -0600 Subject: [PATCH 12/31] Update GET handler for APIv5 Also converted the handler to make use of api.Wrap --- .../traffic_ops_golang/routing/routes.go | 6 +- .../traffic_ops_golang/server/servers.go | 337 ++++++++---------- .../traffic_ops_golang/server/servers_test.go | 246 ++++++------- 3 files changed, 260 insertions(+), 329 deletions(-) diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go b/traffic_ops/traffic_ops_golang/routing/routes.go index 6e21f9d382..9139f3847e 100644 --- a/traffic_ops/traffic_ops_golang/routing/routes.go +++ b/traffic_ops/traffic_ops_golang/routing/routes.go @@ -317,7 +317,7 @@ func Routes(d ServerData) ([]Route, http.Handler, error) { {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPost, Path: `servers/{id-or-name}/update$`, Handler: server.UpdateHandlerV4, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:UPDATE", "SERVER:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 4438132331}, //Server: CRUD - {Version: api.Version{Major: 5, Minor: 0}, 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: 472095928531}, + {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodGet, Path: `servers/?$`, Handler: api.Wrap(server.Read, nil, nil), 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: 472095928531}, {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPut, Path: `servers/{id}$`, Handler: server.Update, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:UPDATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 45863410331}, {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPost, Path: `servers/?$`, Handler: server.Create, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:CREATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 422555806131}, {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodDelete, Path: `servers/{id}$`, Handler: server.Delete, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:DELETE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 49232223331}, @@ -721,7 +721,7 @@ func Routes(d ServerData) ([]Route, http.Handler, error) { {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodPost, Path: `servers/{id-or-name}/update$`, Handler: server.UpdateHandlerV4, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:UPDATE", "SERVER:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 443813233}, //Server: CRUD - {Version: api.Version{Major: 4, Minor: 0}, 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: 47209592853}, + {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodGet, Path: `servers/?$`, Handler: api.Wrap(server.Read, nil, nil), 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: 47209592853}, {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodPut, Path: `servers/{id}$`, Handler: server.Update, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:UPDATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 4586341033}, {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodPost, Path: `servers/?$`, Handler: server.Create, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:CREATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 42255580613}, {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodDelete, Path: `servers/{id}$`, Handler: server.Delete, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:DELETE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 4923222333}, @@ -1119,7 +1119,7 @@ func Routes(d ServerData) ([]Route, http.Handler, error) { {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodPost, Path: `servers/{id-or-name}/update$`, Handler: server.UpdateHandler, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 143813233}, //Server: CRUD - {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodGet, Path: `servers/?$`, Handler: server.Read, RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 27209592853}, + {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodGet, Path: `servers/?$`, Handler: api.Wrap(server.Read, nil, nil), RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 27209592853}, {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodPut, Path: `servers/{id}$`, Handler: server.Update, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 2586341033}, {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodPost, Path: `servers/?$`, Handler: server.Create, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 22255580613}, {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodDelete, Path: `servers/{id}$`, Handler: server.Delete, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 2923222333}, diff --git a/traffic_ops/traffic_ops_golang/server/servers.go b/traffic_ops/traffic_ops_golang/server/servers.go index 4253514d5f..6c3c39bc38 100644 --- a/traffic_ops/traffic_ops_golang/server/servers.go +++ b/traffic_ops/traffic_ops_golang/server/servers.go @@ -139,7 +139,6 @@ SELECT s.phys_location AS phys_location_id, (SELECT ARRAY_AGG(sp.profile_name ORDER BY sp.priority ASC) FROM server_profile AS sp where sp.server=s.id) AS profile_name, s.rack, - s.revalidate_update_time > s.revalidate_apply_time AS reval_pending, s.revalidate_update_time, s.revalidate_apply_time, st.name AS status, @@ -147,7 +146,6 @@ SELECT s.tcp_port, t.name AS server_type, s.type AS server_type_id, - s.config_update_time > s.config_apply_time AS upd_pending, s.config_update_time, s.config_apply_time, s.xmpp_id, @@ -671,74 +669,47 @@ and p.id = $1 } // Read is the handler for GET requests to /servers. -func Read(w http.ResponseWriter, r *http.Request) { - var maxTime *time.Time - inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil) - tx := inf.Tx.Tx - if userErr != nil || sysErr != nil { - api.HandleErr(w, r, tx, errCode, userErr, sysErr) - return - } - defer inf.Close() - - // Middleware should've already handled this, so idk why this is a pointer at all tbh +func Read(inf *api.APIInfo) (int, error, error) { + useIMS := inf.UseIMS() version := inf.Version - if version == nil { - middleware.NotImplementedHandler().ServeHTTP(w, r) - return - } - servers := []tc.ServerV40{} - var serverCount uint64 - cfg, e := api.GetConfig(r.Context()) - useIMS := false - if e == nil && cfg != nil { - useIMS = cfg.UseIMS - } else { - log.Warnf("Couldn't get config %v", e) - } - - servers, serverCount, userErr, sysErr, errCode, maxTime = getServers(r.Header, inf.Params, inf.Tx, inf.User, useIMS, *version, inf.Config.RoleBasedPermissions) - if maxTime != nil && api.SetLastModifiedHeader(r, useIMS) { - api.AddLastModifiedHdr(w, *maxTime) + servers, serverCount, userErr, sysErr, errCode, maxTime := getServers(inf.RequestHeaders(), inf.Params, inf.Tx, inf.User, useIMS, *version, inf.Config.RoleBasedPermissions) + if useIMS && maxTime != nil { + inf.SetLastModified(*maxTime) } if errCode == http.StatusNotModified { - w.WriteHeader(errCode) - api.WriteResp(w, r, []tc.ServerV40{}) - return + return inf.WriteNotModifiedResponse([]tc.ServerV5{}) } if userErr != nil || sysErr != nil { - api.HandleErr(w, r, tx, errCode, userErr, sysErr) - return + return errCode, userErr, sysErr } - if version.Major >= 4 { - if version.Minor >= 1 { - api.WriteRespWithSummary(w, r, servers, serverCount) - return - } - v40Servers := make([]tc.ServerV40, 0) - for _, server := range servers { - v40Servers = append(v40Servers, server) - } - api.WriteRespWithSummary(w, r, v40Servers, serverCount) - return + if version.Major >= 5 { + return inf.WriteOKResponse(servers) + } + + downgraded := make([]tc.ServerV4, len(servers), len(servers)) + for i := range servers { + downgraded[i] = tc.ServerV4(servers[i].Downgrade()) + } + if version.Major == 4 { + return inf.WriteOKResponseWithSummary(downgraded, serverCount) } - v3Servers := make([]tc.ServerV30, 0) - for _, server := range servers { + + tx := inf.Tx.Tx + v3Servers := make([]tc.ServerV30, len(downgraded), len(downgraded)) + for i, server := range downgraded { csp, err := dbhelpers.GetCommonServerPropertiesFromV4(server, tx) if err != nil { - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("failed to get common server properties from V4 server struct: %w", err)) - return + return http.StatusInternalServerError, nil, fmt.Errorf("failed to get common server properties from V4 server struct: %w", err) } + v3Server, err := server.ToServerV3FromV4(csp) if err != nil { - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("failed to convert servers to V3 format: %w", err)) - return + return http.StatusInternalServerError, nil, fmt.Errorf("failed to convert servers to V3 format: %w", err) } - v3Servers = append(v3Servers, v3Server) + v3Servers[i] = v3Server } - api.WriteRespWithSummary(w, r, v3Servers, serverCount) - return + return inf.WriteOKResponseWithSummary(v3Servers, serverCount) } func selectMaxLastUpdatedQuery(queryAddition string, where string) string { @@ -767,30 +738,25 @@ 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, roleBasedPerms bool) ([]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, roleBasedPerms bool) ([]tc.ServerV5, uint64, error, error, int, *time.Time) { var maxTime time.Time var runSecond bool // Query Parameters to Database Query column mappings // see the fields mapped in the SQL query queryParamsToSQLCols := map[string]dbhelpers.WhereColumnInfo{ - "cachegroup": {Column: "s.cachegroup", Checker: api.IsInt}, - "parentCachegroup": {Column: "cg.parent_cachegroup_id", Checker: api.IsInt}, - "cachegroupName": {Column: "cg.name", Checker: nil}, - "cdn": {Column: "s.cdn_id", Checker: api.IsInt}, - "id": {Column: "s.id", Checker: api.IsInt}, - "hostName": {Column: "s.host_name", Checker: nil}, - "physLocation": {Column: "s.phys_location", Checker: api.IsInt}, - "status": {Column: "st.name", Checker: nil}, - "topology": {Column: "tc.topology", Checker: nil}, - "type": {Column: "t.name", Checker: nil}, - "dsId": {Column: "dss.deliveryservice", Checker: nil}, - } - - if version.Major == 3 { - queryParamsToSQLCols["profileId"] = dbhelpers.WhereColumnInfo{ - Column: "s.profile", - Checker: api.IsInt, - } + "cachegroup": {Column: "s.cachegroup", Checker: api.IsInt}, + "parentCachegroup": {Column: "cg.parent_cachegroup_id", Checker: api.IsInt}, + "cachegroupName": {Column: "cg.name", Checker: nil}, + "cdn": {Column: "s.cdn_id", Checker: api.IsInt}, + "id": {Column: "s.id", Checker: api.IsInt}, + "hostName": {Column: "s.host_name", Checker: nil}, + "physLocation": {Column: "s.phys_location", Checker: nil}, + "physicalLocationID": {Column: "s.phys_location_id", Checker: api.IsInt}, + "physicalLocation": {Column: "s.phys_location", Checker: nil}, + "status": {Column: "st.name", Checker: nil}, + "topology": {Column: "tc.topology", Checker: nil}, + "type": {Column: "t.name", Checker: nil}, + "dsId": {Column: "dss.deliveryservice", Checker: nil}, } if version.Major >= 4 { @@ -798,6 +764,11 @@ func getServers(h http.Header, params map[string]string, tx *sqlx.Tx, user *auth Column: "sp.profile_name", Checker: nil, } + } else { + queryParamsToSQLCols["profileId"] = dbhelpers.WhereColumnInfo{ + Column: "s.profile", + Checker: api.IsInt, + } } usesMids := false @@ -885,7 +856,7 @@ JOIN server_profile sp ON s.id = sp.server` return nil, 0, nil, fmt.Errorf("failed to get servers count: %v", err), http.StatusInternalServerError, nil } - serversList := []tc.ServerV40{} + serversList := []tc.ServerV5{} if useIMS { runSecond, maxTime = ims.TryIfModifiedSinceQuery(tx, h, queryValues, selectMaxLastUpdatedQuery(queryAddition, where)) if !runSecond { @@ -912,14 +883,15 @@ JOIN server_profile sp ON s.id = sp.server` HiddenField := "********" - servers := make(map[int]tc.ServerV40) + servers := make(map[int]tc.ServerV5) ids := []int{} for rows.Next() { - s := tc.ServerV40{} - err := rows.Scan(&s.Cachegroup, - &s.CachegroupID, + var s tc.ServerV5 + err := rows.Scan( + &s.CacheGroup, + &s.CacheGroupID, &s.CDNID, - &s.CDNName, + &s.CDN, &s.DomainName, &s.GUID, &s.HostName, @@ -935,11 +907,10 @@ JOIN server_profile sp ON s.id = sp.server` &s.MgmtIPGateway, &s.MgmtIPNetmask, &s.OfflineReason, - &s.PhysLocation, - &s.PhysLocationID, - pq.Array(&s.ProfileNames), + &s.PhysicalLocation, + &s.PhysicalLocationID, + pq.Array(&s.Profiles), &s.Rack, - &s.RevalPending, &s.RevalUpdateTime, &s.RevalApplyTime, &s.Status, @@ -947,14 +918,13 @@ JOIN server_profile sp ON s.id = sp.server` &s.TCPPort, &s.Type, &s.TypeID, - &s.UpdPending, &s.ConfigUpdateTime, &s.ConfigApplyTime, &s.XMPPID, &s.XMPPPasswd, &s.StatusLastUpdated) if err != nil { - return nil, serverCount, nil, errors.New("getting servers: " + err.Error()), http.StatusInternalServerError, nil + return nil, serverCount, nil, fmt.Errorf("getting servers: %w", err), http.StatusInternalServerError, nil } if (version.GreaterThanOrEqualTo(&api.Version{Major: 4}) && roleBasedPerms) || version.GreaterThanOrEqualTo(&api.Version{Major: 5}) { if !user.Can("SECURE-SERVER:READ") { @@ -966,14 +936,11 @@ JOIN server_profile sp ON s.id = sp.server` s.XMPPPasswd = &HiddenField } - if s.ID == nil { - return nil, serverCount, nil, errors.New("found server with nil ID"), http.StatusInternalServerError, nil + if _, ok := servers[s.ID]; ok { + return nil, serverCount, nil, fmt.Errorf("found more than one server with ID #%d", s.ID), http.StatusInternalServerError, nil } - if _, ok := servers[*s.ID]; ok { - return nil, serverCount, nil, fmt.Errorf("found more than one server with ID #%d", *s.ID), http.StatusInternalServerError, nil - } - servers[*s.ID] = s - ids = append(ids, *s.ID) + servers[s.ID] = s + ids = append(ids, s.ID) } // if ds requested uses mid-tier caches, add those to the list as well @@ -990,7 +957,7 @@ JOIN server_profile sp ON s.id = sp.server` } if len(ids) < 1 { - return []tc.ServerV40{}, serverCount, nil, nil, http.StatusOK, nil + return []tc.ServerV5{}, 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) @@ -1061,7 +1028,7 @@ JOIN server_profile sp ON s.id = sp.server` } } - returnable := make([]tc.ServerV40, 0, len(ids)) + returnable := make([]tc.ServerV5, 0, len(ids)) for _, id := range ids { server := servers[id] @@ -1075,7 +1042,7 @@ JOIN server_profile sp ON s.id = sp.server` } // 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.ServerV5, dsID int, cdnID int, tx *sqlx.Tx, includeCapabilities bool) ([]int, error, error, int) { if len(edgeIDs) == 0 { return nil, nil, nil, http.StatusOK } @@ -1143,11 +1110,12 @@ func getMidServers(edgeIDs []int, servers map[int]tc.ServerV40, dsID int, cdnID ids := []int{} for rows.Next() { - var s tc.ServerV40 - if err := rows.Scan(&s.Cachegroup, - &s.CachegroupID, + var s tc.ServerV5 + if err := rows.Scan( + &s.CacheGroup, + &s.CacheGroupID, &s.CDNID, - &s.CDNName, + &s.CDN, &s.DomainName, &s.GUID, &s.HostName, @@ -1163,11 +1131,10 @@ func getMidServers(edgeIDs []int, servers map[int]tc.ServerV40, dsID int, cdnID &s.MgmtIPGateway, &s.MgmtIPNetmask, &s.OfflineReason, - &s.PhysLocation, - &s.PhysLocationID, - pq.Array(&s.ProfileNames), + &s.PhysicalLocation, + &s.PhysicalLocationID, + pq.Array(&s.Profiles), &s.Rack, - &s.RevalPending, &s.RevalUpdateTime, &s.RevalApplyTime, &s.Status, @@ -1175,7 +1142,6 @@ func getMidServers(edgeIDs []int, servers map[int]tc.ServerV40, dsID int, cdnID &s.TCPPort, &s.Type, &s.TypeID, - &s.UpdPending, &s.ConfigUpdateTime, &s.ConfigApplyTime, &s.XMPPID, @@ -1184,46 +1150,42 @@ func getMidServers(edgeIDs []int, servers map[int]tc.ServerV40, dsID int, cdnID log.Errorf("could not scan mid servers: %s\n", err) return nil, nil, err, http.StatusInternalServerError } - if s.ID == nil { - return nil, nil, errors.New("found server with nil ID"), http.StatusInternalServerError - } // This may mean that the server was caught by other query parameters, // so not technically an error, unlike earlier in 'getServers'. - if _, ok := servers[*s.ID]; ok { - continue + if _, ok := servers[s.ID]; !ok { + servers[s.ID] = s + ids = append(ids, s.ID) } - servers[*s.ID] = s - ids = append(ids, *s.ID) } return ids, nil, nil, http.StatusOK } -func checkTypeChangeSafety(server tc.ServerV40, tx *sqlx.Tx) (error, error, int) { +func checkTypeChangeSafety(server tc.ServerV5, tx *sqlx.Tx) (error, error, int) { // see if cdn or type changed var cdnID int var typeID int - if err := tx.QueryRow("SELECT type, cdn_id FROM server WHERE id = $1", *server.ID).Scan(&typeID, &cdnID); err != nil { + if err := tx.QueryRow("SELECT type, cdn_id FROM server WHERE id = $1", server.ID).Scan(&typeID, &cdnID); err != nil { if err == sql.ErrNoRows { return errors.New("no server found with this ID"), nil, http.StatusNotFound } - return nil, fmt.Errorf("getting current server type: %v", err), http.StatusInternalServerError + return nil, fmt.Errorf("getting current server type: %w", err), http.StatusInternalServerError } var dsIDs []int64 - if err := tx.QueryRowx("SELECT ARRAY(SELECT deliveryservice FROM deliveryservice_server WHERE server = $1)", server.ID).Scan(pq.Array(&dsIDs)); err != nil && err != sql.ErrNoRows { - return nil, fmt.Errorf("getting server assigned delivery services: %v", err), http.StatusInternalServerError + if err := tx.QueryRowx("SELECT ARRAY(SELECT deliveryservice FROM deliveryservice_server WHERE server = $1)", server.ID).Scan(pq.Array(&dsIDs)); err != nil && !errors.Is(err, sql.ErrNoRows) { + return nil, fmt.Errorf("getting server assigned delivery services: %w", err), http.StatusInternalServerError } // If type is changing ensure it isn't assigned to any DSes. - if typeID != *server.TypeID { + if typeID != server.TypeID { if len(dsIDs) != 0 { return errors.New("server type can not be updated when it is currently assigned to Delivery Services"), nil, http.StatusConflict } } // Check to see if the user is trying to change the CDN of a server, which is already linked with a DS - if cdnID != *server.CDNID && len(dsIDs) != 0 { + if cdnID != server.CDNID && len(dsIDs) != 0 { return errors.New("server cdn can not be updated when it is currently assigned to delivery services"), nil, http.StatusConflict } @@ -1343,51 +1305,30 @@ func Update(w http.ResponseWriter, r *http.Request) { original := originals[0] if original.XMPPID == nil || *original.XMPPID == "" { - log.Warnf("original server %s had no XMPPID\n", *original.HostName) - } - if original.StatusID == nil { - sysErr = errors.New("original server had no status ID") - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, sysErr) - return - } - if original.Status == nil { - sysErr = errors.New("original server had no status name") - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, sysErr) - return - } - if original.CachegroupID == nil { - sysErr = errors.New("original server had no Cache Group ID") - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, sysErr) - return + log.Warnf("original server %s (#%d) had no XMPPID", original.HostName, original.ID) } if original.StatusLastUpdated == nil { log.Warnln("original server had no Status Last Updated time") - if original.LastUpdated == nil { - sysErr = errors.New("original server had no Last Updated time") - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, sysErr) - return - } - original.StatusLastUpdated = &original.LastUpdated.Time + original.StatusLastUpdated = util.Ptr(original.LastUpdated) } var originalXMPPID string if original.XMPPID != nil { originalXMPPID = *original.XMPPID } - originalStatusID := *original.StatusID + originalStatusID := original.StatusID - var server tc.ServerV40 + var server tc.ServerV5 var serverV3 tc.ServerV30 var statusLastUpdatedTime time.Time if inf.Version.Major >= 4 { - server.ID = new(int) - *server.ID = inf.IntParams["id"] + server.ID = inf.IntParams["id"] if err := json.NewDecoder(r.Body).Decode(&server); err != nil { api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil) return } - if server.StatusID != nil && *server.StatusID != originalStatusID { + if server.StatusID != originalStatusID { currentTime := time.Now() server.StatusLastUpdated = ¤tTime statusLastUpdatedTime = currentTime @@ -1395,7 +1336,8 @@ func Update(w http.ResponseWriter, r *http.Request) { server.StatusLastUpdated = original.StatusLastUpdated statusLastUpdatedTime = *original.StatusLastUpdated } - _, userErr, sysErr := validateV4(&server, tx) + tmp := server.Downgrade() + _, userErr, sysErr := validateV4(&tmp, tx) if userErr != nil || sysErr != nil { errCode := http.StatusBadRequest if sysErr != nil { @@ -1404,6 +1346,7 @@ func Update(w http.ResponseWriter, r *http.Request) { api.HandleErr(w, r, tx, errCode, userErr, sysErr) return } + server = tmp.Upgrade() } else { serverV3.ID = new(int) *serverV3.ID = inf.IntParams["id"] @@ -1440,46 +1383,45 @@ func Update(w http.ResponseWriter, r *http.Request) { } profileNames := []string{profileName} - server, err = serverV3.UpgradeToV40(profileNames) + upgraded, err := serverV3.UpgradeToV40(profileNames) if err != nil { - sysErr = fmt.Errorf("error upgrading valid V3 server to V4 structure: %v", err) + sysErr = fmt.Errorf("error upgrading valid V3 server to V4 structure: %w", err) api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, sysErr) return } + server = upgraded.Upgrade() } - if *original.CachegroupID != *server.CachegroupID || *original.CDNID != *server.CDNID { - hasDSOnCDN, err := dbhelpers.CachegroupHasTopologyBasedDeliveryServicesOnCDN(tx, *original.CachegroupID, *original.CDNID) + if original.CacheGroupID != server.CacheGroupID || original.CDNID != server.CDNID { + hasDSOnCDN, err := dbhelpers.CachegroupHasTopologyBasedDeliveryServicesOnCDN(tx, original.CacheGroupID, original.CDNID) if err != nil { api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, err) return } CDNIDs := []int{} if hasDSOnCDN { - CDNIDs = append(CDNIDs, *original.CDNID) + CDNIDs = append(CDNIDs, original.CDNID) } - cacheGroupIds := []int{*original.CachegroupID} - serverIds := []int{*original.ID} - if err = topology_validation.CheckForEmptyCacheGroups(inf.Tx, cacheGroupIds, CDNIDs, true, serverIds); err != nil { - api.HandleErr(w, r, tx, http.StatusBadRequest, errors.New("server is the last one in its cachegroup, which is used by a topology, so it cannot be moved to another cachegroup: "+err.Error()), nil) + if err = topology_validation.CheckForEmptyCacheGroups(inf.Tx, []int{original.CacheGroupID}, CDNIDs, true, []int{original.ID}); err != nil { + api.HandleErr(w, r, tx, http.StatusBadRequest, fmt.Errorf("server is the last one in its cachegroup, which is used by a topology, so it cannot be moved to another cachegroup: %w", err), nil) return } } - status, ok, err := dbhelpers.GetStatusByID(*server.StatusID, tx) + status, ok, err := dbhelpers.GetStatusByID(server.StatusID, tx) if err != nil { - sysErr = fmt.Errorf("getting server #%d status (#%d): %v", id, *server.StatusID, err) + sysErr = fmt.Errorf("getting server #%d status (#%d): %v", id, server.StatusID, err) api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, sysErr) return } if !ok { - log.Warnf("previously existent status #%d not found when fetching later", *server.StatusID) - userErr = fmt.Errorf("no such Status: #%d", *server.StatusID) + log.Warnf("previously existent status #%d not found when fetching later", server.StatusID) + userErr = fmt.Errorf("no such Status: #%d", server.StatusID) api.HandleErr(w, r, tx, http.StatusBadRequest, userErr, nil) return } if status.Name == nil { - sysErr = fmt.Errorf("status #%d had no name", *server.StatusID) + sysErr = fmt.Errorf("status #%d had no name", server.StatusID) api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, sysErr) return } @@ -1508,20 +1450,20 @@ func Update(w http.ResponseWriter, r *http.Request) { return } - userErr, sysErr, statusCode := api.CheckIfUnModified(r.Header, inf.Tx, *server.ID, "server") + userErr, sysErr, statusCode := api.CheckIfUnModified(r.Header, inf.Tx, server.ID, "server") if userErr != nil || sysErr != nil { api.HandleErr(w, r, tx, statusCode, userErr, sysErr) return } - if server.CDNName != nil { - userErr, sysErr, statusCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, *server.CDNName, inf.User.UserName) + if server.CDN != "" { + userErr, sysErr, statusCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, server.CDN, inf.User.UserName) if userErr != nil || sysErr != nil { api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr) return } - } else if server.CDNID != nil { - userErr, sysErr, statusCode = dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(inf.Tx.Tx, int64(*server.CDNID), inf.User.UserName) + } else { + userErr, sysErr, statusCode = dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(inf.Tx.Tx, int64(server.CDNID), inf.User.UserName) if userErr != nil || sysErr != nil { api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr) return @@ -1529,13 +1471,13 @@ func Update(w http.ResponseWriter, r *http.Request) { } if inf.Version.Major >= 4 { - if err = dbhelpers.UpdateServerProfilesForV4(*server.ID, server.ProfileNames, tx); err != nil { + if err = dbhelpers.UpdateServerProfilesForV4(server.ID, server.Profiles, tx); err != nil { userErr, sysErr, errCode := api.ParseDBError(err) api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) return } } else { - if err = dbhelpers.UpdateServerProfileTableForV3(serverV3.ID, serverV3.ProfileID, (original.ProfileNames)[0], tx); err != nil { + if err = dbhelpers.UpdateServerProfileTableForV3(serverV3.ID, serverV3.ProfileID, (original.Profiles)[0], tx); err != nil { api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("failed to update server_profile: %w", err)) return } @@ -1644,7 +1586,7 @@ func Update(w http.ResponseWriter, r *http.Request) { api.CreateChangeLogRawTx(api.ApiChange, changeLogMsg, inf.User, tx) } -func updateServer(tx *sqlx.Tx, server tc.ServerV40) (int64, int, error, error) { +func updateServer(tx *sqlx.Tx, server tc.ServerV5) (int64, int, error, error) { rows, err := tx.NamedQuery(updateQuery, server) if err != nil { @@ -1656,10 +1598,11 @@ func updateServer(tx *sqlx.Tx, server tc.ServerV40) (int64, int, error, error) { var serverId int64 rowsAffected := 0 for rows.Next() { - if err := rows.Scan(&server.Cachegroup, - &server.CachegroupID, + if err := rows.Scan( + &server.CacheGroup, + &server.CacheGroupID, &server.CDNID, - &server.CDNName, + &server.CDN, &server.DomainName, &server.GUID, &server.HostName, @@ -1675,26 +1618,27 @@ func updateServer(tx *sqlx.Tx, server tc.ServerV40) (int64, int, error, error) { &server.MgmtIPGateway, &server.MgmtIPNetmask, &server.OfflineReason, - &server.PhysLocation, - &server.PhysLocationID, - pq.Array(&server.ProfileNames), + &server.PhysicalLocation, + &server.PhysicalLocationID, + pq.Array(&server.Profiles), &server.Rack, &server.Status, &server.StatusID, &server.TCPPort, &server.Type, &server.TypeID, - &server.StatusLastUpdated); err != nil { - return 0, http.StatusNotFound, nil, fmt.Errorf("scanning lastUpdated from server insert: %v", err) + &server.StatusLastUpdated, + ); err != nil { + return 0, http.StatusNotFound, nil, fmt.Errorf("scanning lastUpdated from server insert: %w", err) } rowsAffected++ } if rowsAffected < 1 { - return 0, http.StatusNotFound, fmt.Errorf("no server found with id %d", *server.ID), nil + return 0, http.StatusNotFound, fmt.Errorf("no server found with id %d", server.ID), nil } if rowsAffected > 1 { - return 0, http.StatusInternalServerError, nil, fmt.Errorf("update for server #%d affected too many rows (%d)", *server.ID, rowsAffected) + return 0, http.StatusInternalServerError, nil, fmt.Errorf("update for server #%d affected too many rows (%d)", server.ID, rowsAffected) } return serverId, http.StatusOK, nil, nil @@ -2189,8 +2133,7 @@ func Delete(w http.ResponseWriter, r *http.Request) { return } - var servers []tc.ServerV40 - servers, _, userErr, sysErr, errCode, _ = getServers(r.Header, map[string]string{"id": inf.Params["id"]}, inf.Tx, inf.User, false, *version, inf.Config.RoleBasedPermissions) + servers, _, userErr, sysErr, errCode, _ := getServers(r.Header, map[string]string{"id": inf.Params["id"]}, inf.Tx, inf.User, false, *version, inf.Config.RoleBasedPermissions) if userErr != nil || sysErr != nil { api.HandleErr(w, r, tx, errCode, userErr, sysErr) return @@ -2205,32 +2148,33 @@ func Delete(w http.ResponseWriter, r *http.Request) { return } server := servers[0] - if server.CDNName != nil { - userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, *server.CDNName, inf.User.UserName) + if server.CDN != "" { + userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, server.CDN, inf.User.UserName) if userErr != nil || sysErr != nil { api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) return } - } else if server.CDNID != nil { - userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(inf.Tx.Tx, int64(*server.CDNID), inf.User.UserName) + } else { + // when would this happen? + userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(inf.Tx.Tx, int64(server.CDNID), inf.User.UserName) if userErr != nil || sysErr != nil { api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) return } } - cacheGroupIds := []int{*server.CachegroupID} - serverIds := []int{*server.ID} - hasDSOnCDN, err := dbhelpers.CachegroupHasTopologyBasedDeliveryServicesOnCDN(inf.Tx.Tx, *server.CachegroupID, *server.CDNID) + cacheGroupIds := []int{server.CacheGroupID} + serverIds := []int{server.ID} + hasDSOnCDN, err := dbhelpers.CachegroupHasTopologyBasedDeliveryServicesOnCDN(inf.Tx.Tx, server.CacheGroupID, server.CDNID) if err != nil { api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, err) return } CDNIDs := []int{} if hasDSOnCDN { - CDNIDs = append(CDNIDs, *server.CDNID) + CDNIDs = append(CDNIDs, server.CDNID) } if err := topology_validation.CheckForEmptyCacheGroups(inf.Tx, cacheGroupIds, CDNIDs, true, serverIds); err != nil { - api.HandleErr(w, r, tx, http.StatusBadRequest, errors.New("server is the last one in its cachegroup, which is used by a topology: "+err.Error()), nil) + api.HandleErr(w, r, tx, http.StatusBadRequest, fmt.Errorf("server is the last one in its cachegroup, which is used by a topology: %w", err), nil) return } @@ -2240,23 +2184,26 @@ func Delete(w http.ResponseWriter, r *http.Request) { api.HandleErr(w, r, tx, errCode, userErr, sysErr) return } else if rowsAffected, err := result.RowsAffected(); err != nil { - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("getting rows affected by server delete: %v", err)) + api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("getting rows affected by server delete: %w", err)) return } else if rowsAffected != 1 { api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("incorrect number of rows affected: %d", rowsAffected)) return } - if inf.Version.Major >= 4 { + if inf.Version.Major >= 5 { api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Server deleted", server) + } else if inf.Version.Major >= 4 { + api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Server deleted", server.Downgrade()) } else { - csp, err := dbhelpers.GetCommonServerPropertiesFromV4(server, tx) + csp, err := dbhelpers.GetCommonServerPropertiesFromV4(server.Downgrade(), tx) if err != nil { userErr, sysErr, errCode := api.ParseDBError(err) api.HandleErr(w, r, tx, errCode, userErr, sysErr) return } - serverv3, err := server.ToServerV3FromV4(csp) + downgraded := server.Downgrade() + serverv3, err := downgraded.ToServerV3FromV4(csp) if err != nil { api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, err) return @@ -2264,6 +2211,6 @@ func Delete(w http.ResponseWriter, r *http.Request) { api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Server deleted", serverv3) } - changeLogMsg := fmt.Sprintf("SERVER: %s.%s, ID: %d, ACTION: deleted", *server.HostName, *server.DomainName, *server.ID) + changeLogMsg := fmt.Sprintf("SERVER: %s.%s, ID: %d, ACTION: deleted", server.HostName, server.DomainName, server.ID) api.CreateChangeLogRawTx(api.ApiChange, changeLogMsg, inf.User, tx) } diff --git a/traffic_ops/traffic_ops_golang/server/servers_test.go b/traffic_ops/traffic_ops_golang/server/servers_test.go index 56199998b3..3a5dcd574a 100644 --- a/traffic_ops/traffic_ops_golang/server/servers_test.go +++ b/traffic_ops/traffic_ops_golang/server/servers_test.go @@ -37,50 +37,48 @@ import ( ) type ServerAndInterfaces struct { - Server tc.ServerV40 + Server tc.ServerV5 Interface tc.ServerInterfaceInfoV40 } func getTestServers() []ServerAndInterfaces { servers := []ServerAndInterfaces{} - testServerV40 := tc.ServerV40{ - Cachegroup: util.StrPtr("Cachegroup"), - CachegroupID: util.IntPtr(1), - CDNID: util.IntPtr(1), - CDNName: util.StrPtr("cdnName"), - DomainName: util.StrPtr("domainName"), - GUID: util.StrPtr("guid"), - HostName: util.StrPtr("server1"), - HTTPSPort: util.IntPtr(443), - ID: util.IntPtr(1), - ILOIPAddress: util.StrPtr("iloIpAddress"), - ILOIPGateway: util.StrPtr("iloIpGateway"), - ILOIPNetmask: util.StrPtr("iloIpNetmask"), - ILOPassword: util.StrPtr("iloPassword"), - ILOUsername: util.StrPtr("iloUsername"), - LastUpdated: &tc.TimeNoMod{Time: time.Now()}, - MgmtIPAddress: util.StrPtr("mgmtIpAddress"), - MgmtIPGateway: util.StrPtr("mgmtIpGateway"), - MgmtIPNetmask: util.StrPtr("mgmtIpNetmask"), - OfflineReason: util.StrPtr("offlineReason"), - PhysLocation: util.StrPtr("physLocation"), - PhysLocationID: util.IntPtr(1), - ProfileNames: []string{"profile"}, - Rack: util.StrPtr("rack"), - RevalPending: util.BoolPtr(true), - Status: util.StrPtr("status"), - StatusID: util.IntPtr(1), - TCPPort: util.IntPtr(80), - Type: "EDGE", - TypeID: util.IntPtr(1), - UpdPending: util.BoolPtr(true), - XMPPID: util.StrPtr("xmppId"), - XMPPPasswd: util.StrPtr("xmppPasswd"), - StatusLastUpdated: &(time.Time{}), - ConfigUpdateTime: &(time.Time{}), - ConfigApplyTime: &(time.Time{}), - RevalUpdateTime: &(time.Time{}), - RevalApplyTime: &(time.Time{}), + testServerV40 := tc.ServerV5{ + CacheGroup: "Cache Group", + CacheGroupID: 1, + CDNID: 1, + CDN: "cdnName", + DomainName: "domainName", + GUID: util.Ptr("guid"), + HostName: "server1", + HTTPSPort: util.Ptr(443), + ID: 1, + ILOIPAddress: util.Ptr("iloIpAddress"), + ILOIPGateway: util.Ptr("iloIpGateway"), + ILOIPNetmask: util.Ptr("iloIpNetmask"), + ILOPassword: util.Ptr("iloPassword"), + ILOUsername: util.Ptr("iloUsername"), + LastUpdated: time.Now(), + MgmtIPAddress: util.Ptr("mgmtIpAddress"), + MgmtIPGateway: util.Ptr("mgmtIpGateway"), + MgmtIPNetmask: util.Ptr("mgmtIpNetmask"), + OfflineReason: util.Ptr("offlineReason"), + PhysicalLocation: "physLocation", + PhysicalLocationID: 1, + Profiles: []string{"profile"}, + Rack: util.Ptr("rack"), + Status: "status", + StatusID: 1, + TCPPort: util.Ptr(80), + Type: "EDGE", + TypeID: 1, + XMPPID: util.Ptr("xmppId"), + XMPPPasswd: util.Ptr("xmppPasswd"), + StatusLastUpdated: &(time.Time{}), + ConfigUpdateTime: &(time.Time{}), + ConfigApplyTime: &(time.Time{}), + RevalUpdateTime: &(time.Time{}), + RevalApplyTime: &(time.Time{}), } mtu := uint64(9500) @@ -105,15 +103,15 @@ func getTestServers() []ServerAndInterfaces { servers = append(servers, ServerAndInterfaces{Server: testServerV40, Interface: iface}) testServer2 := testServerV40 - testServer2.Cachegroup = util.StrPtr("cachegroup2") - testServer2.HostName = util.StrPtr("server2") - testServer2.ID = util.IntPtr(2) + testServer2.CacheGroup = "cachegroup2" + testServer2.HostName = "server2" + testServer2.ID = 2 servers = append(servers, ServerAndInterfaces{Server: testServer2, Interface: iface}) testServer3 := testServerV40 - testServer3.Cachegroup = util.StrPtr("cachegroup3") - testServer3.HostName = util.StrPtr("server3") - testServer3.ID = util.IntPtr(3) + testServer3.CacheGroup = "cachegroup3" + testServer3.HostName = "server3" + testServer3.ID = 3 servers = append(servers, ServerAndInterfaces{Server: testServer3, Interface: iface}) return servers @@ -134,7 +132,7 @@ func TestCheckTypeChangeSafety(t *testing.T) { rows := sqlmock.NewRows([]string{"type", "cdn_id"}) // note here that the cdnid is 5, which is not the same as the initial cdnid of the fist traffic server - rows.AddRow(*testServers[0].Server.TypeID, 5) + rows.AddRow(testServers[0].Server.TypeID, 5) // Make it return a list of atleast one associated ds dsrows := sqlmock.NewRows([]string{"array"}) dsrows.AddRow("{3}") @@ -142,11 +140,10 @@ func TestCheckTypeChangeSafety(t *testing.T) { mock.ExpectQuery("SELECT").WillReturnRows(rows) mock.ExpectQuery("SELECT ARRAY").WillReturnRows(dsrows) - s := tc.ServerV40{ - CDNID: testServers[0].Server.CDNID, - FqdnTime: time.Time{}, - TypeID: testServers[0].Server.TypeID, - ID: testServers[0].Server.ID, + s := tc.ServerV5{ + CDNID: testServers[0].Server.CDNID, + TypeID: testServers[0].Server.TypeID, + ID: testServers[0].Server.ID, } userErr, _, errCode := checkTypeChangeSafety(s, db.MustBegin()) @@ -177,8 +174,8 @@ func TestGetServersByCachegroup(t *testing.T) { cols := []string{"cachegroup", "cachegroup_id", "cdn_id", "cdn_name", "domain_name", "guid", "host_name", "https_port", "id", "ilo_ip_address", "ilo_ip_gateway", "ilo_ip_netmask", "ilo_password", "ilo_username", "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", + "phys_location_id", "profile_name", "rack", "revalidate_update_time", "revalidate_apply_time", + "status", "status_id", "tcp_port", "server_type", "server_type_id", "config_update_time", "config_apply_time", "xmpp_id", "xmpp_passwd", "status_last_updated"} interfaceCols := []string{"max_bandwidth", "monitor", "mtu", "name", "server", "router_host_name", "router_port_name"} rows := sqlmock.NewRows(cols) @@ -192,38 +189,36 @@ func TestGetServersByCachegroup(t *testing.T) { for _, srv := range testServers { ts := srv.Server rows = rows.AddRow( - *ts.Cachegroup, - *ts.CachegroupID, - *ts.CDNID, - *ts.CDNName, - *ts.DomainName, + ts.CacheGroup, + ts.CacheGroupID, + ts.CDNID, + ts.CDN, + ts.DomainName, *ts.GUID, - *ts.HostName, + ts.HostName, *ts.HTTPSPort, - *ts.ID, + ts.ID, *ts.ILOIPAddress, *ts.ILOIPGateway, *ts.ILOIPNetmask, *ts.ILOPassword, *ts.ILOUsername, - *ts.LastUpdated, + ts.LastUpdated, *ts.MgmtIPAddress, *ts.MgmtIPGateway, *ts.MgmtIPNetmask, *ts.OfflineReason, - *ts.PhysLocation, - *ts.PhysLocationID, - fmt.Sprintf("{%s}", strings.Join(ts.ProfileNames, ",")), + ts.PhysicalLocation, + ts.PhysicalLocationID, + fmt.Sprintf("{%s}", strings.Join(ts.Profiles, ",")), *ts.Rack, - *ts.RevalPending, *ts.RevalUpdateTime, *ts.RevalApplyTime, - *ts.Status, - *ts.StatusID, + ts.Status, + ts.StatusID, *ts.TCPPort, ts.Type, - *ts.TypeID, - *ts.UpdPending, + ts.TypeID, *ts.ConfigUpdateTime, *ts.ConfigApplyTime, *ts.XMPPID, @@ -235,7 +230,7 @@ func TestGetServersByCachegroup(t *testing.T) { srv.Interface.Monitor, srv.Interface.MTU, srv.Interface.Name, - *ts.ID, + ts.ID, srv.Interface.RouterHostName, srv.Interface.RouterPortName, ) @@ -245,7 +240,7 @@ func TestGetServersByCachegroup(t *testing.T) { ip.Address, ip.Gateway, ip.ServiceAddress, - *ts.ID, + ts.ID, srv.Interface.Name, ) } @@ -288,8 +283,8 @@ func TestGetMidServers(t *testing.T) { testServers := getTestServers() testServers = testServers[0:2] - testServers[1].Server.Cachegroup = util.StrPtr("parentCacheGroup") - testServers[1].Server.CachegroupID = util.IntPtr(2) + testServers[1].Server.CacheGroup = "parentCacheGroup" + testServers[1].Server.CacheGroupID = 2 testServers[1].Server.Type = "MID" unfilteredCols := []string{"count"} @@ -298,8 +293,8 @@ func TestGetMidServers(t *testing.T) { cols := []string{"cachegroup", "cachegroup_id", "cdn_id", "cdn_name", "domain_name", "guid", "host_name", "https_port", "id", "ilo_ip_address", "ilo_ip_gateway", "ilo_ip_netmask", "ilo_password", "ilo_username", "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", + "phys_location_id", "profile_name", "rack", "revalidate_update_time", "revalidate_apply_time", + "status", "status_id", "tcp_port", "server_type", "server_type_id", "config_update_time", "config_apply_time", "xmpp_id", "xmpp_passwd", "status_last_updated"} interfaceCols := []string{"max_bandwidth", "monitor", "mtu", "name", "server", "router_host_name", "router_port_name"} rows := sqlmock.NewRows(cols) @@ -311,38 +306,36 @@ func TestGetMidServers(t *testing.T) { for _, srv := range testServers { ts := srv.Server rows = rows.AddRow( - *ts.Cachegroup, - *ts.CachegroupID, - *ts.CDNID, - *ts.CDNName, - *ts.DomainName, + ts.CacheGroup, + ts.CacheGroupID, + ts.CDNID, + ts.CDN, + ts.DomainName, *ts.GUID, - *ts.HostName, + ts.HostName, *ts.HTTPSPort, - *ts.ID, + ts.ID, *ts.ILOIPAddress, *ts.ILOIPGateway, *ts.ILOIPNetmask, *ts.ILOPassword, *ts.ILOUsername, - *ts.LastUpdated, + ts.LastUpdated, *ts.MgmtIPAddress, *ts.MgmtIPGateway, *ts.MgmtIPNetmask, *ts.OfflineReason, - *ts.PhysLocation, - *ts.PhysLocationID, - fmt.Sprintf("{%s}", strings.Join(ts.ProfileNames, ",")), + ts.PhysicalLocation, + ts.PhysicalLocationID, + fmt.Sprintf("{%s}", strings.Join(ts.Profiles, ",")), *ts.Rack, - *ts.RevalPending, *ts.RevalUpdateTime, *ts.RevalApplyTime, - *ts.Status, - *ts.StatusID, + ts.Status, + ts.StatusID, *ts.TCPPort, ts.Type, - *ts.TypeID, - *ts.UpdPending, + ts.TypeID, *ts.ConfigUpdateTime, *ts.ConfigApplyTime, *ts.XMPPID, @@ -354,7 +347,7 @@ func TestGetMidServers(t *testing.T) { srv.Interface.Monitor, srv.Interface.MTU, srv.Interface.Name, - *ts.ID, + ts.ID, srv.Interface.RouterHostName, srv.Interface.RouterPortName, ) @@ -364,7 +357,7 @@ func TestGetMidServers(t *testing.T) { ip.Address, ip.Gateway, ip.ServiceAddress, - *ts.ID, + ts.ID, srv.Interface.Name, ) } @@ -389,8 +382,8 @@ func TestGetMidServers(t *testing.T) { cols2 := []string{"cachegroup", "cachegroup_id", "cdn_id", "cdn_name", "domain_name", "guid", "host_name", "https_port", "id", "ilo_ip_address", "ilo_ip_gateway", "ilo_ip_netmask", "ilo_password", "ilo_username", "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", + "phys_location_id", "profile_name", "rack", "revalidate_update_time", "revalidate_apply_time", + "status", "status_id", "tcp_port", "server_type", "server_type_id", "config_update_time", "config_apply_time", "xmpp_id", "xmpp_passwd", "status_last_updated"} rows2 := sqlmock.NewRows(cols2) @@ -437,57 +430,52 @@ func TestGetMidServers(t *testing.T) { } cgs = append(cgs, testCG2) - serverMap := make(map[int]tc.ServerV40, len(servers)) + serverMap := make(map[int]tc.ServerV5, len(servers)) serverIDs := make([]int, 0, len(servers)) for _, server := range servers { - if server.ID == nil { - t.Fatal("Found server with nil ID") - } - serverIDs = append(serverIDs, *server.ID) - serverMap[*server.ID] = server + serverIDs = append(serverIDs, server.ID) + serverMap[server.ID] = server } - var ts tc.ServerV40 + var ts tc.ServerV5 for _, s := range servers { - if s.HostName != nil && *s.HostName == "server2" { + if s.HostName == "server2" { ts = s break } } - *ts.ID = *ts.ID + 1 + ts.ID++ rows2 = rows2.AddRow( - *ts.Cachegroup, - *ts.CachegroupID, - *ts.CDNID, - *ts.CDNName, - *ts.DomainName, + ts.CacheGroup, + ts.CacheGroupID, + ts.CDNID, + ts.CDN, + ts.DomainName, *ts.GUID, - *ts.HostName, + ts.HostName, *ts.HTTPSPort, - *ts.ID, + ts.ID, *ts.ILOIPAddress, *ts.ILOIPGateway, *ts.ILOIPNetmask, *ts.ILOPassword, *ts.ILOUsername, - *ts.LastUpdated, + ts.LastUpdated, *ts.MgmtIPAddress, *ts.MgmtIPGateway, *ts.MgmtIPNetmask, *ts.OfflineReason, - *ts.PhysLocation, - *ts.PhysLocationID, - fmt.Sprintf("{%s}", strings.Join(ts.ProfileNames, ",")), + ts.PhysicalLocation, + ts.PhysicalLocationID, + fmt.Sprintf("{%s}", strings.Join(ts.Profiles, ",")), *ts.Rack, - *ts.RevalPending, *ts.RevalUpdateTime, *ts.RevalApplyTime, - *ts.Status, - *ts.StatusID, + ts.Status, + ts.StatusID, *ts.TCPPort, ts.Type, - *ts.TypeID, - *ts.UpdPending, + ts.TypeID, *ts.ConfigUpdateTime, *ts.ConfigApplyTime, *ts.XMPPID, @@ -509,16 +497,12 @@ func TestGetMidServers(t *testing.T) { t.Errorf("getMidServers expected: Type == MID, actual: %v", serverMap[mid[0]].Type) } - if serverMap[mid[0]].Cachegroup == nil { - t.Error("getMidServers expected: Cachegroup == parentCacheGroup, actual: nil") - } else if *(serverMap[mid[0]].Cachegroup) != "parentCacheGroup" { - t.Errorf("getMidServers expected: Cachegroup == parentCacheGroup, actual: %v", *(serverMap[mid[0]].Cachegroup)) + if actual := serverMap[mid[0]].CacheGroup; actual != "parentCacheGroup" { + t.Errorf("getMidServers expected: Cachegroup == parentCacheGroup, actual: %s", actual) } - if serverMap[mid[0]].CachegroupID == nil { - t.Error("getMidServers expected: CachegroupID == 2, actual: nil") - } else if *(serverMap[mid[0]].CachegroupID) != 2 { - t.Errorf("getMidServers expected: CachegroupID == 2, actual: %v", *(serverMap[mid[0]].CachegroupID)) + if actual := serverMap[mid[0]].CacheGroupID; actual != 2 { + t.Errorf("getMidServers expected: CachegroupID == 2, actual: %d", actual) } } @@ -850,7 +834,7 @@ func TestCreateServerV4(t *testing.T) { db := sqlx.NewDb(mockDB, "sqlmock") defer db.Close() - s4 := getTestServers()[0].Server + s4 := getTestServers()[0].Server.Downgrade() mock.ExpectBegin() rows0 := sqlmock.NewRows([]string{"id"}) @@ -1020,7 +1004,7 @@ func TestUpdateServer(t *testing.T) { db := sqlx.NewDb(mockDB, "sqlmock") defer db.Close() - s4 := getTestServers()[0].Server + s4 := getTestServers()[0].Server.Downgrade() mock.ExpectBegin() rows := sqlmock.NewRows([]string{ @@ -1093,7 +1077,7 @@ func TestUpdateServer(t *testing.T) { WillReturnRows(rows) mock.ExpectCommit() - sid, code, usrErr, sysErr := updateServer(db.MustBegin(), s4) + sid, code, usrErr, sysErr := updateServer(db.MustBegin(), s4.Upgrade()) if usrErr != nil { t.Errorf("unable to update v4 server, user error: %v", usrErr) } From b6dc67e4701ae959a34090cdae3e87957a1c2970 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Mon, 14 Aug 2023 17:11:24 -0600 Subject: [PATCH 13/31] Update PUT handler for APIv5 --- .../traffic_ops_golang/routing/routes.go | 6 +- .../traffic_ops_golang/server/servers.go | 258 ++++++++---------- 2 files changed, 118 insertions(+), 146 deletions(-) diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go b/traffic_ops/traffic_ops_golang/routing/routes.go index 9139f3847e..00e818ed17 100644 --- a/traffic_ops/traffic_ops_golang/routing/routes.go +++ b/traffic_ops/traffic_ops_golang/routing/routes.go @@ -318,7 +318,7 @@ func Routes(d ServerData) ([]Route, http.Handler, error) { //Server: CRUD {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodGet, Path: `servers/?$`, Handler: api.Wrap(server.Read, nil, nil), 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: 472095928531}, - {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPut, Path: `servers/{id}$`, Handler: server.Update, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:UPDATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 45863410331}, + {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPut, Path: `servers/{id}$`, Handler: api.Wrap(server.Update, []string{"id"}, []string{"id"}), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:UPDATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 45863410331}, {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPost, Path: `servers/?$`, Handler: server.Create, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:CREATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 422555806131}, {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodDelete, Path: `servers/{id}$`, Handler: server.Delete, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:DELETE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 49232223331}, @@ -722,7 +722,7 @@ func Routes(d ServerData) ([]Route, http.Handler, error) { //Server: CRUD {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodGet, Path: `servers/?$`, Handler: api.Wrap(server.Read, nil, nil), 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: 47209592853}, - {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodPut, Path: `servers/{id}$`, Handler: server.Update, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:UPDATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 4586341033}, + {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodPut, Path: `servers/{id}$`, Handler: api.Wrap(server.Update, []string{"id"}, []string{"id"}), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:UPDATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 4586341033}, {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodPost, Path: `servers/?$`, Handler: server.Create, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:CREATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 42255580613}, {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodDelete, Path: `servers/{id}$`, Handler: server.Delete, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:DELETE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 4923222333}, @@ -1120,7 +1120,7 @@ func Routes(d ServerData) ([]Route, http.Handler, error) { //Server: CRUD {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodGet, Path: `servers/?$`, Handler: api.Wrap(server.Read, nil, nil), RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 27209592853}, - {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodPut, Path: `servers/{id}$`, Handler: server.Update, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 2586341033}, + {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodPut, Path: `servers/{id}$`, Handler: api.Wrap(server.Update, []string{"id"}, []string{"id"}), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 2586341033}, {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodPost, Path: `servers/?$`, Handler: server.Create, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 22255580613}, {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodDelete, Path: `servers/{id}$`, Handler: server.Delete, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 2923222333}, diff --git a/traffic_ops/traffic_ops_golang/server/servers.go b/traffic_ops/traffic_ops_golang/server/servers.go index 6c3c39bc38..009fed903a 100644 --- a/traffic_ops/traffic_ops_golang/server/servers.go +++ b/traffic_ops/traffic_ops_golang/server/servers.go @@ -1270,37 +1270,19 @@ func deleteInterfaces(id int, tx *sql.Tx) (error, error, int) { } // Update is the handler for PUT requests to /servers. -func Update(w http.ResponseWriter, r *http.Request) { - inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"id"}, []string{"id"}) - tx := inf.Tx.Tx - if userErr != nil || sysErr != nil { - api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) - return - } - defer inf.Close() - - // Middleware should've already handled this, so idk why this is a pointer at all tbh - version := inf.Version - if version == nil { - middleware.NotImplementedHandler().ServeHTTP(w, r) - return - } - +func Update(inf *api.APIInfo) (int, error, error) { id := inf.IntParams["id"] // Get original server - originals, _, userErr, sysErr, errCode, _ := getServers(r.Header, inf.Params, inf.Tx, inf.User, false, *version, inf.Config.RoleBasedPermissions) + originals, _, userErr, sysErr, errCode, _ := getServers(inf.RequestHeaders(), inf.Params, inf.Tx, inf.User, false, *inf.Version, inf.Config.RoleBasedPermissions) if userErr != nil || sysErr != nil { - api.HandleErr(w, r, tx, errCode, userErr, sysErr) - return + return errCode, userErr, sysErr } if len(originals) < 1 { - api.HandleErr(w, r, tx, http.StatusNotFound, errors.New("the server doesn't exist, cannot update"), nil) - return + return http.StatusNotFound, errors.New("the server doesn't exist, cannot update"), nil } if len(originals) > 1 { - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("too many servers by ID %d: %d", id, len(originals))) - return + return http.StatusInternalServerError, nil, fmt.Errorf("too many servers by ID %d: %d", id, len(originals)) } original := originals[0] @@ -1321,12 +1303,12 @@ func Update(w http.ResponseWriter, r *http.Request) { var server tc.ServerV5 var serverV3 tc.ServerV30 var statusLastUpdatedTime time.Time + tx := inf.Tx.Tx - if inf.Version.Major >= 4 { + if inf.Version.Major >= 5 { server.ID = inf.IntParams["id"] - if err := json.NewDecoder(r.Body).Decode(&server); err != nil { - api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil) - return + if err := inf.DecodeBody(&server); err != nil { + return http.StatusBadRequest, err, nil } if server.StatusID != originalStatusID { currentTime := time.Now() @@ -1339,20 +1321,39 @@ func Update(w http.ResponseWriter, r *http.Request) { tmp := server.Downgrade() _, userErr, sysErr := validateV4(&tmp, tx) if userErr != nil || sysErr != nil { - errCode := http.StatusBadRequest if sysErr != nil { - errCode = http.StatusInternalServerError + return http.StatusInternalServerError, userErr, sysErr } - api.HandleErr(w, r, tx, errCode, userErr, sysErr) - return + return http.StatusBadRequest, userErr, sysErr } server = tmp.Upgrade() + } else if inf.Version.Major >= 4 { + var serverV4 tc.ServerV4 + serverV4.ID = util.Ptr(inf.IntParams["id"]) + if err := inf.DecodeBody(&serverV4); err != nil { + return http.StatusBadRequest, err, nil + } + if serverV4.StatusID == nil || *serverV4.StatusID != originalStatusID { + currentTime := time.Now() + server.StatusLastUpdated = ¤tTime + statusLastUpdatedTime = currentTime + } else { + server.StatusLastUpdated = original.StatusLastUpdated + statusLastUpdatedTime = *original.StatusLastUpdated + } + _, userErr, sysErr := validateV4(&serverV4, tx) + if userErr != nil || sysErr != nil { + if sysErr != nil { + return http.StatusInternalServerError, userErr, sysErr + } + return http.StatusBadRequest, userErr, sysErr + } + server = serverV4.Upgrade() } else { serverV3.ID = new(int) *serverV3.ID = inf.IntParams["id"] - if err := json.NewDecoder(r.Body).Decode(&serverV3); err != nil { - api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil) - return + if err := inf.DecodeBody(&serverV3); err != nil { + return http.StatusBadRequest, err, nil } if serverV3.StatusID != nil && *serverV3.StatusID != originalStatusID { currentTime := time.Now() @@ -1364,30 +1365,24 @@ func Update(w http.ResponseWriter, r *http.Request) { } _, userErr, sysErr := validateV3(&serverV3, tx) if userErr != nil || sysErr != nil { - code := http.StatusBadRequest if sysErr != nil { - code = http.StatusInternalServerError + return http.StatusInternalServerError, userErr, sysErr } - api.HandleErr(w, r, tx, code, userErr, sysErr) - return + return http.StatusBadRequest, userErr, sysErr } profileName, exists, err := dbhelpers.GetProfileNameFromID(*serverV3.ProfileID, tx) if err != nil { - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, err) - return + return http.StatusInternalServerError, nil, err } if !exists { - api.HandleErr(w, r, tx, http.StatusNotFound, errors.New("profile does not exist"), nil) - return + return http.StatusNotFound, errors.New("profile does not exist"), nil } profileNames := []string{profileName} upgraded, err := serverV3.UpgradeToV40(profileNames) if err != nil { - sysErr = fmt.Errorf("error upgrading valid V3 server to V4 structure: %w", err) - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, sysErr) - return + return http.StatusInternalServerError, nil, fmt.Errorf("error upgrading valid V3 server to V4 structure: %w", err) } server = upgraded.Upgrade() } @@ -1395,195 +1390,172 @@ func Update(w http.ResponseWriter, r *http.Request) { if original.CacheGroupID != server.CacheGroupID || original.CDNID != server.CDNID { hasDSOnCDN, err := dbhelpers.CachegroupHasTopologyBasedDeliveryServicesOnCDN(tx, original.CacheGroupID, original.CDNID) if err != nil { - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, err) - return + return http.StatusInternalServerError, nil, err } CDNIDs := []int{} if hasDSOnCDN { CDNIDs = append(CDNIDs, original.CDNID) } if err = topology_validation.CheckForEmptyCacheGroups(inf.Tx, []int{original.CacheGroupID}, CDNIDs, true, []int{original.ID}); err != nil { - api.HandleErr(w, r, tx, http.StatusBadRequest, fmt.Errorf("server is the last one in its cachegroup, which is used by a topology, so it cannot be moved to another cachegroup: %w", err), nil) - return + return http.StatusBadRequest, fmt.Errorf("server is the last one in its Cache Group, which is used by a Topology, so it cannot be moved to another Cache Group: %w", err), nil } } status, ok, err := dbhelpers.GetStatusByID(server.StatusID, tx) if err != nil { - sysErr = fmt.Errorf("getting server #%d status (#%d): %v", id, server.StatusID, err) - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, sysErr) - return + return http.StatusInternalServerError, nil, fmt.Errorf("getting server #%d status (#%d): %v", id, server.StatusID, err) } if !ok { log.Warnf("previously existent status #%d not found when fetching later", server.StatusID) - userErr = fmt.Errorf("no such Status: #%d", server.StatusID) - api.HandleErr(w, r, tx, http.StatusBadRequest, userErr, nil) - return + return http.StatusBadRequest, fmt.Errorf("no such Status: #%d", server.StatusID), nil } if status.Name == nil { - sysErr = fmt.Errorf("status #%d had no name", server.StatusID) - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, sysErr) - return + return http.StatusInternalServerError, nil, fmt.Errorf("status #%d had no name", server.StatusID) } if *status.Name != string(tc.CacheStatusOnline) && *status.Name != string(tc.CacheStatusReported) { dsIDs, err := getActiveDeliveryServicesThatOnlyHaveThisServerAssigned(id, original.Type, tx) if err != nil { - sysErr = fmt.Errorf("getting Delivery Services to which server #%d is assigned that have no other servers: %v", id, err) - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, sysErr) - return + return http.StatusInternalServerError, + nil, + fmt.Errorf("getting Delivery Services to which server #%d is assigned that have no other servers: %w", id, err) } if len(dsIDs) > 0 { prefix := fmt.Sprintf("setting server status to '%s' would leave Active Delivery Service", *status.Name) alertText := InvalidStatusForDeliveryServicesAlertText(prefix, original.Type, dsIDs) - api.WriteAlerts(w, r, http.StatusConflict, tc.CreateAlerts(tc.ErrorLevel, alertText)) - return + return http.StatusConflict, errors.New(alertText), nil } } if userErr, sysErr, errCode = checkTypeChangeSafety(server, inf.Tx); userErr != nil || sysErr != nil { - api.HandleErr(w, r, tx, errCode, userErr, sysErr) - return + return errCode, userErr, sysErr } if server.XMPPID != nil && *server.XMPPID != "" && originalXMPPID != "" && *server.XMPPID != originalXMPPID { - api.WriteAlerts(w, r, http.StatusBadRequest, tc.CreateAlerts(tc.ErrorLevel, fmt.Sprintf("server cannot be updated due to requested XMPPID change. XMPIDD is immutable"))) - return + return http.StatusBadRequest, errors.New("server cannot be updated due to requested XMPPID change. XMPIDD is immutable"), nil } - userErr, sysErr, statusCode := api.CheckIfUnModified(r.Header, inf.Tx, server.ID, "server") + userErr, sysErr, statusCode := api.CheckIfUnModified(inf.RequestHeaders(), inf.Tx, server.ID, "server") if userErr != nil || sysErr != nil { - api.HandleErr(w, r, tx, statusCode, userErr, sysErr) - return + return statusCode, userErr, sysErr } if server.CDN != "" { userErr, sysErr, statusCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, server.CDN, inf.User.UserName) if userErr != nil || sysErr != nil { - api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr) - return + return statusCode, userErr, sysErr } } else { userErr, sysErr, statusCode = dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(inf.Tx.Tx, int64(server.CDNID), inf.User.UserName) if userErr != nil || sysErr != nil { - api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr) - return + return statusCode, userErr, sysErr } } if inf.Version.Major >= 4 { if err = dbhelpers.UpdateServerProfilesForV4(server.ID, server.Profiles, tx); err != nil { userErr, sysErr, errCode := api.ParseDBError(err) - api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) - return + return errCode, userErr, sysErr } } else { if err = dbhelpers.UpdateServerProfileTableForV3(serverV3.ID, serverV3.ProfileID, (original.Profiles)[0], tx); err != nil { - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("failed to update server_profile: %w", err)) - return + return http.StatusInternalServerError, nil, fmt.Errorf("failed to update server_profile: %w", err) } } serverID, errCode, userErr, sysErr := updateServer(inf.Tx, server) if userErr != nil || sysErr != nil { - api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) - return + return errCode, userErr, sysErr } if userErr, sysErr, errCode = deleteInterfaces(id, tx); userErr != nil || sysErr != nil { - api.HandleErr(w, r, tx, errCode, userErr, sysErr) - return + return errCode, userErr, sysErr } if userErr, sysErr, errCode = createInterfaces(id, server.Interfaces, tx); userErr != nil || sysErr != nil { - api.HandleErr(w, r, tx, errCode, userErr, sysErr) - return + return errCode, userErr, sysErr } where := `WHERE s.id = $1` var selquery string - if version.Major <= 4 { + if inf.Version.Major <= 4 { selquery = selectQuery + joinProfileV4 + where } else { selquery = selectQuery + where } - var srvr tc.ServerV40 - err = inf.Tx.QueryRow(selquery, serverID).Scan(&srvr.Cachegroup, - &srvr.CachegroupID, - &srvr.CDNID, - &srvr.CDNName, - &srvr.DomainName, - &srvr.GUID, - &srvr.HostName, - &srvr.HTTPSPort, - &srvr.ID, - &srvr.ILOIPAddress, - &srvr.ILOIPGateway, - &srvr.ILOIPNetmask, - &srvr.ILOPassword, - &srvr.ILOUsername, - &srvr.LastUpdated, - &srvr.MgmtIPAddress, - &srvr.MgmtIPGateway, - &srvr.MgmtIPNetmask, - &srvr.OfflineReason, - &srvr.PhysLocation, - &srvr.PhysLocationID, - pq.Array(&srvr.ProfileNames), - &srvr.Rack, - &srvr.RevalPending, - &srvr.RevalUpdateTime, - &srvr.RevalApplyTime, - &srvr.Status, - &srvr.StatusID, - &srvr.TCPPort, - &srvr.Type, - &srvr.TypeID, - &srvr.UpdPending, - &srvr.ConfigUpdateTime, - &srvr.ConfigApplyTime, - &srvr.XMPPID, - &srvr.XMPPPasswd, - &srvr.StatusLastUpdated) + + err = inf.Tx.QueryRow(selquery, serverID).Scan( + &server.CacheGroup, + &server.CacheGroupID, + &server.CDNID, + &server.CDN, + &server.DomainName, + &server.GUID, + &server.HostName, + &server.HTTPSPort, + &server.ID, + &server.ILOIPAddress, + &server.ILOIPGateway, + &server.ILOIPNetmask, + &server.ILOPassword, + &server.ILOUsername, + &server.LastUpdated, + &server.MgmtIPAddress, + &server.MgmtIPGateway, + &server.MgmtIPNetmask, + &server.OfflineReason, + &server.PhysicalLocation, + &server.PhysicalLocationID, + pq.Array(&server.Profiles), + &server.Rack, + &server.RevalUpdateTime, + &server.RevalApplyTime, + &server.Status, + &server.StatusID, + &server.TCPPort, + &server.Type, + &server.TypeID, + &server.ConfigUpdateTime, + &server.ConfigApplyTime, + &server.XMPPID, + &server.XMPPPasswd, + &server.StatusLastUpdated, + ) if err != nil { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err) - return + return http.StatusInternalServerError, nil, err } - serversInterfaces, err := dbhelpers.GetServersInterfaces([]int{*srvr.ID}, inf.Tx.Tx) + serversInterfaces, err := dbhelpers.GetServersInterfaces([]int{server.ID}, inf.Tx.Tx) if err != nil { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err) - return + return http.StatusInternalServerError, nil, err } - if interfacesMap, ok := serversInterfaces[*srvr.ID]; ok { + if interfacesMap, ok := serversInterfaces[server.ID]; ok { for _, intfc := range interfacesMap { - srvr.Interfaces = append(srvr.Interfaces, intfc) + server.Interfaces = append(server.Interfaces, intfc) } } if userErr, sysErr, errCode = updateStatusLastUpdatedTime(id, &statusLastUpdatedTime, tx); userErr != nil || sysErr != nil { - api.HandleErr(w, r, tx, errCode, userErr, sysErr) - return + return errCode, userErr, sysErr } if inf.Version.Major >= 5 { - api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Server updated", srvr) + inf.WriteSuccessResponse(server, "Server updated") } else if inf.Version.Major >= 4 { - api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Server updated", srvr) + inf.WriteSuccessResponse(server.Downgrade(), "Server updated") } else if inf.Version.Major >= 3 { - csp, err := dbhelpers.GetCommonServerPropertiesFromV4(srvr, inf.Tx.Tx) + downgraded := server.Downgrade() + csp, err := dbhelpers.GetCommonServerPropertiesFromV4(downgraded, inf.Tx.Tx) if err != nil { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err) - return + return http.StatusInternalServerError, nil, err } - srvrV30, err := srvr.ToServerV3FromV4(csp) + serverV30, err := downgraded.ToServerV3FromV4(csp) if err != nil { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err) - return + return http.StatusInternalServerError, nil, err } - api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Server updated", srvrV30) + inf.WriteSuccessResponse(serverV30, "Server updated") } - changeLogMsg := fmt.Sprintf("SERVER: %s.%s, ID: %d, ACTION: updated", *srvr.HostName, *srvr.DomainName, *srvr.ID) - api.CreateChangeLogRawTx(api.ApiChange, changeLogMsg, inf.User, tx) + inf.CreateChangeLog(fmt.Sprintf("SERVER: %s.%s, ID: %d, ACTION: updated", server.HostName, server.DomainName, server.ID)) + return http.StatusOK, nil, nil } func updateServer(tx *sqlx.Tx, server tc.ServerV5) (int64, int, error, error) { From 251b6d6827f09cbdd475c004a268503516f5fcf7 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Mon, 14 Aug 2023 19:43:11 -0600 Subject: [PATCH 14/31] Update POST handler for APIv5 --- .../traffic_ops_golang/routing/routes.go | 6 +- .../traffic_ops_golang/server/servers.go | 350 +++++++++++++----- 2 files changed, 252 insertions(+), 104 deletions(-) diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go b/traffic_ops/traffic_ops_golang/routing/routes.go index 00e818ed17..4a637bb9f0 100644 --- a/traffic_ops/traffic_ops_golang/routing/routes.go +++ b/traffic_ops/traffic_ops_golang/routing/routes.go @@ -319,7 +319,7 @@ func Routes(d ServerData) ([]Route, http.Handler, error) { //Server: CRUD {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodGet, Path: `servers/?$`, Handler: api.Wrap(server.Read, nil, nil), 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: 472095928531}, {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPut, Path: `servers/{id}$`, Handler: api.Wrap(server.Update, []string{"id"}, []string{"id"}), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:UPDATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 45863410331}, - {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPost, Path: `servers/?$`, Handler: server.Create, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:CREATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 422555806131}, + {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPost, Path: `servers/?$`, Handler: api.Wrap(server.Create, nil, nil), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:CREATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 422555806131}, {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodDelete, Path: `servers/{id}$`, Handler: server.Delete, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:DELETE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 49232223331}, //Server Capability @@ -723,7 +723,7 @@ func Routes(d ServerData) ([]Route, http.Handler, error) { //Server: CRUD {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodGet, Path: `servers/?$`, Handler: api.Wrap(server.Read, nil, nil), 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: 47209592853}, {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodPut, Path: `servers/{id}$`, Handler: api.Wrap(server.Update, []string{"id"}, []string{"id"}), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:UPDATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 4586341033}, - {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodPost, Path: `servers/?$`, Handler: server.Create, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:CREATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 42255580613}, + {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodPost, Path: `servers/?$`, Handler: api.Wrap(server.Create, nil, nil), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:CREATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 42255580613}, {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodDelete, Path: `servers/{id}$`, Handler: server.Delete, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:DELETE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 4923222333}, //Server Capability @@ -1121,7 +1121,7 @@ func Routes(d ServerData) ([]Route, http.Handler, error) { //Server: CRUD {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodGet, Path: `servers/?$`, Handler: api.Wrap(server.Read, nil, nil), RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 27209592853}, {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodPut, Path: `servers/{id}$`, Handler: api.Wrap(server.Update, []string{"id"}, []string{"id"}), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 2586341033}, - {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodPost, Path: `servers/?$`, Handler: server.Create, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 22255580613}, + {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodPost, Path: `servers/?$`, Handler: api.Wrap(server.Create, nil, nil), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 22255580613}, {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodDelete, Path: `servers/{id}$`, Handler: server.Delete, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 2923222333}, //Server Capability diff --git a/traffic_ops/traffic_ops_golang/server/servers.go b/traffic_ops/traffic_ops_golang/server/servers.go index 009fed903a..55a2096b36 100644 --- a/traffic_ops/traffic_ops_golang/server/servers.go +++ b/traffic_ops/traffic_ops_golang/server/servers.go @@ -23,7 +23,6 @@ package server import ( "database/sql" - "encoding/json" "errors" "fmt" "net" @@ -1636,24 +1635,21 @@ func insertServerProfile(id int, pName []string, tx *sql.Tx) (error, error, int) return nil, nil, http.StatusOK } -func createV3(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) { +func createV3(inf *api.APIInfo) (int, error, error) { var server tc.ServerV30 - if err := json.NewDecoder(r.Body).Decode(&server); err != nil { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, err, nil) - return + if err := inf.DecodeBody(&server); err != nil { + return http.StatusBadRequest, err, nil } if server.ID != nil { var prevID int err := inf.Tx.Tx.QueryRow("SELECT id from server where id = $1", server.ID).Scan(&prevID) - if err != nil && err != sql.ErrNoRows { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, fmt.Errorf("checking if server with id %d exists", *server.ID)) - return + if err != nil && !errors.Is(err, sql.ErrNoRows) { + return http.StatusInternalServerError, nil, fmt.Errorf("checking if server with id %d exists", *server.ID) } if prevID != 0 { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, fmt.Errorf("server with id %d already exists. Please do not provide an id", *server.ID), nil) - return + return http.StatusBadRequest, fmt.Errorf("server with id %d already exists. Please do not provide an id", *server.ID), nil } } @@ -1661,12 +1657,10 @@ func createV3(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) { _, userErr, sysErr := validateV3(&server, inf.Tx.Tx) if userErr != nil || sysErr != nil { - code := http.StatusBadRequest if sysErr != nil { - code = http.StatusInternalServerError + return http.StatusInternalServerError, userErr, sysErr } - api.HandleErr(w, r, inf.Tx.Tx, code, userErr, sysErr) - return + return http.StatusBadRequest, userErr, sysErr } currentTime := time.Now() @@ -1675,56 +1669,50 @@ func createV3(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) { if server.CDNName != nil { userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, *server.CDNName, inf.User.UserName) if userErr != nil || sysErr != nil { - api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr) - return + return statusCode, userErr, sysErr } } else if server.CDNID != nil { userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(inf.Tx.Tx, int64(*server.CDNID), inf.User.UserName) if userErr != nil || sysErr != nil { - api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr) - return + return statusCode, userErr, sysErr } } serverID, err := createServerV3(inf.Tx, server) if err != nil { userErr, sysErr, errCode := api.ParseDBError(err) - api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) - return + return errCode, userErr, sysErr } interfaces, err := tc.ToInterfacesV4(server.Interfaces, server.RouterHostName, server.RouterPortName) if err != nil { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err) - return + return http.StatusInternalServerError, nil, err } userErr, sysErr, errCode := createInterfaces(int(serverID), interfaces, inf.Tx.Tx) if userErr != nil || sysErr != nil { - api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) - return + return errCode, userErr, sysErr } var origProfile string err = inf.Tx.Tx.QueryRow("SELECT name from profile where id = $1", server.ProfileID).Scan(&origProfile) if err != nil && err != sql.ErrNoRows { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, fmt.Errorf("retreiving profile with id %d", *server.ProfileID)) - return + return http.StatusInternalServerError, nil, fmt.Errorf("retreiving profile with id %d", *server.ProfileID) } - var origProfiles = []string{origProfile} + var origProfiles = []string{origProfile} userErr, sysErr, statusCode := insertServerProfile(int(serverID), origProfiles, inf.Tx.Tx) if userErr != nil || sysErr != nil { - api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr) - return + return statusCode, userErr, sysErr } where := `WHERE s.id = $1` selquery := selectQuery + where - var s4 tc.ServerV40 - err = inf.Tx.QueryRow(selquery, serverID).Scan(&s4.Cachegroup, - &s4.CachegroupID, + var s4 tc.ServerV5 + err = inf.Tx.QueryRow(selquery, serverID).Scan( + &s4.CacheGroup, + &s4.CacheGroupID, &s4.CDNID, - &s4.CDNName, + &s4.CDN, &s4.DomainName, &s4.GUID, &s4.HostName, @@ -1740,11 +1728,10 @@ func createV3(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) { &s4.MgmtIPGateway, &s4.MgmtIPNetmask, &s4.OfflineReason, - &s4.PhysLocation, - &s4.PhysLocationID, - pq.Array(&s4.ProfileNames), + &s4.PhysicalLocation, + &s4.PhysicalLocationID, + pq.Array(&s4.Profiles), &s4.Rack, - &s4.RevalPending, &s4.RevalUpdateTime, &s4.RevalApplyTime, &s4.Status, @@ -1752,54 +1739,146 @@ func createV3(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) { &s4.TCPPort, &s4.Type, &s4.TypeID, - &s4.UpdPending, &s4.ConfigUpdateTime, &s4.ConfigApplyTime, &s4.XMPPID, &s4.XMPPPasswd, - &s4.StatusLastUpdated) + &s4.StatusLastUpdated, + ) if err != nil { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err) - return + return http.StatusInternalServerError, nil, err } s4.Interfaces = interfaces - csp, err := dbhelpers.GetCommonServerPropertiesFromV4(s4, inf.Tx.Tx) + downgraded := s4.Downgrade() + csp, err := dbhelpers.GetCommonServerPropertiesFromV4(downgraded, inf.Tx.Tx) if err != nil { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err) + return http.StatusInternalServerError, nil, err } - srvr, err := s4.ToServerV3FromV4(csp) + server, err = downgraded.ToServerV3FromV4(csp) if err != nil { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err) - return + return http.StatusInternalServerError, nil, err + } + + inf.WriteCreatedResponse(server, "Server created", fmt.Sprintf("servers?id=%d", server.ID)) + inf.CreateChangeLog(fmt.Sprintf("SERVER: %s.%s, ID: %d, ACTION: created", *server.HostName, *server.DomainName, *server.ID)) + return http.StatusCreated, nil, nil +} + +func createV5(inf *api.APIInfo) (int, error, error) { + var server tc.ServerV5 + + if err := inf.DecodeBody(&server); err != nil { + return http.StatusBadRequest, err, nil + } + + server.XMPPID = newUUID() + + tmp := server.Downgrade() + _, userErr, sysErr := validateV4(&tmp, inf.Tx.Tx) + if userErr != nil || sysErr != nil { + if sysErr != nil { + return http.StatusInternalServerError, userErr, sysErr + } + return http.StatusBadRequest, userErr, sysErr } - alerts := tc.CreateAlerts(tc.SuccessLevel, "Server created") - api.WriteAlertsObj(w, r, http.StatusCreated, alerts, srvr) + currentTime := time.Now() + server.StatusLastUpdated = ¤tTime - 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) + if server.CDN != "" { + userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, server.CDN, inf.User.UserName) + if userErr != nil || sysErr != nil { + return statusCode, userErr, sysErr + } + } else { + userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(inf.Tx.Tx, int64(server.CDNID), inf.User.UserName) + if userErr != nil || sysErr != nil { + return statusCode, userErr, sysErr + } + } + + origProfiles := server.Profiles + serverID, err := createServerV5(inf.Tx, server) + if err != nil { + userErr, sysErr, errCode := api.ParseDBError(err) + return errCode, userErr, sysErr + } + + userErr, sysErr, errCode := createInterfaces(int(serverID), server.Interfaces, inf.Tx.Tx) + if userErr != nil || sysErr != nil { + return errCode, userErr, sysErr + } + + userErr, sysErr, statusCode := insertServerProfile(int(serverID), origProfiles, inf.Tx.Tx) + if userErr != nil || sysErr != nil { + return statusCode, userErr, sysErr + } + + where := `WHERE s.id = $1` + selquery := selectQuery + joinProfileV4 + where + err = inf.Tx.QueryRow(selquery, serverID).Scan( + &server.CacheGroup, + &server.CacheGroupID, + &server.CDNID, + &server.CDN, + &server.DomainName, + &server.GUID, + &server.HostName, + &server.HTTPSPort, + &server.ID, + &server.ILOIPAddress, + &server.ILOIPGateway, + &server.ILOIPNetmask, + &server.ILOPassword, + &server.ILOUsername, + &server.LastUpdated, + &server.MgmtIPAddress, + &server.MgmtIPGateway, + &server.MgmtIPNetmask, + &server.OfflineReason, + &server.PhysicalLocation, + &server.PhysicalLocationID, + pq.Array(&server.Profiles), + &server.Rack, + &server.RevalUpdateTime, + &server.RevalApplyTime, + &server.Status, + &server.StatusID, + &server.TCPPort, + &server.Type, + &server.TypeID, + &server.ConfigUpdateTime, + &server.ConfigApplyTime, + &server.XMPPID, + &server.XMPPPasswd, + &server.StatusLastUpdated, + ) + if err != nil { + return http.StatusInternalServerError, nil, err + } + + code, userErr, sysErr := inf.WriteCreatedResponse(server, "Server created", fmt.Sprintf("servers?id=%d", server.ID)) + inf.CreateChangeLog(fmt.Sprintf("SERVER: %s.%s, ID: %d, ACTION: created", server.HostName, server.DomainName, server.ID)) + return code, userErr, sysErr } -func createV4(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) { +func createV4(inf *api.APIInfo) (int, error, error) { var server tc.ServerV40 - if err := json.NewDecoder(r.Body).Decode(&server); err != nil { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, err, nil) - return + if err := inf.DecodeBody(&server); err != nil { + return http.StatusBadRequest, err, nil } if server.ID != nil { var prevID int err := inf.Tx.Tx.QueryRow("SELECT id from server where id = $1", server.ID).Scan(&prevID) - if err != nil && err != sql.ErrNoRows { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, fmt.Errorf("checking if server with id %d exists", *server.ID)) - return + if err != nil && !errors.Is(err, sql.ErrNoRows) { + return http.StatusInternalServerError, nil, fmt.Errorf("checking if server with id %d exists", *server.ID) } if prevID != 0 { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, fmt.Errorf("server with id %d already exists. Please do not provide an id", *server.ID), nil) - return + return http.StatusBadRequest, fmt.Errorf("server with id %d already exists. Please do not provide an id", *server.ID), nil } } @@ -1807,12 +1886,10 @@ func createV4(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) { _, userErr, sysErr := validateV4(&server, inf.Tx.Tx) if userErr != nil || sysErr != nil { - errCode := http.StatusBadRequest if sysErr != nil { - errCode = http.StatusInternalServerError + return http.StatusInternalServerError, userErr, sysErr } - api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) - return + return http.StatusBadRequest, userErr, sysErr } currentTime := time.Now() @@ -1821,14 +1898,12 @@ func createV4(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) { if server.CDNName != nil { userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, *server.CDNName, inf.User.UserName) if userErr != nil || sysErr != nil { - api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr) - return + return statusCode, userErr, sysErr } } else if server.CDNID != nil { userErr, sysErr, statusCode := dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(inf.Tx.Tx, int64(*server.CDNID), inf.User.UserName) if userErr != nil || sysErr != nil { - api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr) - return + return statusCode, userErr, sysErr } } @@ -1836,29 +1911,27 @@ func createV4(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) { serverID, err := createServerV4(inf.Tx, server) if err != nil { userErr, sysErr, errCode := api.ParseDBError(err) - api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) - return + return errCode, userErr, sysErr } userErr, sysErr, errCode := createInterfaces(int(serverID), server.Interfaces, inf.Tx.Tx) if userErr != nil || sysErr != nil { - api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) - return + return errCode, userErr, sysErr } userErr, sysErr, statusCode := insertServerProfile(int(serverID), origProfiles, inf.Tx.Tx) if userErr != nil || sysErr != nil { - api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr) - return + return statusCode, userErr, sysErr } where := `WHERE s.id = $1` selquery := selectQuery + joinProfileV4 + where - var srvr tc.ServerV40 - err = inf.Tx.QueryRow(selquery, serverID).Scan(&srvr.Cachegroup, - &srvr.CachegroupID, + var srvr tc.ServerV5 + err = inf.Tx.QueryRow(selquery, serverID).Scan( + &srvr.CacheGroup, + &srvr.CacheGroupID, &srvr.CDNID, - &srvr.CDNName, + &srvr.CDN, &srvr.DomainName, &srvr.GUID, &srvr.HostName, @@ -1874,11 +1947,10 @@ func createV4(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) { &srvr.MgmtIPGateway, &srvr.MgmtIPNetmask, &srvr.OfflineReason, - &srvr.PhysLocation, - &srvr.PhysLocationID, - pq.Array(&srvr.ProfileNames), + &srvr.PhysicalLocation, + &srvr.PhysicalLocationID, + pq.Array(&srvr.Profiles), &srvr.Rack, - &srvr.RevalPending, &srvr.RevalUpdateTime, &srvr.RevalApplyTime, &srvr.Status, @@ -1886,25 +1958,106 @@ func createV4(inf *api.APIInfo, w http.ResponseWriter, r *http.Request) { &srvr.TCPPort, &srvr.Type, &srvr.TypeID, - &srvr.UpdPending, &srvr.ConfigUpdateTime, &srvr.ConfigApplyTime, &srvr.XMPPID, &srvr.XMPPPasswd, - &srvr.StatusLastUpdated) + &srvr.StatusLastUpdated, + ) if err != nil { - api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, err) - return + return http.StatusInternalServerError, nil, err } // TODO: Use returned values from SQL insert to ensure inserted values match srvr.Interfaces = server.Interfaces - alerts := tc.CreateAlerts(tc.SuccessLevel, "Server created") - api.WriteAlertsObj(w, r, http.StatusCreated, alerts, srvr) + code, userErr, sysErr := inf.WriteCreatedResponse(srvr.Downgrade(), "Server created", fmt.Sprintf("servers?id=%d", srvr.ID)) + inf.CreateChangeLog(fmt.Sprintf("SERVER: %s.%s, ID: %d, ACTION: created", srvr.HostName, srvr.DomainName, srvr.ID)) + return code, userErr, sysErr +} + +func createServerV5(tx *sqlx.Tx, server tc.ServerV5) (int64, error) { + var profileID int + err := tx.QueryRow("SELECT id FROM profile p WHERE name=$1", (server.Profiles)[0]).Scan(&profileID) + if err != nil { + return 0, fmt.Errorf("unable to get profileID for a profile name: %w", err) + } + + rows, err := tx.Query(insertQueryV4, + server.CacheGroupID, + server.CDNID, + server.DomainName, + server.HostName, + server.HTTPSPort, + server.ILOIPAddress, + server.ILOIPNetmask, + server.ILOIPGateway, + server.ILOUsername, + server.ILOPassword, + server.MgmtIPAddress, + server.MgmtIPNetmask, + server.MgmtIPGateway, + server.OfflineReason, + server.PhysicalLocationID, + profileID, + server.Rack, + server.StatusID, + server.TCPPort, + server.TypeID, + server.XMPPID, + server.XMPPPasswd, + ) + if err != nil { + return 0, err + } + defer log.Close(rows, "failed to close rows for createServerV4") + + rowsAffected := 0 + var serverID int64 + for rows.Next() { + rowsAffected++ + err := rows.Scan( + &server.CacheGroup, + &server.CacheGroupID, + &server.CDNID, + &server.CDN, + &server.DomainName, + &server.GUID, + &server.HostName, + &server.HTTPSPort, + &serverID, + &server.ILOIPAddress, + &server.ILOIPGateway, + &server.ILOIPNetmask, + &server.ILOPassword, + &server.ILOUsername, + &server.LastUpdated, + &server.MgmtIPAddress, + &server.MgmtIPGateway, + &server.MgmtIPNetmask, + &server.OfflineReason, + &server.PhysicalLocation, + &server.PhysicalLocationID, + pq.Array(&server.Profiles), + &server.Rack, + &server.Status, + &server.StatusID, + &server.TCPPort, + &server.Type, + &server.TypeID, + ) + if err != nil { + return 0, err + } + } + + if rowsAffected == 0 { + return 0, errors.New("server create: no server was inserted, no id was returned") + } else if rowsAffected > 1 { + return 0, fmt.Errorf("too many ids returned from server insert: %d", rowsAffected) + } - 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) + return serverID, nil } func createServerV4(tx *sqlx.Tx, server tc.ServerV40) (int64, error) { @@ -1997,19 +2150,14 @@ func createServerV3(tx *sqlx.Tx, server tc.ServerV30) (int64, error) { } // Create is the handler for POST requests to /servers. -func Create(w http.ResponseWriter, r *http.Request) { - inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil) - if userErr != nil || sysErr != nil { - api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) - return - } - defer inf.Close() - - switch { - case inf.Version.Major == 3: - createV3(inf, w, r) +func Create(inf *api.APIInfo) (int, error, error) { + switch inf.Version.Major { + case 3: + return createV3(inf) + case 4: + return createV4(inf) default: - createV4(inf, w, r) + return createV5(inf) } } From 9d9fb949dea7cb58c3f970ed82ad15060e7eb338 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Mon, 14 Aug 2023 19:54:02 -0600 Subject: [PATCH 15/31] update DELETE handler for APIv5 --- .../traffic_ops_golang/routing/routes.go | 6 +- .../traffic_ops_golang/server/servers.go | 112 ++++++------------ 2 files changed, 42 insertions(+), 76 deletions(-) diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go b/traffic_ops/traffic_ops_golang/routing/routes.go index 4a637bb9f0..8ef97f5b3e 100644 --- a/traffic_ops/traffic_ops_golang/routing/routes.go +++ b/traffic_ops/traffic_ops_golang/routing/routes.go @@ -320,7 +320,7 @@ func Routes(d ServerData) ([]Route, http.Handler, error) { {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodGet, Path: `servers/?$`, Handler: api.Wrap(server.Read, nil, nil), 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: 472095928531}, {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPut, Path: `servers/{id}$`, Handler: api.Wrap(server.Update, []string{"id"}, []string{"id"}), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:UPDATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 45863410331}, {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodPost, Path: `servers/?$`, Handler: api.Wrap(server.Create, nil, nil), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:CREATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 422555806131}, - {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodDelete, Path: `servers/{id}$`, Handler: server.Delete, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:DELETE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 49232223331}, + {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodDelete, Path: `servers/{id}$`, Handler: api.Wrap(server.Delete, []string{"id"}, []string{"id"}), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:DELETE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 49232223331}, //Server Capability {Version: api.Version{Major: 5, Minor: 0}, Method: http.MethodGet, Path: `server_capabilities$`, Handler: servercapability.GetServerCapabilityV5, RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: []string{"SERVER-CAPABILITY:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 41040739131}, @@ -724,7 +724,7 @@ func Routes(d ServerData) ([]Route, http.Handler, error) { {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodGet, Path: `servers/?$`, Handler: api.Wrap(server.Read, nil, nil), 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: 47209592853}, {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodPut, Path: `servers/{id}$`, Handler: api.Wrap(server.Update, []string{"id"}, []string{"id"}), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:UPDATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 4586341033}, {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodPost, Path: `servers/?$`, Handler: api.Wrap(server.Create, nil, nil), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:CREATE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 42255580613}, - {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodDelete, Path: `servers/{id}$`, Handler: server.Delete, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:DELETE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 4923222333}, + {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodDelete, Path: `servers/{id}$`, Handler: api.Wrap(server.Delete, []string{"id"}, []string{"id"}), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:DELETE", "SERVER:READ", "DELIVERY-SERVICE:READ", "CDN:READ", "PHYSICAL-LOCATION:READ", "CACHE-GROUP:READ", "TYPE:READ", "PROFILE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 4923222333}, //Server Capability {Version: api.Version{Major: 4, Minor: 0}, Method: http.MethodGet, Path: `server_capabilities$`, Handler: api.ReadHandler(&servercapability.TOServerCapability{}), RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: []string{"SERVER-CAPABILITY:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 4104073913}, @@ -1122,7 +1122,7 @@ func Routes(d ServerData) ([]Route, http.Handler, error) { {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodGet, Path: `servers/?$`, Handler: api.Wrap(server.Read, nil, nil), RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 27209592853}, {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodPut, Path: `servers/{id}$`, Handler: api.Wrap(server.Update, []string{"id"}, []string{"id"}), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 2586341033}, {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodPost, Path: `servers/?$`, Handler: api.Wrap(server.Create, nil, nil), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 22255580613}, - {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodDelete, Path: `servers/{id}$`, Handler: server.Delete, RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 2923222333}, + {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodDelete, Path: `servers/{id}$`, Handler: api.Wrap(server.Delete, []string{"id"}, []string{"id"}), RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 2923222333}, //Server Capability {Version: api.Version{Major: 3, Minor: 0}, Method: http.MethodGet, Path: `server_capabilities$`, Handler: api.ReadHandler(&servercapability.TOServerCapability{}), RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions: nil, Authenticated: Authenticated, Middlewares: nil, ID: 2104073913}, diff --git a/traffic_ops/traffic_ops_golang/server/servers.go b/traffic_ops/traffic_ops_golang/server/servers.go index 55a2096b36..409a9c699f 100644 --- a/traffic_ops/traffic_ops_golang/server/servers.go +++ b/traffic_ops/traffic_ops_golang/server/servers.go @@ -39,7 +39,6 @@ import ( "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/auth" "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers" "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/deliveryservice" - "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/routing/middleware" "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant" "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/topology/topology_validation" "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/util/ims" @@ -2203,7 +2202,7 @@ func getActiveDeliveryServicesThatOnlyHaveThisServerAssigned(id int, serverType var topology *string err = rows.Scan(&dsID, &mso, &topology) if err != nil { - return ids, fmt.Errorf("scanning: %v", err) + return ids, fmt.Errorf("scanning: %w", err) } if (isEdge && topology == nil) || (isOrigin && mso) { ids = append(ids, dsID) @@ -2214,123 +2213,90 @@ func getActiveDeliveryServicesThatOnlyHaveThisServerAssigned(id int, serverType } // Delete is the handler for DELETE requests to the /servers API endpoint. -func Delete(w http.ResponseWriter, r *http.Request) { - inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"id"}, []string{"id"}) - tx := inf.Tx.Tx - if userErr != nil || sysErr != nil { - api.HandleErr(w, r, tx, errCode, userErr, sysErr) - return - } - defer inf.Close() - - // Middleware should've already handled this, so idk why this is a pointer at all tbh - version := inf.Version - if version == nil { - middleware.NotImplementedHandler().ServeHTTP(w, r) - return - } - +func Delete(inf *api.APIInfo) (int, error, error) { id := inf.IntParams["id"] + tx := inf.Tx.Tx serverInfo, exists, err := dbhelpers.GetServerInfo(id, tx) if err != nil { - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, err) - return + return http.StatusInternalServerError, nil, err } if !exists { - api.HandleErr(w, r, tx, http.StatusNotFound, fmt.Errorf("no server exists by id #%d", id), nil) - return + return http.StatusNotFound, fmt.Errorf("no server exists by id #%d", id), nil } if dsIDs, err := getActiveDeliveryServicesThatOnlyHaveThisServerAssigned(id, serverInfo.Type, tx); err != nil { - sysErr = fmt.Errorf("checking if server #%d is the last server assigned to any Delivery Services: %v", id, err) - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, sysErr) - return + return http.StatusInternalServerError, nil, fmt.Errorf("checking if server #%d is the last server assigned to any Delivery Services: %w", id, err) } else if len(dsIDs) > 0 { - alertText := fmt.Sprintf("deleting server #%d would leave Active Delivery Service", id) - alertText = InvalidStatusForDeliveryServicesAlertText(alertText, serverInfo.Type, dsIDs) - - api.WriteAlerts(w, r, http.StatusConflict, tc.CreateAlerts(tc.ErrorLevel, alertText)) - return + return http.StatusConflict, fmt.Errorf("deleting server #%d would leave Active Delivery Service", id), nil } - servers, _, userErr, sysErr, errCode, _ := getServers(r.Header, map[string]string{"id": inf.Params["id"]}, inf.Tx, inf.User, false, *version, inf.Config.RoleBasedPermissions) + servers, _, userErr, sysErr, errCode, _ := getServers(inf.RequestHeaders(), map[string]string{"id": inf.Params["id"]}, inf.Tx, inf.User, false, *inf.Version, inf.Config.RoleBasedPermissions) if userErr != nil || sysErr != nil { - api.HandleErr(w, r, tx, errCode, userErr, sysErr) - return + return errCode, userErr, sysErr } if len(servers) < 1 { - api.HandleErr(w, r, tx, http.StatusNotFound, fmt.Errorf("no server exists by id #%d", id), nil) - return + return http.StatusNotFound, fmt.Errorf("no server exists by id #%d", id), nil } if len(servers) > 1 { - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("there are somehow two servers with id %d - cannot delete", id)) - return + return http.StatusInternalServerError, nil, fmt.Errorf("there are somehow two servers with id %d - cannot delete", id) } server := servers[0] if server.CDN != "" { - userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, server.CDN, inf.User.UserName) + userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDN(tx, server.CDN, inf.User.UserName) if userErr != nil || sysErr != nil { - api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) - return + return errCode, userErr, sysErr } } else { // when would this happen? - userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(inf.Tx.Tx, int64(server.CDNID), inf.User.UserName) + userErr, sysErr, errCode = dbhelpers.CheckIfCurrentUserCanModifyCDNWithID(tx, int64(server.CDNID), inf.User.UserName) if userErr != nil || sysErr != nil { - api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr) - return + return errCode, userErr, sysErr } } cacheGroupIds := []int{server.CacheGroupID} serverIds := []int{server.ID} - hasDSOnCDN, err := dbhelpers.CachegroupHasTopologyBasedDeliveryServicesOnCDN(inf.Tx.Tx, server.CacheGroupID, server.CDNID) + hasDSOnCDN, err := dbhelpers.CachegroupHasTopologyBasedDeliveryServicesOnCDN(tx, server.CacheGroupID, server.CDNID) if err != nil { - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, err) - return + return http.StatusInternalServerError, nil, err } CDNIDs := []int{} if hasDSOnCDN { CDNIDs = append(CDNIDs, server.CDNID) } if err := topology_validation.CheckForEmptyCacheGroups(inf.Tx, cacheGroupIds, CDNIDs, true, serverIds); err != nil { - api.HandleErr(w, r, tx, http.StatusBadRequest, fmt.Errorf("server is the last one in its cachegroup, which is used by a topology: %w", err), nil) - return + return http.StatusBadRequest, fmt.Errorf("server is the last one in its cachegroup, which is used by a topology: %w", err), nil } if result, err := tx.Exec(deleteServerQuery, id); err != nil { log.Errorf("Raw error: %v", err) userErr, sysErr, errCode = api.ParseDBError(err) - api.HandleErr(w, r, tx, errCode, userErr, sysErr) - return + return errCode, userErr, sysErr } else if rowsAffected, err := result.RowsAffected(); err != nil { - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("getting rows affected by server delete: %w", err)) - return + return http.StatusInternalServerError, nil, fmt.Errorf("getting rows affected by server delete: %w", err) } else if rowsAffected != 1 { - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, fmt.Errorf("incorrect number of rows affected: %d", rowsAffected)) - return + return http.StatusInternalServerError, nil, fmt.Errorf("incorrect number of rows affected: %d", rowsAffected) } + inf.CreateChangeLog(fmt.Sprintf("SERVER: %s.%s, ID: %d, ACTION: deleted", server.HostName, server.DomainName, server.ID)) if inf.Version.Major >= 5 { - api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Server deleted", server) - } else if inf.Version.Major >= 4 { - api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Server deleted", server.Downgrade()) - } else { - csp, err := dbhelpers.GetCommonServerPropertiesFromV4(server.Downgrade(), tx) - if err != nil { - userErr, sysErr, errCode := api.ParseDBError(err) - api.HandleErr(w, r, tx, errCode, userErr, sysErr) - return - } - downgraded := server.Downgrade() - serverv3, err := downgraded.ToServerV3FromV4(csp) - if err != nil { - api.HandleErr(w, r, tx, http.StatusInternalServerError, nil, err) - return - } - api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Server deleted", serverv3) + return inf.WriteSuccessResponse(server, "Server deleted") + } + + downgraded := server.Downgrade() + if inf.Version.Major >= 4 { + return inf.WriteSuccessResponse(downgraded, "Server deleted") } - changeLogMsg := fmt.Sprintf("SERVER: %s.%s, ID: %d, ACTION: deleted", server.HostName, server.DomainName, server.ID) - api.CreateChangeLogRawTx(api.ApiChange, changeLogMsg, inf.User, tx) + csp, err := dbhelpers.GetCommonServerPropertiesFromV4(downgraded, tx) + if err != nil { + userErr, sysErr, errCode := api.ParseDBError(err) + return errCode, userErr, sysErr + } + + serverv3, err := downgraded.ToServerV3FromV4(csp) + if err != nil { + return http.StatusInternalServerError, nil, err + } + return inf.WriteSuccessResponse(serverv3, "Server deleted") } From 7de4b727b14cbd7b11629c62494769dc6466ef9f Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Mon, 14 Aug 2023 19:58:04 -0600 Subject: [PATCH 16/31] fix broken unit test --- .../traffic_ops_golang/server/servers_test.go | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/traffic_ops/traffic_ops_golang/server/servers_test.go b/traffic_ops/traffic_ops_golang/server/servers_test.go index 3a5dcd574a..3c8155dff2 100644 --- a/traffic_ops/traffic_ops_golang/server/servers_test.go +++ b/traffic_ops/traffic_ops_golang/server/servers_test.go @@ -1004,7 +1004,7 @@ func TestUpdateServer(t *testing.T) { db := sqlx.NewDb(mockDB, "sqlmock") defer db.Close() - s4 := getTestServers()[0].Server.Downgrade() + server := getTestServers()[0].Server mock.ExpectBegin() rows := sqlmock.NewRows([]string{ @@ -1039,55 +1039,55 @@ func TestUpdateServer(t *testing.T) { "status_last_updated", }) rows.AddRow( - *s4.Cachegroup, - *s4.CachegroupID, - *s4.CDNID, - *s4.CDNName, - *s4.DomainName, - *s4.GUID, - *s4.HostName, - *s4.HTTPSPort, - *s4.ID, - *s4.ILOIPAddress, - *s4.ILOIPGateway, - *s4.ILOIPNetmask, - *s4.ILOPassword, - *s4.ILOUsername, - *s4.LastUpdated, - *s4.MgmtIPAddress, - *s4.MgmtIPGateway, - *s4.MgmtIPNetmask, - *s4.OfflineReason, - *s4.PhysLocation, - *s4.PhysLocationID, - fmt.Sprintf("{%s}", strings.Join(s4.ProfileNames, ",")), - *s4.Rack, - *s4.Status, - *s4.StatusID, - *s4.TCPPort, - s4.Type, - *s4.TypeID, - *s4.StatusLastUpdated, + server.CacheGroup, + server.CacheGroupID, + server.CDNID, + server.CDN, + server.DomainName, + *server.GUID, + server.HostName, + *server.HTTPSPort, + server.ID, + *server.ILOIPAddress, + *server.ILOIPGateway, + *server.ILOIPNetmask, + *server.ILOPassword, + *server.ILOUsername, + server.LastUpdated, + *server.MgmtIPAddress, + *server.MgmtIPGateway, + *server.MgmtIPNetmask, + *server.OfflineReason, + server.PhysicalLocation, + server.PhysicalLocationID, + fmt.Sprintf("{%s}", strings.Join(server.Profiles, ",")), + *server.Rack, + server.Status, + server.StatusID, + *server.TCPPort, + server.Type, + server.TypeID, + *server.StatusLastUpdated, ) mock.ExpectQuery("UPDATE server SET"). - WithArgs(*s4.CachegroupID, *s4.CDNID, *s4.DomainName, *s4.HostName, *s4.HTTPSPort, *s4.ILOIPAddress, - *s4.ILOIPNetmask, *s4.ILOIPGateway, *s4.ILOUsername, *s4.ILOPassword, *s4.MgmtIPAddress, - *s4.MgmtIPNetmask, *s4.MgmtIPGateway, *s4.OfflineReason, *s4.PhysLocationID, 1, *s4.Rack, - *s4.StatusID, *s4.TCPPort, *s4.TypeID, *s4.XMPPPasswd, *s4.StatusLastUpdated, *s4.ID). + WithArgs(server.CacheGroupID, server.CDNID, server.DomainName, server.HostName, *server.HTTPSPort, *server.ILOIPAddress, + *server.ILOIPNetmask, *server.ILOIPGateway, *server.ILOUsername, *server.ILOPassword, *server.MgmtIPAddress, + *server.MgmtIPNetmask, *server.MgmtIPGateway, *server.OfflineReason, server.PhysicalLocationID, 1, *server.Rack, + server.StatusID, *server.TCPPort, server.TypeID, *server.XMPPPasswd, *server.StatusLastUpdated, server.ID). WillReturnRows(rows) mock.ExpectCommit() - sid, code, usrErr, sysErr := updateServer(db.MustBegin(), s4.Upgrade()) + sid, code, usrErr, sysErr := updateServer(db.MustBegin(), server) if usrErr != nil { t.Errorf("unable to update v4 server, user error: %v", usrErr) } if sysErr != nil { t.Errorf("unable to update v4 server, system error: %v", sysErr) } - if sid != int64(*s4.ID) { - t.Errorf("updated incorrect server, expected:%d, got:%d", *s4.ID, sid) + if sid != int64(server.ID) { + t.Errorf("updated incorrect server, expected: %d, got: %d", server.ID, sid) } if code != http.StatusOK { - t.Errorf("failed to update server with id:%d, expected: %d, got: %d", *s4.ID, http.StatusOK, code) + t.Errorf("failed to update server with id: %d, expected: %d, got: %d", server.ID, http.StatusOK, code) } } From dfeda99286de967ea1c74c36c8825d7c9b56dbcc Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Mon, 14 Aug 2023 20:18:50 -0600 Subject: [PATCH 17/31] Replace redundancies in dsserver stuff --- lib/go-tc/deliveryservice_servers.go | 142 +++++++++++++----- .../deliveryservice/eligible.go | 4 +- .../deliveryservice/servers/servers.go | 16 +- 3 files changed, 112 insertions(+), 50 deletions(-) diff --git a/lib/go-tc/deliveryservice_servers.go b/lib/go-tc/deliveryservice_servers.go index de4090b861..2be994a7fe 100644 --- a/lib/go-tc/deliveryservice_servers.go +++ b/lib/go-tc/deliveryservice_servers.go @@ -17,6 +17,8 @@ package tc import ( "time" + + "github.com/apache/trafficcontrol/lib/go-util" ) // DeliveryServiceServerV5 is the struct used to represent a delivery service server, in the latest minor version for @@ -48,6 +50,9 @@ type DeliveryServiceServerResponseV50 struct { // DeliveryServiceServerResponse is the type of a response from Traffic Ops // to a GET request to the /deliveryserviceserver endpoint. +// +// Deprecated: Direct server-to-DS assignments are deprecated in favor of +// Topology usage. type DeliveryServiceServerResponse struct { Orderby string `json:"orderby"` Response []DeliveryServiceServer `json:"response"` @@ -59,6 +64,9 @@ type DeliveryServiceServerResponse struct { // DSSMapResponse is the type of the `response` property of a response from // Traffic Ops to a PUT request made to the /deliveryserviceserver endpoint of // its API. +// +// Deprecated: Direct server-to-DS assignments are deprecated in favor of +// Topology usage. type DSSMapResponse struct { DsId int `json:"dsId"` Replace bool `json:"replace"` @@ -67,6 +75,9 @@ type DSSMapResponse struct { // DSSReplaceResponse is the type of a response from Traffic Ops to a PUT // request made to the /deliveryserviceserver endpoint of its API. +// +// Deprecated: Direct server-to-DS assignments are deprecated in favor of +// Topology usage. type DSSReplaceResponse struct { Alerts Response DSSMapResponse `json:"response"` @@ -75,6 +86,9 @@ type DSSReplaceResponse struct { // DSServersResponse is the type of a response from Traffic Ops to a POST // request made to the /deliveryservices/{{XML ID}}/servers endpoint of its // API. +// +// Deprecated: Direct server-to-DS assignments are deprecated in favor of +// Topology usage. type DSServersResponse struct { Response DeliveryServiceServers `json:"response"` Alerts @@ -82,6 +96,9 @@ type DSServersResponse struct { // DeliveryServiceServers structures represent the servers assigned to a // Delivery Service. +// +// Deprecated: Direct server-to-DS assignments are deprecated in favor of +// Topology usage. type DeliveryServiceServers struct { ServerNames []string `json:"serverNames"` XmlId string `json:"xmlId"` @@ -90,6 +107,9 @@ type DeliveryServiceServers struct { // DeliveryServiceServer is the type of each entry in the `response` array // property of responses from Traffic Ops to GET requests made to the // /deliveryserviceservers endpoint of its API. +// +// Deprecated: Direct server-to-DS assignments are deprecated in favor of +// Topology usage. type DeliveryServiceServer struct { Server *int `json:"server" db:"server"` DeliveryService *int `json:"deliveryService" db:"deliveryservice"` @@ -116,6 +136,9 @@ const ( ) // DSServerBase contains the base information for a Delivery Service Server. +// +// Deprecated: Direct server-to-DS assignments are deprecated in favor of +// Topology usage. type DSServerBase struct { Cachegroup *string `json:"cachegroup" db:"cachegroup"` CachegroupID *int `json:"cachegroupId" db:"cachegroup_id"` @@ -158,6 +181,9 @@ type DSServerBase struct { } // DSServerBaseV4 contains the base information for a Delivery Service Server. +// +// Deprecated: Direct server-to-DS assignments are deprecated in favor of +// Topology usage. type DSServerBaseV4 struct { Cachegroup *string `json:"cachegroup" db:"cachegroup"` CachegroupID *int `json:"cachegroupId" db:"cachegroup_id"` @@ -196,12 +222,18 @@ type DSServerBaseV4 struct { } // DSServerV11 contains the legacy format for a Delivery Service Server. +// +// Deprecated: Direct server-to-DS assignments are deprecated in favor of +// Topology usage. type DSServerV11 struct { DSServerBase LegacyInterfaceDetails } // DSServer contains information for a Delivery Service Server. +// +// Deprecated: Direct server-to-DS assignments are deprecated in favor of +// Topology usage. type DSServer struct { DSServerBase ServerInterfaces *[]ServerInterfaceInfo `json:"interfaces" db:"interfaces"` @@ -209,12 +241,18 @@ type DSServer struct { // DSServerResponseV30 is the type of a response from Traffic Ops to a request // for servers assigned to a Delivery Service - in API version 3.0. +// +// Deprecated: Direct server-to-DS assignments are deprecated in favor of +// Topology usage. type DSServerResponseV30 struct { Response []DSServer `json:"response"` Alerts } // DSServerV4 contains information for a V4.x Delivery Service Server. +// +// Deprecated: Direct server-to-DS assignments are deprecated in favor of +// Topology usage. type DSServerV4 struct { DSServerBaseV4 ServerInterfaces *[]ServerInterfaceInfoV40 `json:"interfaces" db:"interfaces"` @@ -222,6 +260,9 @@ type DSServerV4 struct { // DSServerResponseV40 is the type of a response from Traffic Ops to a request // for servers assigned to a Delivery Service - in API version 4.0. +// +// Deprecated: Direct server-to-DS assignments are deprecated in favor of +// Topology usage. type DSServerResponseV40 struct { Response []DSServerV4 `json:"response"` Alerts @@ -230,23 +271,39 @@ type DSServerResponseV40 struct { // DSServerResponseV4 is the type of a response from Traffic Ops to a request // for servers assigned to a Delivery Service - in the latest minor version of // API version 4. +// +// Deprecated: Direct server-to-DS assignments are deprecated in favor of +// Topology usage. type DSServerResponseV4 = DSServerResponseV40 // DSServerV5 is an alias for the latest minor version of the major version 5. +// +// Deprecated: Direct server-to-DS assignments are deprecated in favor of +// Topology usage. type DSServerV5 = DSServerV50 -// DSServerV50 contains information for a Delivery Service Server. +// DSServerV50 contains information about a server associated with some Delivery +// Service Server. +// +// Deprecated: Direct server-to-DS assignments are deprecated in favor of +// Topology usage. type DSServerV50 struct { - ServerV4 // Please replace me when ServerV50 is born - LastUpdated *time.Time `json:"lastUpdated" db:"last_updated"` - ServerCapabilities []string `json:"-" db:"server_capabilities"` - DeliveryServiceCapabilities []string `json:"-" db:"deliveryservice_capabilities"` + ServerV5 + DeliveryServices map[string][]string `json:"deliveryServices,omitempty"` + ServerCapabilities []string `json:"-" db:"server_capabilities"` + DeliveryServiceCapabilities []string `json:"-" db:"deliveryservice_capabilities"` } // DSServerResponseV5 is an alias for the latest minor version of the major version 5. +// +// Deprecated: Direct server-to-DS assignments are deprecated in favor of +// Topology usage. type DSServerResponseV5 = DSServerResponseV50 // DSServerResponseV50 is response from Traffic Ops to a request for servers assigned to a Delivery Service - in the latest minor version APIv50. +// +// Deprecated: Direct server-to-DS assignments are deprecated in favor of +// Topology usage. type DSServerResponseV50 struct { Response []DSServerV50 `json:"response"` Alerts @@ -340,44 +397,49 @@ func (baseV4 DSServerBaseV4) ToDSServerBase(routerHostName, routerPort, pDesc *s // ToDSServerV5 convert DSServerV4 lastUpdated time format to RFC3339 for DSServerV5 // and also assign V4 values to V5 -func (server DSServerV4) ToDSServerV5() DSServerV5 { - r := time.Unix(server.LastUpdated.Unix(), 0) +func (server DSServerV4) Upgrade() DSServerV5 { + var t time.Time + if server.LastUpdated != nil { + t = server.LastUpdated.Time + } + + var dses map[string][]string + if server.DeliveryServices != nil { + dses = *server.DeliveryServices + } return DSServerV5{ - ServerV4: ServerV4{ - Cachegroup: server.Cachegroup, - CachegroupID: server.CachegroupID, - CDNID: server.CDNID, - CDNName: server.CDNName, - DeliveryServices: server.DeliveryServices, - DomainName: server.DomainName, - FQDN: server.FQDN, - FqdnTime: server.FqdnTime, - GUID: server.GUID, - HostName: server.HostName, - HTTPSPort: server.HTTPSPort, - ID: server.ID, - ILOIPAddress: server.ILOIPAddress, - ILOIPGateway: server.ILOIPGateway, - ILOIPNetmask: server.ILOIPNetmask, - ILOPassword: server.ILOPassword, - ILOUsername: server.ILOUsername, - MgmtIPAddress: server.MgmtIPAddress, - MgmtIPGateway: server.MgmtIPGateway, - MgmtIPNetmask: server.MgmtIPNetmask, - OfflineReason: server.OfflineReason, - PhysLocation: server.PhysLocation, - PhysLocationID: server.PhysLocationID, - Rack: server.Rack, - Status: server.Status, - StatusID: server.StatusID, - TCPPort: server.TCPPort, - Type: server.Type, - TypeID: server.TypeID, - UpdPending: server.UpdPending, - Interfaces: *server.ServerInterfaces, + DeliveryServices: dses, + ServerV5: ServerV5{ + CacheGroup: util.CoalesceToDefault(server.Cachegroup), + CacheGroupID: util.CoalesceToDefault(server.CachegroupID), + CDNID: util.CoalesceToDefault(server.CDNID), + CDN: util.CoalesceToDefault(server.CDNName), + DomainName: util.CoalesceToDefault(server.DomainName), + GUID: server.GUID, + HostName: util.CoalesceToDefault(server.HostName), + HTTPSPort: server.HTTPSPort, + ID: util.CoalesceToDefault(server.ID), + ILOIPAddress: server.ILOIPAddress, + ILOIPGateway: server.ILOIPGateway, + ILOIPNetmask: server.ILOIPNetmask, + ILOPassword: server.ILOPassword, + ILOUsername: server.ILOUsername, + LastUpdated: t, + MgmtIPAddress: server.MgmtIPAddress, + MgmtIPGateway: server.MgmtIPGateway, + MgmtIPNetmask: server.MgmtIPNetmask, + OfflineReason: server.OfflineReason, + PhysicalLocation: util.CoalesceToDefault(server.PhysLocation), + PhysicalLocationID: util.CoalesceToDefault(server.PhysLocationID), + Rack: server.Rack, + Status: util.CoalesceToDefault(server.Status), + StatusID: util.CoalesceToDefault(server.StatusID), + TCPPort: server.TCPPort, + Type: server.Type, + TypeID: util.CoalesceToDefault(server.TypeID), + Interfaces: *server.ServerInterfaces, }, - LastUpdated: &r, ServerCapabilities: server.ServerCapabilities, DeliveryServiceCapabilities: server.DeliveryServiceCapabilities, } diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/eligible.go b/traffic_ops/traffic_ops_golang/deliveryservice/eligible.go index 7f06667a46..9a2c184ad7 100644 --- a/traffic_ops/traffic_ops_golang/deliveryservice/eligible.go +++ b/traffic_ops/traffic_ops_golang/deliveryservice/eligible.go @@ -103,7 +103,7 @@ func GetServersEligible(w http.ResponseWriter, r *http.Request) { newServerList := make([]tc.DSServerV5, len(servers)) for i, server := range servers { - newServerList[i] = server.ToDSServerV5() + newServerList[i] = server.Upgrade() } api.WriteResp(w, r, newServerList) @@ -131,7 +131,7 @@ JOIN type t ON s.type = t.id WHERE s.cdn_id = (SELECT cdn_id from deliveryservice where id = (select v from ds_id)) AND (t.name LIKE 'EDGE%' OR t.name LIKE 'ORG%') ` - dataFetchQuery := `, + dataFetchQuery := `, cg.name as cachegroup, s.cachegroup as cachegroup_id, s.cdn_id, diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go index e3a34e0c2a..d0b0587755 100644 --- a/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go +++ b/traffic_ops/traffic_ops_golang/deliveryservice/servers/servers.go @@ -757,7 +757,7 @@ func GetReadAssigned(w http.ResponseWriter, r *http.Request) { newServerList := make([]tc.DSServerV5, len(servers)) for i, server := range servers { - newServerList[i] = server.ToDSServerV5() + newServerList[i] = server.Upgrade() } api.WriteAlertsObj(w, r, http.StatusOK, alerts, newServerList) @@ -940,13 +940,13 @@ func (dss *TODSSDeliveryService) Read(h http.Header, useIMS bool) ([]interface{} ds.id in ( SELECT deliveryService FROM deliveryservice_server WHERE server = :server ) OR ds.id in ( - SELECT id FROM deliveryservice - WHERE topology in ( - SELECT topology FROM topology_cachegroup - WHERE cachegroup = ( - SELECT name FROM cachegroup - WHERE id = ( - SELECT cachegroup FROM server WHERE id = :server + SELECT id FROM deliveryservice + WHERE topology in ( + SELECT topology FROM topology_cachegroup + WHERE cachegroup = ( + SELECT name FROM cachegroup + WHERE id = ( + SELECT cachegroup FROM server WHERE id = :server )))) ` From 2d114e2aea4c92846d841b3ea21a6d4abdb1f0c3 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Mon, 14 Aug 2023 20:44:13 -0600 Subject: [PATCH 18/31] Fix up client methods and tests to use v5 structures --- .../v5/cachegroupsdeliveryservices_test.go | 4 +- traffic_ops/testing/api/v5/cdn_locks_test.go | 4 +- .../testing/api/v5/cdn_queue_updates_test.go | 16 +- traffic_ops/testing/api/v5/monitoring_test.go | 16 +- .../api/v5/servers_hostname_update_test.go | 2 - .../testing/api/v5/servers_id_status_test.go | 17 +- traffic_ops/testing/api/v5/servers_test.go | 30 +- traffic_ops/testing/api/v5/tc-fixtures.json | 582 +++++++----------- .../api/v5/topologies_queue_update_test.go | 5 +- .../testing/api/v5/traffic_control_test.go | 2 +- traffic_ops/v5-client/server.go | 56 +- 11 files changed, 293 insertions(+), 441 deletions(-) diff --git a/traffic_ops/testing/api/v5/cachegroupsdeliveryservices_test.go b/traffic_ops/testing/api/v5/cachegroupsdeliveryservices_test.go index b11194965d..8b4867ce2c 100644 --- a/traffic_ops/testing/api/v5/cachegroupsdeliveryservices_test.go +++ b/traffic_ops/testing/api/v5/cachegroupsdeliveryservices_test.go @@ -82,8 +82,8 @@ func validateCGDSServerAssignments() utils.CkReqFunc { assert.NoError(t, err, "Error: Getting server: %v - alerts: %+v", err, resp.Alerts) assert.Equal(t, len(resp.Response), 1, "Error: Getting servers: expected 1 got %v", len(resp.Response)) - serverDSes, _, err := TOSession.GetDeliveryServicesByServer(*resp.Response[0].ID, client.RequestOptions{}) - assert.NoError(t, err, "Error: Getting Delivery Service Servers #%d: %v - alerts: %+v", *resp.Response[0].ID, err, serverDSes.Alerts) + serverDSes, _, err := TOSession.GetDeliveryServicesByServer(resp.Response[0].ID, client.RequestOptions{}) + assert.NoError(t, err, "Error: Getting Delivery Service Servers #%d: %v - alerts: %+v", resp.Response[0].ID, err, serverDSes.Alerts) for _, dsID := range cgDsResp.DeliveryServices { found := false for _, serverDS := range serverDSes.Response { diff --git a/traffic_ops/testing/api/v5/cdn_locks_test.go b/traffic_ops/testing/api/v5/cdn_locks_test.go index c3a2708448..b2f4fe8395 100644 --- a/traffic_ops/testing/api/v5/cdn_locks_test.go +++ b/traffic_ops/testing/api/v5/cdn_locks_test.go @@ -658,7 +658,7 @@ func TestCDNLocks(t *testing.T) { } }, "SERVER POST": func(t *testing.T) { - server := tc.ServerV4{} + var server tc.ServerV5 err = json.Unmarshal(dat, &server) assert.NoError(t, err, "Error occurred when unmarshalling request body: %v", err) alerts, reqInf, err := testCase.ClientSession.CreateServer(server, testCase.RequestOpts) @@ -667,7 +667,7 @@ func TestCDNLocks(t *testing.T) { } }, "SERVER PUT": func(t *testing.T) { - server := tc.ServerV4{} + var server tc.ServerV5 err = json.Unmarshal(dat, &server) assert.NoError(t, err, "Error occurred when unmarshalling request body: %v", err) alerts, reqInf, err := testCase.ClientSession.UpdateServer(testCase.EndpointID(), server, testCase.RequestOpts) diff --git a/traffic_ops/testing/api/v5/cdn_queue_updates_test.go b/traffic_ops/testing/api/v5/cdn_queue_updates_test.go index 3a04c947ad..0771be0235 100644 --- a/traffic_ops/testing/api/v5/cdn_queue_updates_test.go +++ b/traffic_ops/testing/api/v5/cdn_queue_updates_test.go @@ -87,12 +87,8 @@ func validateServersUpdatePending(cdnID int, params map[string]string) utils.CkR assert.RequireGreaterOrEqual(t, len(servers.Response), 1, "expected atleast one server in response, got %d", len(servers.Response)) for _, server := range servers.Response { - assert.RequireNotNil(t, server.HostName, "Expected server hostname to not be nil.") - assert.RequireNotNil(t, server.UpdPending, "Expected Update Pending field for server %s to not be nil.", *server.HostName) - assert.Equal(t, true, *server.UpdPending, "Expected updates to be queued on all the servers filtered by CDN and parameter, but %s didn't queue updates", *server.HostName) - if server.ID != nil { - serverIDMap[*server.ID] = true - } + assert.Equal(t, true, *server.Downgrade().UpdPending, "Expected updates to be queued on all the servers filtered by CDN and parameter, but %s didn't queue updates", server.HostName) + serverIDMap[server.ID] = true } // Make sure that the servers that are not filtered by the above criteria do not have updates queued @@ -100,12 +96,8 @@ func validateServersUpdatePending(cdnID int, params map[string]string) utils.CkR assert.RequireNoError(t, err, "Couldn't get all servers: %v", err) for _, server := range allServersResp.Response { - if server.ID != nil { - if _, ok := serverIDMap[*server.ID]; !ok { - assert.RequireNotNil(t, server.HostName, "Expected server hostname to not be nil.") - assert.RequireNotNil(t, server.UpdPending, "Expected Update Pending field for server %s to not be nil.", *server.HostName) - assert.Equal(t, false, *server.UpdPending, "Did not expect server %s to have queued updates", *server.HostName) - } + if _, ok := serverIDMap[server.ID]; !ok { + assert.Equal(t, false, *server.Downgrade().UpdPending, "Did not expect server %s to have queued updates", server.HostName) } } } diff --git a/traffic_ops/testing/api/v5/monitoring_test.go b/traffic_ops/testing/api/v5/monitoring_test.go index 62e0c0c3b8..32f44f262f 100644 --- a/traffic_ops/testing/api/v5/monitoring_test.go +++ b/traffic_ops/testing/api/v5/monitoring_test.go @@ -33,7 +33,7 @@ func TestMonitoring(t *testing.T) { // This MUST NOT be run after a different function in the same Test creates a Snapshot, or the test will be invalid. // This prevents a critical bug of upgrading to 4.x bringing a CDN down until a Snapshot is performed. func GetTestMonitoringConfigNoSnapshotOnTheFly(t *testing.T) { - var server tc.ServerV4 + var server tc.ServerV5 for _, sv := range testData.Servers { if sv.Type != "EDGE" { continue @@ -41,28 +41,24 @@ func GetTestMonitoringConfigNoSnapshotOnTheFly(t *testing.T) { server = sv break } - if server.CDNName == nil || *server.CDNName == "" { + if server.CDN == "" { t.Fatal("No edge server found in test data, cannot test") } - resp, _, err := TOSession.GetTrafficMonitorConfig(*server.CDNName, client.RequestOptions{}) + resp, _, err := TOSession.GetTrafficMonitorConfig(server.CDN, client.RequestOptions{}) if err != nil { t.Errorf("getting monitoring: %v - alerts: %+v", err, resp.Alerts) } else if len(resp.Response.TrafficServers) == 0 { - t.Errorf("Expected Monitoring without a snapshot to generate on-the-fly, actual: empty monitoring object for CDN '%s'", *server.CDNName) + t.Errorf("Expected Monitoring without a snapshot to generate on-the-fly, actual: empty monitoring object for CDN '%s'", server.CDN) } } func AllCDNsCanSnapshot(t *testing.T) { - serversByHost := make(map[string]tc.ServerV4, len(testData.Servers)) + serversByHost := make(map[string]tc.ServerV5, len(testData.Servers)) for _, server := range testData.Servers { - if server.HostName == nil { - t.Error("Found server in test data with null or undefined hostName") - continue - } - serversByHost[*server.HostName] = server + serversByHost[server.HostName] = server } opts := client.NewRequestOptions() diff --git a/traffic_ops/testing/api/v5/servers_hostname_update_test.go b/traffic_ops/testing/api/v5/servers_hostname_update_test.go index 7a2b7b5453..3e7c767206 100644 --- a/traffic_ops/testing/api/v5/servers_hostname_update_test.go +++ b/traffic_ops/testing/api/v5/servers_hostname_update_test.go @@ -118,8 +118,6 @@ func validateServerApplyTimes(hostName string, expectedResp map[string]interface resp, _, err := TOSession.GetServers(opts) assert.RequireNoError(t, err, "Cannot GET Server by name '%s': %v - alerts: %+v", hostName, err, resp.Alerts) assert.RequireEqual(t, 1, len(resp.Response), "GET Server expected 1, actual %v", len(resp.Response)) - assert.RequireNotNil(t, resp.Response[0].UpdPending, "Server '%s' had nil UpdPending after update status change", hostName) - assert.RequireNotNil(t, resp.Response[0].RevalPending, "Server '%s' had nil RevalPending after update status change", hostName) for field, expected := range expectedResp { for _, server := range resp.Response { diff --git a/traffic_ops/testing/api/v5/servers_id_status_test.go b/traffic_ops/testing/api/v5/servers_id_status_test.go index 57f74162ee..0a3e3ec135 100644 --- a/traffic_ops/testing/api/v5/servers_id_status_test.go +++ b/traffic_ops/testing/api/v5/servers_id_status_test.go @@ -147,15 +147,13 @@ func validateUpdPending(hostName string) utils.CkReqFunc { assert.RequireEqual(t, 1, len(servers.Response), "Expected exactly one server returned from response, Got: %d", len(servers.Response)) updatedServer := servers.Response[0] - assert.RequireNotNil(t, updatedServer.CachegroupID, "Expected Server's CachegroupID to NOT be nil.") - assert.RequireNotNil(t, updatedServer.Cachegroup, "Expected Server's Cachegroup to NOT be nil.") opts.QueryParameters.Del("hostName") cacheGroups, _, err := TOSession.GetCacheGroups(opts) assert.RequireNoError(t, err, "Expected no error when getting cache groups: %v", err) for _, cacheGroup := range cacheGroups.Response { if cacheGroup.ParentCachegroupID != nil { - if *cacheGroup.ParentCachegroupID == *servers.Response[0].CachegroupID { + if *cacheGroup.ParentCachegroupID == servers.Response[0].CacheGroupID { assert.RequireNotNil(t, cacheGroup.Name, "Expected Cachegroup's Name to NOT be nil.") descendants[*cacheGroup.Name] = struct{}{} if cacheGroup.SecondaryParentCachegroupID != nil { @@ -165,7 +163,7 @@ func validateUpdPending(hostName string) utils.CkReqFunc { } } if cacheGroup.SecondaryParentCachegroupID != nil { - if *cacheGroup.SecondaryParentCachegroupID == *servers.Response[0].CachegroupID { + if *cacheGroup.SecondaryParentCachegroupID == servers.Response[0].CacheGroupID { assert.RequireNotNil(t, cacheGroup.Name, "Expected Cachegroup's Name to NOT be nil.") descendants[*cacheGroup.Name] = struct{}{} } @@ -175,14 +173,11 @@ func validateUpdPending(hostName string) utils.CkReqFunc { allServers, _, err := TOSession.GetServers(opts) assert.RequireNoError(t, err, "Expected no error when getting servers: %v", err) for _, server := range allServers.Response { - assert.RequireNotNil(t, server.HostName, "Expected Hostname to NOT be nil.") - assert.RequireNotNil(t, server.Cachegroup, "Expected Cachegroup to NOT be nil.") - assert.RequireNotNil(t, server.UpdPending, "Expected UpdPending to NOT be nil.") - _, ok := descendants[*server.Cachegroup] - if ok && *server.CDNName == *updatedServer.CDNName { - assert.Equal(t, true, *server.UpdPending, "Expected server %s with cachegroup %s to have updates pending.", *server.HostName, *server.Cachegroup) + _, ok := descendants[server.CacheGroup] + if ok && server.CDN == updatedServer.CDN { + assert.Equal(t, true, *server.Downgrade().UpdPending, "Expected server %s with cachegroup %s to have updates pending.", server.HostName, server.CacheGroup) } else { - assert.Equal(t, false, *server.UpdPending, "Expected server %s with cachegroup %s to NOT have updates pending.", *server.HostName, *server.Cachegroup) + assert.Equal(t, false, *server.Downgrade().UpdPending, "Expected server %s with cachegroup %s to NOT have updates pending.", server.HostName, server.CacheGroup) } } } diff --git a/traffic_ops/testing/api/v5/servers_test.go b/traffic_ops/testing/api/v5/servers_test.go index 5b488e7724..4e8a0efc22 100644 --- a/traffic_ops/testing/api/v5/servers_test.go +++ b/traffic_ops/testing/api/v5/servers_test.go @@ -344,7 +344,7 @@ func TestServers(t *testing.T) { for method, testCases := range methodTests { t.Run(method, func(t *testing.T) { for name, testCase := range testCases { - server := tc.ServerV4{} + var server tc.ServerV5 if testCase.RequestBody != nil { dat, err := json.Marshal(testCase.RequestBody) @@ -556,8 +556,7 @@ func GetServerID(t *testing.T, hostName string) func() int { serversResp, _, err := TOSession.GetServers(opts) assert.RequireNoError(t, err, "Get Servers Request failed with error:", err) assert.RequireEqual(t, 1, len(serversResp.Response), "Expected response object length 1, but got %d", len(serversResp.Response)) - assert.RequireNotNil(t, serversResp.Response[0].ID, "Expected id to not be nil") - return *serversResp.Response[0].ID + return serversResp.Response[0].ID } } @@ -573,8 +572,8 @@ func UpdateTestServerStatusLastUpdated(t *testing.T) { originalServer := resp.Response[0] // Perform an update with no changes to status - alerts, _, err := TOSession.UpdateServer(*originalServer.ID, originalServer, client.RequestOptions{}) - assert.RequireNoError(t, err, "Cannot UPDATE Server by ID %d (hostname '%s'): %v - alerts: %+v", *originalServer.ID, hostName, err, alerts) + alerts, _, err := TOSession.UpdateServer(originalServer.ID, 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) assert.RequireNoError(t, err, "Cannot get Server by hostname '%s': %v - alerts %+v", hostName, err, resp.Alerts) @@ -586,10 +585,10 @@ func UpdateTestServerStatusLastUpdated(t *testing.T) { // Changing the status, perform an update and make sure that statusLastUpdated changed newStatusID := GetStatusID(t, "ONLINE")() - originalServer.StatusID = &newStatusID + originalServer.StatusID = newStatusID - alerts, _, err = TOSession.UpdateServer(*originalServer.ID, originalServer, client.RequestOptions{}) - assert.RequireNoError(t, err, "Cannot UPDATE Server by ID %d (hostname '%s'): %v - alerts: %+v", *originalServer.ID, hostName, err, alerts) + alerts, _, err = TOSession.UpdateServer(originalServer.ID, 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) assert.RequireNoError(t, err, "Cannot get Server by hostname '%s': %v - alerts %+v", hostName, err, resp.Alerts) @@ -613,7 +612,7 @@ func UpdateDSGetServerDSID(t *testing.T) { servers, _, err := TOSession.GetServers(opts) assert.RequireNoError(t, err, "Failed to get Servers: %v - alerts: %+v", err, servers.Alerts) assert.RequireGreaterOrEqual(t, len(servers.Response), 1, "Failed to get at least one Server") - assert.RequireEqual(t, hostName, *servers.Response[0].HostName, "Expected delivery service assignment between xmlId: %v and server: %v. Got server: %v", xmlId, hostName, servers.Response[0].HostName) + assert.RequireEqual(t, hostName, servers.Response[0].HostName, "Expected delivery service assignment between xmlId: %v and server: %v. Got server: %v", xmlId, hostName, servers.Response[0].HostName) opts.QueryParameters.Set("xmlId", xmlId) dses, _, err := TOSession.GetDeliveryServices(opts) @@ -636,14 +635,14 @@ func UpdateDSGetServerDSID(t *testing.T) { assert.RequireNoError(t, err, "Failed to get servers by Topology-based Delivery Service ID with xmlId %s: %v - alerts: %+v", xmlId, err, servers.Alerts) assert.RequireGreaterOrEqual(t, len(servers.Response), 1, "Expected at least one server") for _, server := range servers.Response { - assert.NotEqual(t, hostName, *server.HostName, "Server: %v was not expected to be returned.") + assert.NotEqual(t, hostName, server.HostName, "Server: %v was not expected to be returned.") } } func CreateTestServers(t *testing.T) { for _, server := range testData.Servers { resp, _, err := TOSession.CreateServer(server, client.RequestOptions{}) - assert.RequireNoError(t, err, "Could not create server '%s': %v - alerts: %+v", *server.HostName, err, resp.Alerts) + assert.RequireNoError(t, err, "Could not create server '%s': %v - alerts: %+v", server.HostName, err, resp.Alerts) } } @@ -652,14 +651,13 @@ func DeleteTestServers(t *testing.T) { assert.NoError(t, err, "Cannot get Servers: %v - alerts: %+v", err, servers.Alerts) for _, server := range servers.Response { - delResp, _, err := TOSession.DeleteServer(*server.ID, client.RequestOptions{}) + delResp, _, err := TOSession.DeleteServer(server.ID, client.RequestOptions{}) assert.NoError(t, err, "Could not delete Server: %v - alerts: %+v", err, delResp.Alerts) // Retrieve Server to see if it got deleted opts := client.NewRequestOptions() - opts.QueryParameters.Set("id", strconv.Itoa(*server.ID)) + opts.QueryParameters.Set("id", strconv.Itoa(server.ID)) getServer, _, err := TOSession.GetServers(opts) - assert.RequireNotNil(t, server.HostName, "Expected server host name to not be nil.") - assert.NoError(t, err, "Error deleting Server for '%s' : %v - alerts: %+v", *server.HostName, err, getServer.Alerts) - assert.Equal(t, 0, len(getServer.Response), "Expected Server '%s' to be deleted", *server.HostName) + assert.NoError(t, err, "Error deleting Server for '%s' : %v - alerts: %+v", server.HostName, err, getServer.Alerts) + assert.Equal(t, 0, len(getServer.Response), "Expected Server '%s' to be deleted", server.HostName) } } diff --git a/traffic_ops/testing/api/v5/tc-fixtures.json b/traffic_ops/testing/api/v5/tc-fixtures.json index 8df76f8d48..cc00f0b1e1 100644 --- a/traffic_ops/testing/api/v5/tc-fixtures.json +++ b/traffic_ops/testing/api/v5/tc-fixtures.json @@ -2673,7 +2673,7 @@ ], "servers": [ { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, @@ -2710,19 +2710,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, "type": "EDGE", - "updPending": false, "xmppId": "atlanta-edge-01\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn2", "domainName": "ga.atlanta.kabletown.net", "guid": null, @@ -2754,19 +2752,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGEInCDN2"], + "physicalLocation": "Denver", + "profiles": ["EDGEInCDN2"], "rack": "", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, "type": "EDGE", - "updPending": false, "xmppId": "", "xmppPasswd": "" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "domainName": "kabletown.net", "guid": null, @@ -2803,19 +2799,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 8086, "type": "EDGE", - "updPending": false, "xmppId": "", "xmppPasswd": "" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, @@ -2852,19 +2846,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, "type": "EDGE", - "updPending": false, "xmppId": "atlanta-router-01\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "cachegroup2", + "cacheGroup": "cachegroup2", "cdnName": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, @@ -2901,19 +2893,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, "type": "EDGE", - "updPending": false, "xmppId": "atlanta-edge-03\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, @@ -2950,19 +2940,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, "type": "EDGE", - "updPending": false, "xmppId": "atlanta-edge-14\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, @@ -3000,19 +2988,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, "type": "EDGE", - "updPending": false, "xmppId": "atlanta-edge-15\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "parentCachegroup", + "cacheGroup": "parentCachegroup", "cdnName": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, @@ -3049,19 +3035,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["MID1"], + "physicalLocation": "Denver", + "profiles": ["MID1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, "type": "MID", - "updPending": false, "xmppId": "atlanta-mid-16\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "parentCachegroup", + "cacheGroup": "parentCachegroup", "cdnName": "cdn2", "domainName": "ga.atlanta.kabletown.net", "guid": null, @@ -3098,19 +3082,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["MID2"], + "physicalLocation": "Denver", + "profiles": ["MID2"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, "type": "MID", - "updPending": false, "xmppId": "atlanta-mid-17\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, @@ -3147,19 +3129,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, "type": "EDGE", - "updPending": false, "xmppId": "atlanta-org-1\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, @@ -3196,19 +3176,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, "type": "EDGE", - "updPending": false, "xmppId": "atlanta-org-1\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "parentCachegroup", + "cacheGroup": "parentCachegroup", "cdnName": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, @@ -3245,19 +3223,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["MID1"], + "physicalLocation": "Denver", + "profiles": ["MID1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, "type": "MID", - "updPending": false, "xmppId": "atlanta-mid-01\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "domainName": "kabletown.net", "guid": null, @@ -3294,19 +3270,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["RASCAL1"], + "physicalLocation": "Denver", + "profiles": ["RASCAL1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 81, "type": "RASCAL", - "updPending": false, "xmppId": "", "xmppPasswd": "X" }, { - "cachegroup": "cachegroup2", + "cacheGroup": "cachegroup2", "cdnName": "cdn2", "domainName": "kabletown2.net", "guid": null, @@ -3343,19 +3317,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["CDN2_EDGE"], + "physicalLocation": "Denver", + "profiles": ["CDN2_EDGE"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 81, "type": "EDGE", - "updPending": false, "xmppId": "", "xmppPasswd": "X" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "domainName": "infra.ciab.test", "guid": null, @@ -3392,19 +3364,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "ONLINE", "tcpPort": 8088, "type": "RIAK", - "updPending": false, "xmppId": "", "xmppPasswd": "" }, { - "cachegroup": "multiOriginCachegroup", + "cacheGroup": "multiOriginCachegroup", "cdnName": "cdn1", "domainName": "ga.denver.kabletown.net", "guid": null, @@ -3441,19 +3411,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["MSO"], + "physicalLocation": "Denver", + "profiles": ["MSO"], "rack": "RR 119.02", - "revalPending": true, "status": "REPORTED", "tcpPort": 80, "type": "ORG", - "updPending": true, "xmppId": "denver-mso-org-01\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "multiOriginCachegroup", + "cacheGroup": "multiOriginCachegroup", "cdnName": "cdn1", "domainName": "ga.denver.kabletown.net", "guid": null, @@ -3490,19 +3458,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["MSO"], + "physicalLocation": "Denver", + "profiles": ["MSO"], "rack": "RR 119.02", - "revalPending": false, "status": "ONLINE", "tcpPort": 80, "type": "ORG", - "updPending": false, "xmppId": "test-mso-org-01\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "multiOriginCachegroup", + "cacheGroup": "multiOriginCachegroup", "cdnName": "cdn2", "domainName": "ga.denver.kabletown.net", "guid": null, @@ -3539,19 +3505,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["MSO-CDN2"], + "physicalLocation": "Denver", + "profiles": ["MSO-CDN2"], "rack": "RR 119.02", - "revalPending": true, "status": "REPORTED", "tcpPort": 80, "type": "ORG", - "updPending": true, "xmppId": "denver-mso-org-02\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "cachegroup3", + "cacheGroup": "cachegroup3", "cdnName": "cdn1", "domainName": "kabletown2.net", "guid": null, @@ -3588,19 +3552,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 81, "type": "EDGE", - "updPending": false, "xmppId": "", "xmppPasswd": "X" }, { - "cachegroup": "cachegroup3", + "cacheGroup": "cachegroup3", "cdnName": "cdn1", "domainName": "kabletown2.net", "guid": null, @@ -3637,19 +3599,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 81, "type": "EDGE", - "updPending": false, "xmppId": "", "xmppPasswd": "X" }, { - "cachegroup": "topology-edge-cg-01", + "cacheGroup": "topology-edge-cg-01", "cdnName": "cdn1", "domainName": "edge-01.forked-topology.kabletown.net", "hostName": "topology-edge-01", @@ -3675,17 +3635,15 @@ "routerPort": "9019" } ], - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "EDGE", - "updPending": false + "type": "EDGE" }, { - "cachegroup": "topology-edge-cg-02", + "cacheGroup": "topology-edge-cg-02", "cdnName": "cdn1", "domainName": "edge-02.forked-topology.kabletown.net", "hostName": "topology-edge-02", @@ -3711,17 +3669,15 @@ "routerPort": "9020" } ], - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "EDGE", - "updPending": false + "type": "EDGE" }, { - "cachegroup": "topology-mid-cg-04", + "cacheGroup": "topology-mid-cg-04", "cdnName": "cdn1", "domainName": "mid-04.forked-topology.kabletown.net", "hostName": "topology-mid-04", @@ -3747,17 +3703,15 @@ "routerPort": "9021" } ], - "physLocation": "Denver", - "profileNames": ["MID1"], + "physicalLocation": "Denver", + "profiles": ["MID1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "MID", - "updPending": false + "type": "MID" }, { - "cachegroup": "dtrc1", + "cacheGroup": "dtrc1", "cdnName": "cdn1", "domainName": "kabletown.net", "hostName": "dtrc-mid-01", @@ -3783,17 +3737,15 @@ "routerPort": "9022" } ], - "physLocation": "Denver", - "profileNames": ["MID1"], + "physicalLocation": "Denver", + "profiles": ["MID1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "MID", - "updPending": false + "type": "MID" }, { - "cachegroup": "dtrc1", + "cacheGroup": "dtrc1", "cdnName": "cdn1", "domainName": "kabletown.net", "hostName": "dtrc-mid-02", @@ -3819,17 +3771,15 @@ "routerPort": "9023" } ], - "physLocation": "Denver", - "profileNames": ["MID1"], + "physicalLocation": "Denver", + "profiles": ["MID1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "MID", - "updPending": false + "type": "MID" }, { - "cachegroup": "dtrc1", + "cacheGroup": "dtrc1", "cdnName": "cdn1", "domainName": "kabletown.net", "hostName": "dtrc-mid-03", @@ -3855,17 +3805,15 @@ "routerPort": "9024" } ], - "physLocation": "Denver", - "profileNames": ["MID1"], + "physicalLocation": "Denver", + "profiles": ["MID1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "MID", - "updPending": false + "type": "MID" }, { - "cachegroup": "dtrc2", + "cacheGroup": "dtrc2", "cdnName": "cdn1", "domainName": "kabletown.net", "hostName": "dtrc-edge-01", @@ -3891,17 +3839,15 @@ "routerPort": "9025" } ], - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "EDGE", - "updPending": false + "type": "EDGE" }, { - "cachegroup": "dtrc2", + "cacheGroup": "dtrc2", "cdnName": "cdn1", "domainName": "kabletown.net", "hostName": "dtrc-edge-02", @@ -3927,17 +3873,15 @@ "routerPort": "9026" } ], - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "EDGE", - "updPending": false + "type": "EDGE" }, { - "cachegroup": "dtrc2", + "cacheGroup": "dtrc2", "cdnName": "cdn1", "domainName": "kabletown.net", "hostName": "dtrc-edge-03", @@ -3963,17 +3907,15 @@ "routerPort": "9027" } ], - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "EDGE", - "updPending": false + "type": "EDGE" }, { - "cachegroup": "dtrc3", + "cacheGroup": "dtrc3", "cdnName": "cdn1", "domainName": "kabletown.net", "hostName": "dtrc-edge-04", @@ -3999,17 +3941,15 @@ "routerPort": "9028" } ], - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "EDGE", - "updPending": false + "type": "EDGE" }, { - "cachegroup": "dtrc3", + "cacheGroup": "dtrc3", "cdnName": "cdn1", "domainName": "kabletown.net", "hostName": "dtrc-edge-05", @@ -4035,17 +3975,15 @@ "routerPort": "9029" } ], - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "EDGE", - "updPending": false + "type": "EDGE" }, { - "cachegroup": "dtrc3", + "cacheGroup": "dtrc3", "cdnName": "cdn1", "domainName": "kabletown.net", "hostName": "dtrc-edge-06", @@ -4071,17 +4009,15 @@ "routerPort": "9030" } ], - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "EDGE", - "updPending": false + "type": "EDGE" }, { - "cachegroup": "dtrc2", + "cacheGroup": "dtrc2", "cdnName": "cdn2", "domainName": "kabletown.net", "hostName": "dtrc-edge-07", @@ -4107,17 +4043,15 @@ "routerPort": "9031" } ], - "physLocation": "Denver", - "profileNames": ["CDN2_EDGE"], + "physicalLocation": "Denver", + "profiles": ["CDN2_EDGE"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "EDGE", - "updPending": false + "type": "EDGE" }, { - "cachegroup": "dtrc3", + "cacheGroup": "dtrc3", "cdnName": "cdn2", "domainName": "kabletown.net", "hostName": "dtrc-edge-08", @@ -4143,17 +4077,15 @@ "routerPort": "9032" } ], - "physLocation": "Denver", - "profileNames": ["CDN2_EDGE"], + "physicalLocation": "Denver", + "profiles": ["CDN2_EDGE"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "EDGE", - "updPending": false + "type": "EDGE" }, { - "cachegroup": "dtrc1", + "cacheGroup": "dtrc1", "cdnName": "cdn2", "domainName": "kabletown.net", "hostName": "dtrc-mid-04", @@ -4179,17 +4111,15 @@ "routerPort": "9033" } ], - "physLocation": "Denver", - "profileNames": ["CDN2_MID"], + "physicalLocation": "Denver", + "profiles": ["CDN2_MID"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "MID", - "updPending": false + "type": "MID" }, { - "cachegroup": "cachegroup3", + "cacheGroup": "cachegroup3", "cdnName": "cdn1", "domainName": "kabletown.net", "hostName": "edgeInCachegroup3", @@ -4215,17 +4145,15 @@ "routerPort": "9034" } ], - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "EDGE", - "updPending": false + "type": "EDGE" }, { - "cachegroup": "parentCachegroup", + "cacheGroup": "parentCachegroup", "cdnName": "cdn2", "domainName": "kabletown.net", "hostName": "midInParentCachegroup", @@ -4251,17 +4179,15 @@ "routerPort": "9035" } ], - "physLocation": "Denver", - "profileNames": ["CDN2_MID"], + "physicalLocation": "Denver", + "profiles": ["CDN2_MID"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "MID", - "updPending": false + "type": "MID" }, { - "cachegroup": "secondaryCachegroup", + "cacheGroup": "secondaryCachegroup", "cdnName": "cdn2", "domainName": "kabletown.net", "hostName": "midInSecondaryCachegroup", @@ -4287,17 +4213,15 @@ "routerPort": "9036" } ], - "physLocation": "Denver", - "profileNames": ["CDN2_MID"], + "physicalLocation": "Denver", + "profiles": ["CDN2_MID"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "MID", - "updPending": false + "type": "MID" }, { - "cachegroup": "secondaryCachegroup", + "cacheGroup": "secondaryCachegroup", "cdnName": "cdn1", "domainName": "kabletown.net", "hostName": "midInSecondaryCachegroupInCDN1", @@ -4323,17 +4247,15 @@ "routerPort": "9037" } ], - "physLocation": "Denver", - "profileNames": ["MID1"], + "physicalLocation": "Denver", + "profiles": ["MID1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "MID", - "updPending": false + "type": "MID" }, { - "cachegroup": "cdn1-only", + "cacheGroup": "cdn1-only", "cdnName": "cdn1", "domainName": "foo.kabletown.net", "guid": null, @@ -4370,19 +4292,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, "type": "EDGE", - "updPending": false, "xmppId": "", "xmppPasswd": "" }, { - "cachegroup": "fallback1", + "cacheGroup": "fallback1", "cdnName": "cdn2", "domainName": "kabletown.net", "hostName": "edgeInFallback1", @@ -4408,17 +4328,15 @@ "routerPort": "9039" } ], - "physLocation": "Denver", - "profileNames": ["CDN2_EDGE"], + "physicalLocation": "Denver", + "profiles": ["CDN2_EDGE"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "EDGE", - "updPending": false + "type": "EDGE" }, { - "cachegroup": "fallback2", + "cacheGroup": "fallback2", "cdnName": "cdn2", "domainName": "kabletown.net", "hostName": "edgeInFallback2", @@ -4444,17 +4362,15 @@ "routerPort": "9040" } ], - "physLocation": "Denver", - "profileNames": ["CDN2_EDGE"], + "physicalLocation": "Denver", + "profiles": ["CDN2_EDGE"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "EDGE", - "updPending": false + "type": "EDGE" }, { - "cachegroup": "parentCachegroup2", + "cacheGroup": "parentCachegroup2", "cdnName": "cdn2", "domainName": "kabletown.net", "hostName": "midInParentCachegroup2", @@ -4480,17 +4396,15 @@ "routerPort": "9041" } ], - "physLocation": "Denver", - "profileNames": ["CDN2_MID"], + "physicalLocation": "Denver", + "profiles": ["CDN2_MID"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "MID", - "updPending": false + "type": "MID" }, { - "cachegroup": "topology-edge-cg-01", + "cacheGroup": "topology-edge-cg-01", "cdnName": "cdn2", "domainName": "kabletown.net", "hostName": "edgeInTopologyEdgeCg01", @@ -4516,17 +4430,15 @@ "routerPort": "9042" } ], - "physLocation": "Denver", - "profileNames": ["CDN2_EDGE"], + "physicalLocation": "Denver", + "profiles": ["CDN2_EDGE"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "EDGE", - "updPending": false + "type": "EDGE" }, { - "cachegroup": "topology-edge-cg-02", + "cacheGroup": "topology-edge-cg-02", "cdnName": "cdn2", "domainName": "kabletown.net", "hostName": "edgeInTopologyEdgeCg02", @@ -4552,17 +4464,15 @@ "routerPort": "9043" } ], - "physLocation": "Denver", - "profileNames": ["CDN2_EDGE"], + "physicalLocation": "Denver", + "profiles": ["CDN2_EDGE"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "EDGE", - "updPending": false + "type": "EDGE" }, { - "cachegroup": "topology-mid-cg-01", + "cacheGroup": "topology-mid-cg-01", "cdnName": "cdn2", "domainName": "kabletown.net", "hostName": "midInTopologyMidCg01", @@ -4588,17 +4498,15 @@ "routerPort": "9044" } ], - "physLocation": "Denver", - "profileNames": ["CDN2_MID"], + "physicalLocation": "Denver", + "profiles": ["CDN2_MID"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "MID", - "updPending": false + "type": "MID" }, { - "cachegroup": "topology-mid-cg-01", + "cacheGroup": "topology-mid-cg-01", "cdnName": "cdn1", "domainName": "kabletown.net", "hostName": "midInTopologyMidCg01InCDN1", @@ -4624,17 +4532,15 @@ "routerPort": "9045" } ], - "physLocation": "Denver", - "profileNames": ["MID1"], + "physicalLocation": "Denver", + "profiles": ["MID1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "MID", - "updPending": false + "type": "MID" }, { - "cachegroup": "topology-mid-cg-02", + "cacheGroup": "topology-mid-cg-02", "cdnName": "cdn2", "domainName": "kabletown.net", "hostName": "midInTopologyMidCg02", @@ -4660,17 +4566,15 @@ "routerPort": "9046" } ], - "physLocation": "Denver", - "profileNames": ["CDN2_MID"], + "physicalLocation": "Denver", + "profiles": ["CDN2_MID"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "MID", - "updPending": false + "type": "MID" }, { - "cachegroup": "topology-mid-cg-03", + "cacheGroup": "topology-mid-cg-03", "cdnName": "cdn2", "domainName": "kabletown.net", "hostName": "midInTopologyMidCg03", @@ -4696,17 +4600,15 @@ "routerPort": "9047" } ], - "physLocation": "Denver", - "profileNames": ["CDN2_MID"], + "physicalLocation": "Denver", + "profiles": ["CDN2_MID"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "MID", - "updPending": false + "type": "MID" }, { - "cachegroup": "topology-mid-cg-04", + "cacheGroup": "topology-mid-cg-04", "cdnName": "cdn2", "domainName": "kabletown.net", "hostName": "midInTopologyMidCg04", @@ -4732,17 +4634,15 @@ "routerPort": "9048" } ], - "physLocation": "Denver", - "profileNames": ["CDN2_MID"], + "physicalLocation": "Denver", + "profiles": ["CDN2_MID"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "MID", - "updPending": false + "type": "MID" }, { - "cachegroup": "topology-mid-cg-05", + "cacheGroup": "topology-mid-cg-05", "cdnName": "cdn2", "domainName": "kabletown.net", "hostName": "midInTopologyMidCg05", @@ -4768,17 +4668,15 @@ "routerPort": "9049" } ], - "physLocation": "Denver", - "profileNames": ["CDN2_MID"], + "physicalLocation": "Denver", + "profiles": ["CDN2_MID"], "rack": "RR 119.02", - "revalPending": true, "status": "OFFLINE", "tcpPort": 80, - "type": "MID", - "updPending": true + "type": "MID" }, { - "cachegroup": "topology-mid-cg-06", + "cacheGroup": "topology-mid-cg-06", "cdnName": "cdn2", "domainName": "kabletown.net", "hostName": "midInTopologyMidCg06", @@ -4804,17 +4702,15 @@ "routerPort": "9050" } ], - "physLocation": "Denver", - "profileNames": ["CDN2_MID"], + "physicalLocation": "Denver", + "profiles": ["CDN2_MID"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "MID", - "updPending": false + "type": "MID" }, { - "cachegroup": "topology-mid-cg-07", + "cacheGroup": "topology-mid-cg-07", "cdnName": "cdn2", "domainName": "kabletown.net", "hostName": "midInTopologyMidCg07", @@ -4840,17 +4736,15 @@ "routerPort": "9051" } ], - "physLocation": "Denver", - "profileNames": ["CDN2_MID"], + "physicalLocation": "Denver", + "profiles": ["CDN2_MID"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, - "type": "MID", - "updPending": false + "type": "MID" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "domainName": "test-ds-server-assignments", "hostName": "test-ds-server-assignments", @@ -4869,16 +4763,14 @@ "routerPort": "9052" } ], - "physLocation": "Denver", - "profileNames": ["EDGE1"], - "revalPending": false, + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "status": "ONLINE", "tcpPort": 80, - "type": "EDGE", - "updPending": false + "type": "EDGE" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, @@ -4915,19 +4807,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1", "ATS_EDGE_TIER_CACHE"], + "physicalLocation": "Denver", + "profiles": ["EDGE1", "ATS_EDGE_TIER_CACHE"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, "type": "EDGE", - "updPending": false, "xmppId": "atlanta-edge-16\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "parentCachegroup3", + "cacheGroup": "parentCachegroup3", "cdnName": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, @@ -4964,19 +4854,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["MID1"], + "physicalLocation": "Denver", + "profiles": ["MID1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, "type": "MID", - "updPending": false, "xmppId": "atlanta-mid-02\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, @@ -5013,19 +4901,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": true, "status": "REPORTED", "tcpPort": 80, "type": "EDGE", - "updPending": false, "xmppId": "revalpnd-legacy-true\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, @@ -5062,19 +4948,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, "type": "EDGE", - "updPending": true, "xmppId": "revalpnd-legaacy-true\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "configUpdateTime": "2022-01-01T17:00:00-07:00", "configApplyTime": "1969-12-31T17:00:00-07:00", @@ -5113,19 +4997,17 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, "type": "EDGE", - "updPending": false, "xmppId": "config-update-time\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "configUpdateTime": "2022-01-01T17:00:00-07:00", "configApplyTime": "1969-12-31T17:00:00-07:00", @@ -5164,10 +5046,9 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "REPORTED", "tcpPort": 80, "type": "EDGE", @@ -5175,7 +5056,7 @@ "xmppPasswd": "X" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, @@ -5212,21 +5093,19 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "revalUpdateTime": "2022-01-01T17:00:00-07:00", "revalApplyTime": "1969-12-31T17:00:00-07:00", "status": "REPORTED", "tcpPort": 80, "type": "EDGE", - "updPending": false, "xmppId": "config-update-time\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, @@ -5263,20 +5142,19 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", "revalUpdateTime": "2022-01-01T17:00:00-07:00", "revalApplyTime": "1969-12-31T17:00:00-07:00", "status": "REPORTED", "tcpPort": 80, "type": "EDGE", - "updPending": false, "xmppId": "config-update-time\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "configUpdateTime": "2022-01-01T17:00:00-07:00", "configApplyTime": "1969-12-31T17:00:00-07:00", @@ -5315,21 +5193,19 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "revalUpdateTime": "2022-01-01T17:00:00-07:00", "revalApplyTime": "1969-12-31T17:00:00-07:00", "status": "REPORTED", "tcpPort": 80, "type": "EDGE", - "updPending": false, "xmppId": "config-update-time\\\\@ocdn.kabletown.net", "xmppPasswd": "X" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "configUpdateTime": "2022-01-01T17:00:00-07:00", "configApplyTime": "1969-12-31T17:00:00-07:00", @@ -5368,8 +5244,8 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", "revalUpdateTime": "2022-01-01T17:00:00-07:00", "revalApplyTime": "1969-12-31T17:00:00-07:00", @@ -5380,7 +5256,7 @@ "xmppPasswd": "X" }, { - "cachegroup": "cachegroup1", + "cacheGroup": "cachegroup1", "cdnName": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, @@ -5417,14 +5293,12 @@ "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": null, - "physLocation": "Denver", - "profileNames": ["EDGE1"], + "physicalLocation": "Denver", + "profiles": ["EDGE1"], "rack": "RR 119.02", - "revalPending": false, "status": "ADMIN_DOWN", "tcpPort": 80, "type": "EDGE", - "updPending": false, "xmppId": "atlanta-edge-01\\\\@ocdn.kabletown.net", "xmppPasswd": "X" } diff --git a/traffic_ops/testing/api/v5/topologies_queue_update_test.go b/traffic_ops/testing/api/v5/topologies_queue_update_test.go index af368b7b6f..95c5584dec 100644 --- a/traffic_ops/testing/api/v5/topologies_queue_update_test.go +++ b/traffic_ops/testing/api/v5/topologies_queue_update_test.go @@ -133,11 +133,10 @@ func validateServerUpdatesAreQueued(topologyDS string) utils.CkReqFunc { for _, server := range serversResponse.Response { assert.RequireNotNil(t, server.CDNID, "Expected Server CDNID to not be nil.") assert.RequireNotNil(t, server.HostName, "Expected Server HostName to not be nil.") - assert.RequireNotNil(t, server.UpdPending, "Expected Server UpdPending to not be nil.") - if *server.CDNID != int(topQueueUpdateResp.CDNID) { + if server.CDNID != int(topQueueUpdateResp.CDNID) { continue } - assert.Equal(t, true, *server.UpdPending, "Expected Server %s Update Pending flag to be set to true.", *server.HostName) + assert.Equal(t, true, *server.Downgrade().UpdPending, "Expected Server %s to have pending updates.", server.HostName) } } } diff --git a/traffic_ops/testing/api/v5/traffic_control_test.go b/traffic_ops/testing/api/v5/traffic_control_test.go index 970b74fc76..de4f019c49 100644 --- a/traffic_ops/testing/api/v5/traffic_control_test.go +++ b/traffic_ops/testing/api/v5/traffic_control_test.go @@ -45,7 +45,7 @@ type TrafficControl struct { PhysLocations []tc.PhysLocationV5 `json:"physLocations"` Regions []tc.RegionV5 `json:"regions"` Roles []tc.RoleV4 `json:"roles"` - Servers []tc.ServerV4 `json:"servers"` + Servers []tc.ServerV5 `json:"servers"` ServerServerCapabilities []tc.ServerServerCapability `json:"serverServerCapabilities"` ServerCapabilities []tc.ServerCapabilityV5 `json:"serverCapabilities"` ServiceCategories []tc.ServiceCategoryV5 `json:"serviceCategories"` diff --git a/traffic_ops/v5-client/server.go b/traffic_ops/v5-client/server.go index faad8c0cbb..6e20e57bbb 100644 --- a/traffic_ops/v5-client/server.go +++ b/traffic_ops/v5-client/server.go @@ -36,63 +36,63 @@ func needAndCanFetch(id *int, name *string) bool { } // CreateServer creates the given Server. -func (to *Session) CreateServer(server tc.ServerV4, opts RequestOptions) (tc.Alerts, toclientlib.ReqInf, error) { +func (to *Session) CreateServer(server tc.ServerV5, opts RequestOptions) (tc.Alerts, toclientlib.ReqInf, error) { var alerts tc.Alerts var remoteAddr net.Addr reqInf := toclientlib.ReqInf{CacheHitStatus: toclientlib.CacheHitStatusMiss, RemoteAddr: remoteAddr} - if needAndCanFetch(server.CachegroupID, server.Cachegroup) { + if server.CacheGroupID <= 0 && server.CacheGroup != "" { innerOpts := NewRequestOptions() - innerOpts.QueryParameters.Set("name", *server.Cachegroup) + innerOpts.QueryParameters.Set("name", server.CacheGroup) cg, reqInf, err := to.GetCacheGroups(innerOpts) if err != nil { - return cg.Alerts, reqInf, fmt.Errorf("no cachegroup named %s: %w", *server.Cachegroup, err) + return cg.Alerts, reqInf, fmt.Errorf("no Cache Group named %s: %w", server.CacheGroup, err) } if len(cg.Response) == 0 { - return cg.Alerts, reqInf, fmt.Errorf("no cachegroup named %s", *server.Cachegroup) + return cg.Alerts, reqInf, fmt.Errorf("no Cache Group named %s", server.CacheGroup) } if cg.Response[0].ID == nil { - return cg.Alerts, reqInf, fmt.Errorf("Cachegroup named %s has a nil ID", *server.Cachegroup) + return cg.Alerts, reqInf, fmt.Errorf("Cache Group named %s has a nil ID", server.CacheGroup) } - server.CachegroupID = cg.Response[0].ID + server.CacheGroupID = *cg.Response[0].ID } - if needAndCanFetch(server.CDNID, server.CDNName) { + if server.CDNID <= 0 && server.CDN != "" { innerOpts := NewRequestOptions() - innerOpts.QueryParameters.Set("name", *server.CDNName) + innerOpts.QueryParameters.Set("name", server.CDN) c, reqInf, err := to.GetCDNs(innerOpts) if err != nil { - return c.Alerts, reqInf, fmt.Errorf("no CDN named %s: %w", *server.CDNName, err) + return c.Alerts, reqInf, fmt.Errorf("no CDN named %s: %w", server.CDN, err) } if len(c.Response) == 0 { - return c.Alerts, reqInf, fmt.Errorf("no CDN named %s", *server.CDNName) + return c.Alerts, reqInf, fmt.Errorf("no CDN named %s", server.CDN) } - server.CDNID = &c.Response[0].ID + server.CDNID = c.Response[0].ID } - if needAndCanFetch(server.PhysLocationID, server.PhysLocation) { + if server.PhysicalLocationID <= 0 && server.PhysicalLocation != "" { innerOpts := NewRequestOptions() - innerOpts.QueryParameters.Set("name", *server.PhysLocation) + innerOpts.QueryParameters.Set("name", server.PhysicalLocation) ph, reqInf, err := to.GetPhysLocations(innerOpts) if err != nil { - return ph.Alerts, reqInf, fmt.Errorf("no physlocation named %s: %w", *server.PhysLocation, err) + return ph.Alerts, reqInf, fmt.Errorf("no Physical Location named %s: %w", server.PhysicalLocation, err) } if len(ph.Response) == 0 { - return ph.Alerts, reqInf, fmt.Errorf("no physlocation named %s", *server.PhysLocation) + return ph.Alerts, reqInf, fmt.Errorf("no Physical Location named %s", server.PhysicalLocation) } - server.PhysLocationID = &ph.Response[0].ID + server.PhysicalLocationID = ph.Response[0].ID } - if needAndCanFetch(server.StatusID, server.Status) { + if server.StatusID <= 0 && server.Status != "" { innerOpts := NewRequestOptions() - innerOpts.QueryParameters.Set("name", *server.Status) + innerOpts.QueryParameters.Set("name", server.Status) st, reqInf, err := to.GetStatuses(innerOpts) if err != nil { - return st.Alerts, reqInf, fmt.Errorf("no Status named %s: %w", *server.Status, err) + return st.Alerts, reqInf, fmt.Errorf("no Status named %s: %w", server.Status, err) } if len(st.Response) == 0 { - return alerts, reqInf, fmt.Errorf("no Status named %s", *server.Status) + return alerts, reqInf, fmt.Errorf("no Status named %s", server.Status) } - server.StatusID = &st.Response[0].ID + server.StatusID = st.Response[0].ID } - if (server.TypeID == nil || *server.TypeID == 0) && server.Type != "" { + if server.TypeID <= 0 && server.Type != "" { innerOpts := NewRequestOptions() innerOpts.QueryParameters.Set("name", server.Type) ty, _, err := to.GetTypes(innerOpts) @@ -100,9 +100,9 @@ func (to *Session) CreateServer(server tc.ServerV4, opts RequestOptions) (tc.Ale return ty.Alerts, reqInf, fmt.Errorf("no Type named '%s': %w", server.Type, err) } if len(ty.Response) == 0 { - return ty.Alerts, reqInf, fmt.Errorf("no type named %s", server.Type) + return ty.Alerts, reqInf, fmt.Errorf("no Type named %s", server.Type) } - server.TypeID = &ty.Response[0].ID + server.TypeID = ty.Response[0].ID } reqInf, err := to.post(apiServers, opts, server, &alerts) @@ -110,7 +110,7 @@ func (to *Session) CreateServer(server tc.ServerV4, opts RequestOptions) (tc.Ale } // UpdateServer replaces the Server identified by ID with the provided one. -func (to *Session) UpdateServer(id int, server tc.ServerV4, opts RequestOptions) (tc.Alerts, toclientlib.ReqInf, error) { +func (to *Session) UpdateServer(id int, server tc.ServerV5, opts RequestOptions) (tc.Alerts, toclientlib.ReqInf, error) { var alerts tc.Alerts route := fmt.Sprintf("%s/%d", apiServers, id) reqInf, err := to.put(route, opts, server, &alerts) @@ -118,8 +118,8 @@ func (to *Session) UpdateServer(id int, server tc.ServerV4, opts RequestOptions) } // GetServers retrieves Servers from Traffic Ops. -func (to *Session) GetServers(opts RequestOptions) (tc.ServersV4Response, toclientlib.ReqInf, error) { - var data tc.ServersV4Response +func (to *Session) GetServers(opts RequestOptions) (tc.ServersV5Response, toclientlib.ReqInf, error) { + var data tc.ServersV5Response reqInf, err := to.get(apiServers, opts, &data) return data, reqInf, err } From 882377f1583242d4723caeaeea47cd072dfe9dc0 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Tue, 15 Aug 2023 09:45:25 -0600 Subject: [PATCH 19/31] Update docs --- docs/source/api/v5/servers.rst | 160 +++++++++--------- .../traffic_ops_golang/server/servers.go | 1 + 2 files changed, 77 insertions(+), 84 deletions(-) diff --git a/docs/source/api/v5/servers.rst b/docs/source/api/v5/servers.rst index a612c1add1..9ca3e26e7c 100644 --- a/docs/source/api/v5/servers.rst +++ b/docs/source/api/v5/servers.rst @@ -32,43 +32,50 @@ Request Structure ----------------- .. table:: Request Query Parameters - +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ - | Name | Required | Description | - +================+==========+===================================================================================================================+ - | cachegroup | no | Return only those servers within the :term:`Cache Group` that has this :ref:`cache-group-id` | - +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ - | cachegroupName | no | Return only those servers within the :term:`Cache Group` that has this :ref:`cache-group-name` | - +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ - | dsId | no | Return only those servers assigned to the :term:`Delivery Service` identified by this integral, unique identifier.| - | | | If the Delivery Service has a :term:`Topology` assigned to it, the :ref:`to-api-servers` endpoint will return | - | | | each server whose :term:`Cache Group` is associated with a :term:`Topology Node` of that Topology and has the | - | | | :term:`Server Capabilities` that are | - | | | :term:`required by the Delivery Service ` but excluding | - | | | :term:`Origin Servers` that are not assigned to the Delivery Service. For more information, see | - | | | :ref:`multi-site-origin-qht`. | - +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ - | hostName | no | Return only those servers that have this (short) hostname | - +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ - | id | no | Return only the server with this integral, unique identifier | - +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ - | profileName | no | Return only those servers that are using the :term:`Profile` that has this :ref:`profile-name` | - +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ - | status | no | Return only those servers with this status - see :ref:`health-proto` | - +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ - | type | no | Return only servers of this :term:`Type` | - +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ - | topology | no | Return only servers who belong to cachegroups assigned to the :term:`Topology` identified by this name | - +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ - | sortOrder | no | Changes the order of sorting. Either ascending (default or "asc") or descending ("desc") | - +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ - | limit | no | Choose the maximum number of results to return | - +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ - | offset | no | The number of results to skip before beginning to return results. Must use in conjunction with limit | - +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ - | page | no | Return the n\ :sup:`th` page of results, where "n" is the value of this parameter, pages are ``limit`` long and | - | | | the first page is 1. If ``offset`` was defined, this query parameter has no effect. ``limit`` must be defined to | - | | | make use of ``page``. | - +----------------+----------+-------------------------------------------------------------------------------------------------------------------+ + +--------------------+----------+-------------------------------------------------------------------------------------------------------------------+ + | Name | Required | Description | + +====================+==========+===================================================================================================================+ + | cacheGroup | no | Return only those servers within the :term:`Cache Group` that has this :ref:`cache-group-name` | + +--------------------+----------+-------------------------------------------------------------------------------------------------------------------+ + | cachegroup | no | Return only those servers within the :term:`Cache Group` that has this :ref:`cache-group-id` | + +--------------------+----------+-------------------------------------------------------------------------------------------------------------------+ + | cacheGroupName | no | Return only those servers within the :term:`Cache Group` that has this :ref:`cache-group-name` | + +--------------------+----------+-------------------------------------------------------------------------------------------------------------------+ + | dsId | no | Return only those servers assigned to the :term:`Delivery Service` identified by this integral, unique identifier.| + | | | If the Delivery Service has a :term:`Topology` assigned to it, the :ref:`to-api-servers` endpoint will return | + | | | each server whose :term:`Cache Group` is associated with a :term:`Topology Node` of that Topology and has the | + | | | :term:`Server Capabilities` that are | + | | | :term:`required by the Delivery Service ` but excluding | + | | | :term:`Origin Servers` that are not assigned to the Delivery Service. For more information, see | + | | | :ref:`multi-site-origin-qht`. | + +--------------------+----------+-------------------------------------------------------------------------------------------------------------------+ + | hostName | no | Return only those servers that have this (short) hostname | + +--------------------+----------+-------------------------------------------------------------------------------------------------------------------+ + | id | no | Return only the server with this integral, unique identifier | + +--------------------+----------+-------------------------------------------------------------------------------------------------------------------+ + | physicalLocation | no | Return only servers that have the :term:`Physical Location` with this Name | + +--------------------+----------+-------------------------------------------------------------------------------------------------------------------+ + | physicalLocationID | no | Return only servers that have the :term:`Physical Location` with this integral, unique identifier | + +--------------------+----------+-------------------------------------------------------------------------------------------------------------------+ + | status | no | Return only those servers with this status - see :ref:`health-proto` | + +--------------------+----------+-------------------------------------------------------------------------------------------------------------------+ + | type | no | Return only servers of this :term:`Type` | + +--------------------+----------+-------------------------------------------------------------------------------------------------------------------+ + | topology | no | Return only servers who belong to cacheGroups assigned to the :term:`Topology` identified by this name | + +--------------------+----------+-------------------------------------------------------------------------------------------------------------------+ + | sortOrder | no | Changes the order of sorting. Either ascending (default or "asc") or descending ("desc") | + +--------------------+----------+-------------------------------------------------------------------------------------------------------------------+ + | limit | no | Choose the maximum number of results to return | + +--------------------+----------+-------------------------------------------------------------------------------------------------------------------+ + | offset | no | The number of results to skip before beginning to return results. Must use in conjunction with limit | + +--------------------+----------+-------------------------------------------------------------------------------------------------------------------+ + | page | no | Return the n\ :sup:`th` page of results, where "n" is the value of this parameter, pages are ``limit`` long and | + | | | the first page is 1. If ``offset`` was defined, this query parameter has no effect. ``limit`` must be defined to | + | | | make use of ``page``. | + +--------------------+----------+-------------------------------------------------------------------------------------------------------------------+ + +.. deprecated:: ATCv8 + Rather than ``cachegroup`` or ``cachegroupName``, prefer ``cacheGroup`` as the other two are deprecated. .. code-block:: http :caption: Request Example @@ -81,10 +88,10 @@ Request Structure Response Structure ------------------ -: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 +: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 -:cdnName: Name of the CDN to which the server belongs +:cdn: Name of the CDN to which the server belongs :configUpdateTime: The last time an update was requested for this server. This field defaults to standard epoch :configApplyTime: The last time an update was applied for this server. This field defaults to standard epoch :domainName: The domain part of the server's :abbr:`FQDN (Fully Qualified Domain Name)` @@ -135,10 +142,9 @@ Response Structure This field is deprecated and will be removed in a future API version. Operators should migrate this data into the ``interfaces`` property of the server. :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 -:physLocationId: An integral, unique identifier for the physical location where the server resides -:profileNames: List of :ref:`profile-name` of the :term:`Profiles` used by this server -:revalPending: A boolean value which, if ``true`` indicates that this server has pending content invalidation/revalidation +:physicalLocation: The name of the physical location where the server resides +:physicalLocationID: An integral, unique identifier for the physical location where the server resides +:profiles: List of :ref:`profile-name` of the :term:`Profiles` used by this server :revalUpdateTime: The last time a content invalidation/revalidation request was submitted for this server. This field defaults to standard epoch :revalApplyTime: The last time a content invalidation/revalidation request was applied by this server. This field defaults to standard epoch :rack: A string indicating "server rack" location @@ -156,7 +162,6 @@ Response Structure :type: The name of the :term:`Type` of this server :typeId: The integral, unique identifier of the 'type' of this server -: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 - displays as simply ``******`` if the currently logged-in user does not have the SECURE-SERVER:READ permission. @@ -173,10 +178,10 @@ Response Structure Content-Length: 538 { "response": [{ - "cachegroup": "CDN_in_a_Box_Mid", - "cachegroupId": 6, + "cacheGroup": "CDN_in_a_Box_Mid", + "cacheGroupID": 6, "cdnId": 2, - "cdnName": "CDN-in-a-Box", + "cdn": "CDN-in-a-Box", "configUpdateTime": "1969-12-31T17:00:00-07:00", "configApplyTime": "1969-12-31T17:00:00-07:00", "domainName": "infra.ciab.test", @@ -189,16 +194,15 @@ Response Structure "iloIpNetmask": "", "iloPassword": "", "iloUsername": "", - "lastUpdated": "2020-05-19 14:49:39+00", + "lastUpdated": "2020-05-19T14:49:39Z", "mgmtIpAddress": "", "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": "", - "physLocation": "Apachecon North America 2018", - "physLocationId": 1, - "profileNames": ["ATS_MID_TIER_CACHE"], + "physicalLocation": "Apachecon North America 2018", + "physicalLocationID": 1, + "profiles": ["ATS_MID_TIER_CACHE"], "rack": "", - "revalPending": false, "revalUpdateTime": "1969-12-31T17:00:00-07:00", "revalApplyTime": "1969-12-31T17:00:00-07:00", "status": "REPORTED", @@ -206,7 +210,6 @@ Response Structure "tcpPort": 80, "type": "MID", "typeId": 12, - "updPending": false, "xmppId": "", "xmppPasswd": "", "interfaces": [ @@ -226,14 +229,7 @@ Response Structure "routerPortName": "" } ] - }], - "summary": { - "count": 13 - }} - -Summary Fields -"""""""""""""" -The ``summary`` object returned by this method of this endpoint uses only the ``count`` :ref:`standard property `. + }]} ``POST`` ======== @@ -246,7 +242,7 @@ Allows a user to create a new server. Request Structure ----------------- -:cachegroupId: An integer that is the :ref:`ID of the Cache Group ` to which the server shall belong +:cacheGroupID: An integer that is the :ref:`ID of the Cache Group ` to which the server shall belong :cdnId: The integral, unique identifier of the CDN to which the server shall belong :domainName: The domain part of the server's :abbr:`FQDN (Fully Qualified Domain Name)` :hostName: The (short) hostname of the server @@ -289,8 +285,8 @@ Request Structure .. deprecated:: 3.0 This field is deprecated and will be removed in a future API version. Operators should migrate this data into the ``interfaces`` property of the server. -:physLocationId: An integral, unique identifier for the physical location where the server resides -:profileNames: List of :ref:`profile-name` of the :term:`Profiles` that shall be used by this server +:physicalLocationID: An integral, unique identifier for the physical location where the server resides +:profiles: List of :ref:`profile-name` of the :term:`Profiles` that shall be used by this server :rack: An optional string indicating "server rack" location :statusId: The integral, unique identifier of the status of this server @@ -316,7 +312,7 @@ Request Structure Content-Type: application/json { - "cachegroupId": 6, + "cacheGroupID": 6, "cdnId": 2, "domainName": "infra.ciab.test", "hostName": "test", @@ -359,8 +355,8 @@ Request Structure "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": "", - "physLocationId": 1, - "profileNames": ["ATS_MID_TIER_CACHE"], + "physicalLocationID": 1, + "profiles": ["ATS_MID_TIER_CACHE"], "statusId": 3, "tcpPort": 80, "typeId": 12 @@ -368,10 +364,10 @@ Request Structure Response Structure ------------------ -: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 +: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 -:cdnName: Name of the CDN to which the server belongs +:cdn: Name of the CDN to which the server belongs :configUpdateTime: The last time an update was requested for this server. This field defaults to standard epoch :configApplyTime: The last time an update was applied for this server. This field defaults to standard epoch :domainName: The domain part of the server's :abbr:`FQDN (Fully Qualified Domain Name)` @@ -422,10 +418,9 @@ Response Structure This field is deprecated and will be removed in a future API version. Operators should migrate this data into the ``interfaces`` property of the server. :offlineReason: A user-entered reason why the server is in ADMIN_DOWN or OFFLINE status -:physLocation: The name of the :term:`Physical Location` where the server resides -:physLocationId: An integral, unique identifier for the :term:`Physical Location` where the server resides -:profileNames: List of :ref:`profile-name` of the :term:`Profiles` used by this server -:revalPending: A boolean value which, if ``true`` indicates that this server has pending content invalidation/revalidation +:physicalLocation: The name of the :term:`Physical Location` where the server resides +:physicalLocationID: An integral, unique identifier for the :term:`Physical Location` where the server resides +:profiles: List of :ref:`profile-name` of the :term:`Profiles` used by this server :revalUpdateTime: The last time a content invalidation/revalidation request was submitted for this server. This field defaults to standard epoch :revalApplyTime: The last time a content invalidation/revalidation request was applied by this server. This field defaults to standard epoch :rack: A string indicating "server rack" location @@ -443,7 +438,6 @@ Response Structure :type: The name of the 'type' of this server :typeId: The integral, unique identifier of the 'type' of this server -: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 (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 - displays as simply ``******`` if the currently logged-in user does not have the SECURE-SERVER:READ permission. @@ -466,10 +460,10 @@ Response Structure } ], "response": { - "cachegroup": "CDN_in_a_Box_Mid", - "cachegroupId": 6, + "cacheGroup": "CDN_in_a_Box_Mid", + "cacheGroupID": 6, "cdnId": 2, - "cdnName": "CDN-in-a-Box", + "cdn": "CDN-in-a-Box", "configUpdateTime": "1969-12-31T17:00:00-07:00", "configApplyTime": "1969-12-31T17:00:00-07:00", "domainName": "infra.ciab.test", @@ -487,11 +481,10 @@ Response Structure "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": "", - "physLocation": "Apachecon North America 2018", - "physLocationId": 1, - "profileNames": ["ATS_MID_TIER_CACHE"], + "physicalLocation": "Apachecon North America 2018", + "physicalLocationID": 1, + "profiles": ["ATS_MID_TIER_CACHE"], "rack": null, - "revalPending": false, "revalUpdateTime": "1969-12-31T17:00:00-07:00", "revalApplyTime": "1969-12-31T17:00:00-07:00", "status": "REPORTED", @@ -499,7 +492,6 @@ Response Structure "tcpPort": 80, "type": "MID", "typeId": 12, - "updPending": false, "xmppId": null, "xmppPasswd": null, "interfaces": [ diff --git a/traffic_ops/traffic_ops_golang/server/servers.go b/traffic_ops/traffic_ops_golang/server/servers.go index 409a9c699f..b15a0cc6d4 100644 --- a/traffic_ops/traffic_ops_golang/server/servers.go +++ b/traffic_ops/traffic_ops_golang/server/servers.go @@ -743,6 +743,7 @@ func getServers(h http.Header, params map[string]string, tx *sqlx.Tx, user *auth // see the fields mapped in the SQL query queryParamsToSQLCols := map[string]dbhelpers.WhereColumnInfo{ "cachegroup": {Column: "s.cachegroup", Checker: api.IsInt}, + "cacheGroup": {Column: "cg.name", Checker: nil}, "parentCachegroup": {Column: "cg.parent_cachegroup_id", Checker: api.IsInt}, "cachegroupName": {Column: "cg.name", Checker: nil}, "cdn": {Column: "s.cdn_id", Checker: api.IsInt}, From 988c7bca4a21513aa2e9482fbf1e59fa145da043 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Tue, 15 Aug 2023 10:07:44 -0600 Subject: [PATCH 20/31] Add the ability to quickly check if updates or revalidations are pending --- lib/go-tc/servers.go | 14 ++++++-- lib/go-tc/servers_test.go | 34 +++++++++++++++++++ .../testing/api/v5/cdn_queue_updates_test.go | 4 +-- .../testing/api/v5/servers_id_status_test.go | 4 +-- .../api/v5/topologies_queue_update_test.go | 2 +- 5 files changed, 51 insertions(+), 7 deletions(-) diff --git a/lib/go-tc/servers.go b/lib/go-tc/servers.go index 509c97a61b..27b1e4b268 100644 --- a/lib/go-tc/servers.go +++ b/lib/go-tc/servers.go @@ -1396,13 +1396,13 @@ func (s ServerV50) Downgrade() ServerV4 { PhysLocationID: util.Ptr(s.PhysicalLocationID), ProfileNames: make([]string, len(s.Profiles)), Rack: util.CopyIfNotNil(s.Rack), - RevalPending: util.Ptr(s.RevalApplyTime != nil && s.RevalUpdateTime != nil && !s.RevalApplyTime.Before(*s.RevalUpdateTime)), + RevalPending: util.Ptr(s.RevalidationPending()), Status: util.Ptr(s.Status), StatusID: util.Ptr(s.StatusID), TCPPort: util.CopyIfNotNil(s.TCPPort), Type: s.Type, TypeID: util.Ptr(s.TypeID), - UpdPending: util.Ptr(s.ConfigApplyTime != nil && s.ConfigUpdateTime != nil && !s.ConfigApplyTime.Before(*s.ConfigUpdateTime)), + UpdPending: util.Ptr(s.UpdatePending()), XMPPID: util.CopyIfNotNil(s.XMPPID), XMPPPasswd: util.CopyIfNotNil(s.XMPPPasswd), Interfaces: make([]ServerInterfaceInfoV40, len(s.Interfaces)), @@ -1421,6 +1421,16 @@ func (s ServerV50) Downgrade() ServerV4 { return downgraded } +// UpdatePending tells whether the Server has pending updates. +func (s ServerV50) UpdatePending() bool { + return s.ConfigApplyTime != nil && s.ConfigUpdateTime != nil && !s.ConfigApplyTime.Before(*s.ConfigUpdateTime) +} + +// RevalidationPending tells whether the Server has pending revalidations. +func (s ServerV50) RevalidationPending() bool { + return s.RevalApplyTime != nil && s.RevalUpdateTime != nil && !s.RevalApplyTime.Before(*s.RevalUpdateTime) +} + // ServerV5 is the representation of a Server in the latest minor version of // version 5 of the Traffic Ops API. type ServerV5 = ServerV50 diff --git a/lib/go-tc/servers_test.go b/lib/go-tc/servers_test.go index 7e0c3ee220..0b3c8333ec 100644 --- a/lib/go-tc/servers_test.go +++ b/lib/go-tc/servers_test.go @@ -1489,3 +1489,37 @@ func TestServerNullableV2_Upgrade(t *testing.T) { t.Errorf("Incorrect XMPPPasswd after upgraded conversion; want: '%s', got: '%s'", *nullable.XMPPPasswd, *upgraded.XMPPPasswd) } } + +func ExampleServerV50_UpdatePending() { + s := ServerV50{ + ConfigApplyTime: new(time.Time), + ConfigUpdateTime: new(time.Time), + } + + *s.ConfigApplyTime = time.Now() + *s.ConfigUpdateTime = s.ConfigApplyTime.Add(-time.Hour) + fmt.Println(s.UpdatePending()) + + *s.ConfigUpdateTime = s.ConfigUpdateTime.Add(2 * time.Hour) + fmt.Println(s.UpdatePending()) + + // Output: true + // false +} + +func ExampleServerV50_RevalidationPending() { + s := ServerV50{ + RevalApplyTime: new(time.Time), + RevalUpdateTime: new(time.Time), + } + + *s.RevalApplyTime = time.Now() + *s.RevalUpdateTime = s.RevalApplyTime.Add(-time.Hour) + fmt.Println(s.RevalidationPending()) + + *s.RevalUpdateTime = s.RevalUpdateTime.Add(2 * time.Hour) + fmt.Println(s.RevalidationPending()) + + // Output: true + // false +} diff --git a/traffic_ops/testing/api/v5/cdn_queue_updates_test.go b/traffic_ops/testing/api/v5/cdn_queue_updates_test.go index 0771be0235..32d82e3ca3 100644 --- a/traffic_ops/testing/api/v5/cdn_queue_updates_test.go +++ b/traffic_ops/testing/api/v5/cdn_queue_updates_test.go @@ -87,7 +87,7 @@ func validateServersUpdatePending(cdnID int, params map[string]string) utils.CkR assert.RequireGreaterOrEqual(t, len(servers.Response), 1, "expected atleast one server in response, got %d", len(servers.Response)) for _, server := range servers.Response { - assert.Equal(t, true, *server.Downgrade().UpdPending, "Expected updates to be queued on all the servers filtered by CDN and parameter, but %s didn't queue updates", server.HostName) + assert.Equal(t, true, server.UpdatePending(), "Expected updates to be queued on all the servers filtered by CDN and parameter, but %s didn't queue updates", server.HostName) serverIDMap[server.ID] = true } @@ -97,7 +97,7 @@ func validateServersUpdatePending(cdnID int, params map[string]string) utils.CkR for _, server := range allServersResp.Response { if _, ok := serverIDMap[server.ID]; !ok { - assert.Equal(t, false, *server.Downgrade().UpdPending, "Did not expect server %s to have queued updates", server.HostName) + assert.Equal(t, false, server.UpdatePending(), "Did not expect server %s to have queued updates", server.HostName) } } } diff --git a/traffic_ops/testing/api/v5/servers_id_status_test.go b/traffic_ops/testing/api/v5/servers_id_status_test.go index 0a3e3ec135..9555156d28 100644 --- a/traffic_ops/testing/api/v5/servers_id_status_test.go +++ b/traffic_ops/testing/api/v5/servers_id_status_test.go @@ -175,9 +175,9 @@ func validateUpdPending(hostName string) utils.CkReqFunc { for _, server := range allServers.Response { _, ok := descendants[server.CacheGroup] if ok && server.CDN == updatedServer.CDN { - assert.Equal(t, true, *server.Downgrade().UpdPending, "Expected server %s with cachegroup %s to have updates pending.", server.HostName, server.CacheGroup) + assert.Equal(t, true, server.UpdatePending(), "Expected server %s with cachegroup %s to have updates pending.", server.HostName, server.CacheGroup) } else { - assert.Equal(t, false, *server.Downgrade().UpdPending, "Expected server %s with cachegroup %s to NOT have updates pending.", server.HostName, server.CacheGroup) + assert.Equal(t, false, server.UpdatePending(), "Expected server %s with cachegroup %s to NOT have updates pending.", server.HostName, server.CacheGroup) } } } diff --git a/traffic_ops/testing/api/v5/topologies_queue_update_test.go b/traffic_ops/testing/api/v5/topologies_queue_update_test.go index 95c5584dec..a1ee357358 100644 --- a/traffic_ops/testing/api/v5/topologies_queue_update_test.go +++ b/traffic_ops/testing/api/v5/topologies_queue_update_test.go @@ -136,7 +136,7 @@ func validateServerUpdatesAreQueued(topologyDS string) utils.CkReqFunc { if server.CDNID != int(topQueueUpdateResp.CDNID) { continue } - assert.Equal(t, true, *server.Downgrade().UpdPending, "Expected Server %s to have pending updates.", server.HostName) + assert.Equal(t, true, server.UpdatePending(), "Expected Server %s to have pending updates.", server.HostName) } } } From 65bad6252149eb86ae2dbfde6280298394e65ca9 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Tue, 15 Aug 2023 10:14:02 -0600 Subject: [PATCH 21/31] Update CHANGELOG --- CHANGELOG.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2c6b08787..c382dc4ce4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - [#7665](https://github.com/apache/trafficcontrol/pull/7665) *Automation* Changes to Ansible role dataset_loader to add ATS 9 support ### Added - [#7672](https://github.com/apache/trafficcontrol/pull/7672) *Traffic Control Health Client* Added peer monitor flag while using `strategies.yaml` -- [#7609](https://github.com/apache/trafficcontrol/pull/7609) *Traffic Portal* Added Scope Query Param to SSO login. +- [#7609](https://github.com/apache/trafficcontrol/pull/7609) *Traffic Portal* Added Scope Query Param to SSO login. - [#7450](https://github.com/apache/trafficcontrol/pull/7450) *Traffic Ops* Removed hypnotoad section and added listen field to traffic_ops_golang section in order to simplify cdn config. - [#7290](https://github.com/apache/trafficcontrol/pull/7302) *Traffic Monitor* Update TM results with hostname from via header, syncronize health on caches with same service address - [#7291](https://github.com/apache/trafficcontrol/pull/7291) *Traffic Ops* Extended Layered Profile feature to aggregate parameters for all server profiles. @@ -72,9 +72,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - [##7605](https://github.com/apache/trafficcontrol/pull/#7605) *Traffic Ops* Fixes `cachegroups_request_comments` v5 apis to respond with `RFC3339` date/time Format. - [#7621](https://github.com/apache/trafficcontrol/pull/7621) *Traffic Ops* Use ID token for OAuth authentication, not Access Token - [#7694](https://github.com/apache/trafficcontrol/pull/7694) *t3c*, *Traffic Control Health Client* Upgrade to ATS 9.2 -- [#7966](https://github.com/apache/trafficcontrol/pull/7696) *t3c* will no longer clear update flag when config failure occurs and will also give a cache config error msg on exit. +- [#7966](https://github.com/apache/trafficcontrol/pull/7696) *t3c* will no longer clear update flag when config failure occurs and will also give a cache config error msg on exit. - [#7716](https://github.com/apache/trafficcontrol/pull/7716) *Apache Traffic Server* Use GCC 11 for building. - [#7742](https://github.com/apache/trafficcontrol/pull/7742) *Traffic Ops* Changed api tests to supply the absolute path of certs. +- [#7718](https://github.com/apache/trafficcontrol/pull/7718) *Traffic Ops* `/servers` endpoint now responds with RFC3339 timestamps for all timestamp fields. Cleaned up naming conventions and superfluous data. ### Fixed - [#7708] (https://github.com/apache/trafficcontrol/pull/7708) *Traffic Ops* Fixes Parameters V5 apis to respond with RFC3339 date/time Format @@ -91,7 +92,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). - [#7561](https://github.com/apache/trafficcontrol/pull/7561) *Traffic Ops* *Traffic Ops* Fixed `ASN` V5 apis to respond with `RFC3339` date/time Format. - [#7598](https://github.com/apache/trafficcontrol/pull/7598) *Traffic Ops* Fixes Server Capability V5 Type Name Minor version - [#7570](https://github.com/apache/trafficcontrol/pull/7570) *Traffic Ops* Fixes `deliveryservice_request_comments` v5 apis to respond with `RFC3339` date/time Format. -- [#7312](https://github.com/apache/trafficcontrol/issues/7312) *Docs* Changing docs for CDN locks for DELETE response structure v4 and v5. +- [#7312](https://github.com/apache/trafficcontrol/issues/7312) *Docs* Changing docs for CDN locks for DELETE response structure v4 and v5. - [#7572](https://github.com/apache/trafficcontrol/pull/7572) *Traffic Ops* Fixes Delivery Service Requests V5 apis docs with RFC3339 date/time Format - [#7544](https://github.com/apache/trafficcontrol/issues/7544) *Traffic Ops* Fixes stats_summary v5 apis to respond with RFC3339 date/time Format. - [#7542](https://github.com/apache/trafficcontrol/pull/7542) *Traffic Ops* Fixed `CDN Locks` documentation to reflect the correct RFC3339 timestamps. From 600bd918662ce5b620f5ee1d8798f7920ac243d8 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Tue, 15 Aug 2023 13:30:22 -0600 Subject: [PATCH 22/31] Clarify error message --- traffic_ops/traffic_ops_golang/api/api_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/traffic_ops/traffic_ops_golang/api/api_test.go b/traffic_ops/traffic_ops_golang/api/api_test.go index 08d635144f..e852cdeaaa 100644 --- a/traffic_ops/traffic_ops_golang/api/api_test.go +++ b/traffic_ops/traffic_ops_golang/api/api_test.go @@ -436,7 +436,7 @@ func TestAPIInfo_RequestHeaders(t *testing.T) { func TestAPIInfo_SetLastModified(t *testing.T) { w := httptest.NewRecorder() inf := APIInfo{w: w} - tm := time.Now().Truncate(time.Second) + tm := time.Now().Truncate(time.Second).UTC() inf.SetLastModified(tm) wLMHdr := w.Header().Get(rfc.LastModified) @@ -449,7 +449,7 @@ func TestAPIInfo_SetLastModified(t *testing.T) { // value for LastModified headers. I suspect it's a poor attempt at rounding // - for which the `Round` method ought to be used instead. if expected := tm.Add(time.Second); lm != expected { - t.Errorf("Incorrect time set as '%s' header; want: %v, got: %v", rfc.LastModified, expected, lm) + t.Errorf("Incorrect time set as '%s' header; want: %s, got: %s", rfc.LastModified, expected.Format(time.RFC3339Nano), lm.Format(time.RFC3339Nano)) } } From 3e453d503e1666546e643a354369a66cd5d5597d Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Wed, 16 Aug 2023 08:58:46 -0600 Subject: [PATCH 23/31] Fix updPending and revalPending logic --- lib/go-tc/servers.go | 23 ++++++++++++++--------- lib/go-tc/servers_test.go | 8 ++++---- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/go-tc/servers.go b/lib/go-tc/servers.go index 27b1e4b268..d8087823de 100644 --- a/lib/go-tc/servers.go +++ b/lib/go-tc/servers.go @@ -1312,11 +1312,14 @@ func (s *ServerV40) ToServerV2FromV4(csp CommonServerProperties) (ServerNullable // ServerV50 is the representation of a Server in version 5.0 of the Traffic Ops // API. type ServerV50 struct { - CacheGroup string `json:"cacheGroup" db:"cachegroup"` - CacheGroupID int `json:"cacheGroupID" db:"cachegroup_id"` - CDNID int `json:"cdnID" db:"cdn_id"` - CDN string `json:"cdn" db:"cdn_name"` - ConfigApplyTime *time.Time `json:"configApplyTime" db:"config_apply_time"` + CacheGroup string `json:"cacheGroup" db:"cachegroup"` + CacheGroupID int `json:"cacheGroupID" db:"cachegroup_id"` + CDNID int `json:"cdnID" db:"cdn_id"` + CDN string `json:"cdn" db:"cdn_name"` + // The time at which configuration updates were last applied for this server + // by t3c. + ConfigApplyTime *time.Time `json:"configApplyTime" db:"config_apply_time"` + // The time at which configuration updates were last queued for this server. ConfigUpdateTime *time.Time `json:"configUpdateTime" db:"config_update_time"` DomainName string `json:"domainName" db:"domain_name"` // Deprecated: This property has unknown purpose and should not be used so @@ -1350,8 +1353,10 @@ type ServerV50 struct { Profiles []string `json:"profiles" db:"profile_name"` // Deprecated: This property has unknown purpose and should not be used so // that we can get rid of it. - Rack *string `json:"rack" db:"rack"` - RevalApplyTime *time.Time `json:"revalApplyTime" db:"revalidate_apply_time"` + Rack *string `json:"rack" db:"rack"` + // The time at which revalidations for this server were last updated by t3c. + RevalApplyTime *time.Time `json:"revalApplyTime" db:"revalidate_apply_time"` + // The time at which revalidations were last queued for this server. RevalUpdateTime *time.Time `json:"revalUpdateTime" db:"revalidate_update_time"` Status string `json:"status" db:"status"` StatusID int `json:"statusID" db:"status_id"` @@ -1423,12 +1428,12 @@ func (s ServerV50) Downgrade() ServerV4 { // UpdatePending tells whether the Server has pending updates. func (s ServerV50) UpdatePending() bool { - return s.ConfigApplyTime != nil && s.ConfigUpdateTime != nil && !s.ConfigApplyTime.Before(*s.ConfigUpdateTime) + return s.ConfigApplyTime != nil && s.ConfigUpdateTime != nil && !s.ConfigApplyTime.After(*s.ConfigUpdateTime) } // RevalidationPending tells whether the Server has pending revalidations. func (s ServerV50) RevalidationPending() bool { - return s.RevalApplyTime != nil && s.RevalUpdateTime != nil && !s.RevalApplyTime.Before(*s.RevalUpdateTime) + return s.RevalApplyTime != nil && s.RevalUpdateTime != nil && !s.RevalApplyTime.After(*s.RevalUpdateTime) } // ServerV5 is the representation of a Server in the latest minor version of diff --git a/lib/go-tc/servers_test.go b/lib/go-tc/servers_test.go index 0b3c8333ec..690aa49658 100644 --- a/lib/go-tc/servers_test.go +++ b/lib/go-tc/servers_test.go @@ -1503,8 +1503,8 @@ func ExampleServerV50_UpdatePending() { *s.ConfigUpdateTime = s.ConfigUpdateTime.Add(2 * time.Hour) fmt.Println(s.UpdatePending()) - // Output: true - // false + // Output: false + // true } func ExampleServerV50_RevalidationPending() { @@ -1520,6 +1520,6 @@ func ExampleServerV50_RevalidationPending() { *s.RevalUpdateTime = s.RevalUpdateTime.Add(2 * time.Hour) fmt.Println(s.RevalidationPending()) - // Output: true - // false + // Output: false + // true } From 08451a84383391f7c9a485da088273176ef390b8 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Wed, 16 Aug 2023 15:19:14 -0600 Subject: [PATCH 24/31] Remove unused field --- lib/go-tc/servers.go | 9 +++------ lib/go-tc/servers_test.go | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/go-tc/servers.go b/lib/go-tc/servers.go index d8087823de..b92c392038 100644 --- a/lib/go-tc/servers.go +++ b/lib/go-tc/servers.go @@ -1151,7 +1151,6 @@ func (s ServerV4) Upgrade() ServerV50 { Status: util.CoalesceToDefault(s.Status), StatusID: util.CoalesceToDefault(s.StatusID), TCPPort: util.CopyIfNotNil(s.TCPPort), - Topologies: []string{}, Type: s.Type, TypeID: util.CoalesceToDefault(s.TypeID), XMPPID: util.CopyIfNotNil(s.XMPPID), @@ -1362,11 +1361,9 @@ type ServerV50 struct { StatusID int `json:"statusID" db:"status_id"` StatusLastUpdated *time.Time `json:"statusLastUpdated" db:"status_last_updated"` TCPPort *int `json:"tcpPort" db:"tcp_port"` - // read-only - Topologies []string `json:"topologies" db:"topologies"` - Type string `json:"type" db:"server_type"` - TypeID int `json:"typeID" db:"server_type_id"` - XMPPID *string `json:"xmppId" db:"xmpp_id"` + Type string `json:"type" db:"server_type"` + TypeID int `json:"typeID" db:"server_type_id"` + XMPPID *string `json:"xmppId" db:"xmpp_id"` // Deprecated: This property has unknown purpose and should not be used so // that we can get rid of it. XMPPPasswd *string `json:"xmppPasswd" db:"xmpp_passwd"` diff --git a/lib/go-tc/servers_test.go b/lib/go-tc/servers_test.go index 690aa49658..286425953e 100644 --- a/lib/go-tc/servers_test.go +++ b/lib/go-tc/servers_test.go @@ -160,7 +160,6 @@ func TestServerV5DowngradeUpgrade(t *testing.T) { Status: "Status", StatusID: 5, TCPPort: nil, - Topologies: []string{}, Type: "type", TypeID: 6, XMPPID: nil, From 0555c51f37f51768bebed4eb37b14723f6d25634 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Wed, 16 Aug 2023 15:39:12 -0600 Subject: [PATCH 25/31] Fix edge case for uninitialized timestamps Previously, comparison would consider equal timestamps to mean that updates were in order, but now the update flag must have been set strictly after the queued flag is set. --- lib/go-tc/servers.go | 4 ++-- traffic_ops/traffic_ops_golang/server/servers.go | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/go-tc/servers.go b/lib/go-tc/servers.go index b92c392038..55d9ff3734 100644 --- a/lib/go-tc/servers.go +++ b/lib/go-tc/servers.go @@ -1425,12 +1425,12 @@ func (s ServerV50) Downgrade() ServerV4 { // UpdatePending tells whether the Server has pending updates. func (s ServerV50) UpdatePending() bool { - return s.ConfigApplyTime != nil && s.ConfigUpdateTime != nil && !s.ConfigApplyTime.After(*s.ConfigUpdateTime) + return s.ConfigApplyTime != nil && s.ConfigUpdateTime != nil && s.ConfigApplyTime.Before(*s.ConfigUpdateTime) } // RevalidationPending tells whether the Server has pending revalidations. func (s ServerV50) RevalidationPending() bool { - return s.RevalApplyTime != nil && s.RevalUpdateTime != nil && !s.RevalApplyTime.After(*s.RevalUpdateTime) + return s.RevalApplyTime != nil && s.RevalUpdateTime != nil && s.RevalApplyTime.Before(*s.RevalUpdateTime) } // ServerV5 is the representation of a Server in the latest minor version of diff --git a/traffic_ops/traffic_ops_golang/server/servers.go b/traffic_ops/traffic_ops_golang/server/servers.go index b15a0cc6d4..b6f470f686 100644 --- a/traffic_ops/traffic_ops_golang/server/servers.go +++ b/traffic_ops/traffic_ops_golang/server/servers.go @@ -921,7 +921,8 @@ JOIN server_profile sp ON s.id = sp.server` &s.ConfigApplyTime, &s.XMPPID, &s.XMPPPasswd, - &s.StatusLastUpdated) + &s.StatusLastUpdated, + ) if err != nil { return nil, serverCount, nil, fmt.Errorf("getting servers: %w", err), http.StatusInternalServerError, nil } From 1d0c05e28c44753740f5ad3fe3178228016bce94 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Wed, 16 Aug 2023 16:23:40 -0600 Subject: [PATCH 26/31] Update docs --- docs/source/api/v4/servers.rst | 8 ++ docs/source/api/v4/servers_id.rst | 8 ++ docs/source/api/v5/servers.rst | 173 ++++++++++++++++++----- docs/source/api/v5/servers_id.rst | 221 +++++++++++++++++++++--------- 4 files changed, 308 insertions(+), 102 deletions(-) diff --git a/docs/source/api/v4/servers.rst b/docs/source/api/v4/servers.rst index 9580d3f8b0..cc825cecec 100644 --- a/docs/source/api/v4/servers.rst +++ b/docs/source/api/v4/servers.rst @@ -140,6 +140,10 @@ Response Structure :physLocationId: An integral, unique identifier for the physical location where the server resides :profileNames: List of :ref:`profile-name` of the :term:`Profiles` used by this server :revalPending: A boolean value which, if ``true`` indicates that this server has pending content invalidation/revalidation + + .. deprecated:: 4.1 + With the addition of ``revalUpdateTime`` and ``revalApplyTime``, this field is superfluous as it is trivially calculated from those other properties. It has been removed in version 5.0 of the API. + :revalUpdateTime: The last time a content invalidation/revalidation request was submitted for this server. This field defaults to standard epoch :revalApplyTime: The last time a content invalidation/revalidation request was applied by this server. This field defaults to standard epoch :rack: A string indicating "server rack" location @@ -427,6 +431,10 @@ Response Structure :physLocationId: An integral, unique identifier for the :term:`Physical Location` where the server resides :profileNames: List of :ref:`profile-name` of the :term:`Profiles` used by this server :revalPending: A boolean value which, if ``true`` indicates that this server has pending content invalidation/revalidation + + .. deprecated:: 4.1 + With the addition of ``revalUpdateTime`` and ``revalApplyTime``, this field is superfluous as it is trivially calculated from those other properties. It has been removed in version 5.0 of the API. + :revalUpdateTime: The last time a content invalidation/revalidation request was submitted for this server. This field defaults to standard epoch :revalApplyTime: The last time a content invalidation/revalidation request was applied by this server. This field defaults to standard epoch :rack: A string indicating "server rack" location diff --git a/docs/source/api/v4/servers_id.rst b/docs/source/api/v4/servers_id.rst index 881323b709..e17ccf07ee 100644 --- a/docs/source/api/v4/servers_id.rst +++ b/docs/source/api/v4/servers_id.rst @@ -218,6 +218,10 @@ Response Structure :physLocationId: An integral, unique identifier for the :term:`Physical Location` where the server resides :profileNames: List of :ref:`profile-name` of the :term:`Profiles` used by this server :revalPending: A boolean value which, if ``true`` indicates that this server has pending content invalidation/revalidation + + .. deprecated:: 4.1 + With the addition of ``revalUpdateTime`` and ``revalApplyTime``, this field is superfluous as it is trivially calculated from those other properties. It has been removed in version 5.0 of the API. + :revalUpdateTime: The last time a content invalidation/revalidation request was submitted for this server. This field defaults to standard epoch :revalApplyTime: The last time a content invalidation/revalidation request was applied by this server. This field defaults to standard epoch :rack: A string indicating "server rack" location @@ -413,6 +417,10 @@ Response Structure :physLocationId: An integral, unique identifier for the physical location where the server resided :profileNames: List of :ref:`profile-name` of the :term:`Profiles` which was used by this server :revalPending: A boolean value which, if ``true`` indicates that this server has pending content invalidation/revalidation + + .. deprecated:: 4.1 + With the addition of ``revalUpdateTime`` and ``revalApplyTime``, this field is superfluous as it is trivially calculated from those other properties. It has been removed in version 5.0 of the API. + :revalUpdateTime: The last time a content invalidation/revalidation request was submitted for this server. This field defaults to standard epoch :revalApplyTime: The last time a content invalidation/revalidation request was applied by this server. This field defaults to standard epoch :rack: A string indicating "server rack" location diff --git a/docs/source/api/v5/servers.rst b/docs/source/api/v5/servers.rst index 9ca3e26e7c..cf1af30b41 100644 --- a/docs/source/api/v5/servers.rst +++ b/docs/source/api/v5/servers.rst @@ -88,10 +88,26 @@ Request Structure Response Structure ------------------ -: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 -:cdn: Name of the CDN to which the server belongs +:cacheGroup: A string that is the :ref:`name of the Cache Group ` to which the server belongs + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``cachegroup`` - improperly formatted camelCase. + +:cacheGroupID: An integer that is the :ref:`ID of the Cache Group ` to which the server belongs + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``cachegroupId`` - improperly formatted camelCase. + +:cdnID: The integral, unique identifier of the CDN to which the server belongs + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``cdnId`` - improperly formatted camelCase. + +:cdn: Name of the CDN to which the server belongs + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``cdnName``. It has been changed for consistency with others e.g. ``type``, ``status``, etc. + :configUpdateTime: The last time an update was requested for this server. This field defaults to standard epoch :configApplyTime: The last time an update was applied for this server. This field defaults to standard epoch :domainName: The domain part of the server's :abbr:`FQDN (Fully Qualified Domain Name)` @@ -125,7 +141,11 @@ Response Structure :routerPortName: 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. -:lastUpdated: The date and time at which this server description was last modified +:lastUpdated: The date and time at which this server description was last modified, in :RFC:3339 format + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was given in :ref:`non-rfc-datetime`. + :mgmtIpAddress: The IPv4 address of some network interface on the server used for 'management' .. deprecated:: 3.0 @@ -141,10 +161,22 @@ Response Structure .. deprecated:: 3.0 This field is deprecated and will be removed in a future API version. Operators should migrate this data into the ``interfaces`` property of the server. -:offlineReason: A user-entered reason why the server is in ADMIN_DOWN or OFFLINE status -:physicalLocation: The name of the physical location where the server resides -:physicalLocationID: An integral, unique identifier for the physical location where the server resides -:profiles: List of :ref:`profile-name` of the :term:`Profiles` used by this server +:offlineReason: A user-entered reason why the server is in ADMIN_DOWN or OFFLINE status +:physicalLocation: The name of the physical location where the server resides + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``physLocation`` - improperly formatted camelCase. + +:physicalLocationID: An integral, unique identifier for the physical location where the server resides + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``physLocationId`` - improperly formatted camelCase. + +:profiles: List of :ref:`profile-name` of the :term:`Profiles` used by this server + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``profileNames`` - it has been changed because now that this is the only identifying information for a :term:Profile that exists on a server, there is no need to distinguish it from, say, an ID. + :revalUpdateTime: The last time a content invalidation/revalidation request was submitted for this server. This field defaults to standard epoch :revalApplyTime: The last time a content invalidation/revalidation request was applied by this server. This field defaults to standard epoch :rack: A string indicating "server rack" location @@ -152,16 +184,23 @@ Response Structure .. seealso:: :ref:`health-proto` -:statusId: The integral, unique identifier of the status of this server +:statusID: The integral, unique identifier of the status of this server .. seealso:: :ref:`health-proto` + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``statusID`` - improperly formatted camelCase. + :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 :term:`Type` of this server -:typeId: The integral, unique identifier of the 'type' of this server +:type: The name of the :term:`Type` of this server +:typeID: The integral, unique identifier of the 'type' of this server + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``typeID`` - improperly formatted camelCase. + :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 - displays as simply ``******`` if the currently logged-in user does not have the SECURE-SERVER:READ permission. @@ -180,7 +219,7 @@ Response Structure { "response": [{ "cacheGroup": "CDN_in_a_Box_Mid", "cacheGroupID": 6, - "cdnId": 2, + "cdnID": 2, "cdn": "CDN-in-a-Box", "configUpdateTime": "1969-12-31T17:00:00-07:00", "configApplyTime": "1969-12-31T17:00:00-07:00", @@ -206,10 +245,10 @@ Response Structure "revalUpdateTime": "1969-12-31T17:00:00-07:00", "revalApplyTime": "1969-12-31T17:00:00-07:00", "status": "REPORTED", - "statusId": 3, + "statusID": 3, "tcpPort": 80, "type": "MID", - "typeId": 12, + "typeID": 12, "xmppId": "", "xmppPasswd": "", "interfaces": [ @@ -243,7 +282,11 @@ Allows a user to create a new server. Request Structure ----------------- :cacheGroupID: An integer that is the :ref:`ID of the Cache Group ` to which the server shall belong -:cdnId: The integral, unique identifier of the CDN to which the server shall belong +:cdnID: The integral, unique identifier of the CDN to which the server shall belong + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``cdnId`` - improperly formatted camelCase. + :domainName: The domain part of the server's :abbr:`FQDN (Fully Qualified Domain Name)` :hostName: The (short) hostname of the server :httpsPort: An optional port number on which the server listens for incoming HTTPS connections/requests @@ -286,17 +329,32 @@ Request Structure This field is deprecated and will be removed in a future API version. Operators should migrate this data into the ``interfaces`` property of the server. :physicalLocationID: An integral, unique identifier for the physical location where the server resides -:profiles: List of :ref:`profile-name` of the :term:`Profiles` that shall be used by this server -:rack: An optional string indicating "server rack" location -:statusId: The integral, unique identifier of the status of this server + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``physLocationId`` - improperly formatted camelCase. + +:profiles: List of :ref:`profile-name` of the :term:`Profiles` that shall be used by this server + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``profileNames`` - it has been changed because now that this is the only identifying information for a :term:Profile that exists on a server, there is no need to distinguish it from, say, an ID. + +:rack: An optional string indicating "server rack" location +:statusID: The integral, unique identifier of the status of this server .. seealso:: :ref:`health-proto` + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``statusId`` - improperly formatted camelCase. + :tcpPort: An optional port number 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. -:typeId: The integral, unique identifier of the 'type' of this server +:typeID: The integral, unique identifier of the 'type' of this server + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``typeId`` - improperly formatted camelCase. + :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: An optional password used in XMPP communications with the server - displays as simply ``******`` if the currently logged-in user does not have the SECURE-SERVER:READ permission. @@ -313,7 +371,7 @@ Request Structure { "cacheGroupID": 6, - "cdnId": 2, + "cdnID": 2, "domainName": "infra.ciab.test", "hostName": "test", "httpsPort": 443, @@ -357,17 +415,33 @@ Request Structure "offlineReason": "", "physicalLocationID": 1, "profiles": ["ATS_MID_TIER_CACHE"], - "statusId": 3, + "statusID": 3, "tcpPort": 80, - "typeId": 12 + "typeID": 12 } Response Structure ------------------ -: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 -:cdn: Name of the CDN to which the server belongs +:cacheGroup: A string that is the :ref:`name of the Cache Group ` to which the server belongs + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``cachegroup`` - improperly formatted camelCase. + +:cacheGroupID: An integer that is the :ref:`ID of the Cache Group ` to which the server belongs + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``cachegroupId`` - improperly formatted camelCase. + +:cdnID: The integral, unique identifier of the CDN to which the server belongs + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``cdnId`` - improperly formatted camelCase. + +:cdn: Name of the CDN to which the server belongs + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``cdnName``. It has been changed for consistency with others e.g. ``type``, ``status``, etc. + :configUpdateTime: The last time an update was requested for this server. This field defaults to standard epoch :configApplyTime: The last time an update was applied for this server. This field defaults to standard epoch :domainName: The domain part of the server's :abbr:`FQDN (Fully Qualified Domain Name)` @@ -401,7 +475,11 @@ Response Structure :routerPortName: 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. -:lastUpdated: The date and time at which this server description was last modified +:lastUpdated: The date and time at which this server description was last modified, in :RFC:3339 format + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was given in :ref:`non-rfc-datetime`. + :mgmtIpAddress: The IPv4 address of some network interface on the server used for 'management' .. deprecated:: 3.0 @@ -417,10 +495,22 @@ Response Structure .. deprecated:: 3.0 This field is deprecated and will be removed in a future API version. Operators should migrate this data into the ``interfaces`` property of the server. -:offlineReason: A user-entered reason why the server is in ADMIN_DOWN or OFFLINE status -:physicalLocation: The name of the :term:`Physical Location` where the server resides -:physicalLocationID: An integral, unique identifier for the :term:`Physical Location` where the server resides -:profiles: List of :ref:`profile-name` of the :term:`Profiles` used by this server +:offlineReason: A user-entered reason why the server is in ADMIN_DOWN or OFFLINE status +:physicalLocation: The name of the :term:`Physical Location` where the server resides + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``physLocation`` - improperly formatted camelCase. + +:physicalLocationID: An integral, unique identifier for the :term:`Physical Location` where the server resides + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``physLocationId`` - improperly formatted camelCase. + +:profiles: List of :ref:`profile-name` of the :term:`Profiles` used by this server + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``profileNames`` - it has been changed because now that this is the only identifying information for a :term:Profile that exists on a server, there is no need to distinguish it from, say, an ID. + :revalUpdateTime: The last time a content invalidation/revalidation request was submitted for this server. This field defaults to standard epoch :revalApplyTime: The last time a content invalidation/revalidation request was applied by this server. This field defaults to standard epoch :rack: A string indicating "server rack" location @@ -428,16 +518,23 @@ Response Structure .. seealso:: :ref:`health-proto` -:statusId: The integral, unique identifier of the status of this server +:statusID: The integral, unique identifier of the status of this server .. seealso:: :ref:`health-proto` + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``statusId`` - improperly formatted camelCase. + :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 -:typeId: The integral, unique identifier of the 'type' of this server +:type: The name of the 'type' of this server +:typeID: The integral, unique identifier of the 'type' of this server + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``typeId`` - improperly formatted camelCase. + :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 - displays as simply ``******`` if the currently logged-in user does not have the SECURE-SERVER:READ permission. @@ -462,7 +559,7 @@ Response Structure "response": { "cacheGroup": "CDN_in_a_Box_Mid", "cacheGroupID": 6, - "cdnId": 2, + "cdnID": 2, "cdn": "CDN-in-a-Box", "configUpdateTime": "1969-12-31T17:00:00-07:00", "configApplyTime": "1969-12-31T17:00:00-07:00", @@ -488,10 +585,10 @@ Response Structure "revalUpdateTime": "1969-12-31T17:00:00-07:00", "revalApplyTime": "1969-12-31T17:00:00-07:00", "status": "REPORTED", - "statusId": 3, + "statusID": 3, "tcpPort": 80, "type": "MID", - "typeId": 12, + "typeID": 12, "xmppId": null, "xmppPasswd": null, "interfaces": [ diff --git a/docs/source/api/v5/servers_id.rst b/docs/source/api/v5/servers_id.rst index 712b1fb209..f6275e6484 100644 --- a/docs/source/api/v5/servers_id.rst +++ b/docs/source/api/v5/servers_id.rst @@ -38,8 +38,16 @@ Request Structure | ID | The integral, unique identifier of a server | +------+---------------------------------------------+ -:cachegroupId: An integer that is the :ref:`ID of the Cache Group ` to which the server shall belong -:cdnId: The integral, unique identifier of the CDN to which the server shall belong +:cacheGroupID: An integer that is the :ref:`ID of the Cache Group ` to which the server shall belong + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``cachegroupId`` - improperly formatted camelCase. + +:cdnID: The integral, unique identifier of the CDN to which the server shall belong + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``cdnId`` - improperly formatted camelCase. + :domainName: The domain part of the server's :abbr:`FQDN (Fully Qualified Domain Name)` :hostName: The (short) hostname of the server :httpsPort: An optional port number on which the server listens for incoming HTTPS connections/requests @@ -81,18 +89,33 @@ Request Structure .. deprecated:: 3.0 This field is deprecated and will be removed in a future API version. Operators should migrate this data into the ``interfaces`` property of the server. -:physLocationId: An integral, unique identifier for the physical location where the server resides -:profileNames: List of :ref:`profile-name` of the :term:`Profiles` that shall be used by this server -:rack: An optional string indicating "server rack" location -:statusId: The integral, unique identifier of the status of this server +:physicalLocationID: An integral, unique identifier for the physical location where the server resides + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``physLocationId`` - improperly formatted camelCase. + +:profiles: List of :ref:`profile-name` of the :term:`Profiles` that shall be used by this server + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``profileNames`` - it has been changed because now that this is the only identifying information for a :term:Profile that exists on a server, there is no need to distinguish it from, say, an ID. + +:rack: An optional string indicating "server rack" location +:statusID: The integral, unique identifier of the status of this server .. seealso:: :ref:`health-proto` + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``statusId`` - improperly formatted camelCase. + :tcpPort: An optional port number 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. -:typeId: The integral, unique identifier of the 'type' of this server +:typeID: The integral, unique identifier of the 'type' of this server + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``typeId`` - improperly formatted camelCase. + :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: An optional password used in XMPP communications with the server @@ -108,8 +131,8 @@ Request Structure Content-Type: application/json { - "cachegroupId": 6, - "cdnId": 2, + "cacheGroupID": 6, + "cdnID": 2, "domainName": "infra.ciab.test", "hostName": "quest", "httpsPort": 443, @@ -151,21 +174,37 @@ Request Structure "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": "", - "physLocationId": 1, - "profileNames": ["ATS_MID_TIER_CACHE"], - "statusId": 3, + "physicalLocationID": 1, + "profiles": ["ATS_MID_TIER_CACHE"], + "statusID": 3, "tcpPort": 80, - "typeId": 12 + "typeID": 12 } Response Structure ------------------ -: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 -:configUpdateTime: The last time an update was requested for this server. This field defaults to standard epoch +:cacheGroup: A string that is the :ref:`name of the Cache Group ` to which the server belongs + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``cachegroup`` - improperly formatted camelCase. + +:cacheGroupID: An integer that is the :ref:`ID of the Cache Group ` to which the server belongs + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``cachegroupId`` - improperly formatted camelCase. + +:cdnID: The integral, unique identifier of the CDN to which the server belongs + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``cdnId`` - improperly formatted camelCase. + +:cdn: Name of the CDN to which the server belongs + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``cdnName``. It has been changed for consistency with others e.g. ``type``, ``status``, etc. + :configApplyTime: The last time an update was applied for this server. This field defaults to standard epoch -:cdnName: Name of the CDN to which the server belongs +:configUpdateTime: The last time an update was requested for this server. This field defaults to standard epoch :domainName: The domain part of the server's :abbr:`FQDN (Fully Qualified Domain Name)` :guid: An identifier used to uniquely identify the server @@ -197,7 +236,11 @@ Response Structure :routerPortName: 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. -:lastUpdated: The date and time at which this server description was last modified +:lastUpdated: The date and time at which this server description was last modified, in :RFC:3339 format + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was given in :ref:`non-rfc-datetime`. + :mgmtIpAddress: The IPv4 address of some network interface on the server used for 'management' .. deprecated:: 3.0 @@ -213,11 +256,22 @@ Response Structure .. deprecated:: 3.0 This field is deprecated and will be removed in a future API version. Operators should migrate this data into the ``interfaces`` property of the server. -:offlineReason: A user-entered reason why the server is in ADMIN_DOWN or OFFLINE status -:physLocation: The name of the :term:`Physical Location` where the server resides -:physLocationId: An integral, unique identifier for the :term:`Physical Location` where the server resides -:profileNames: List of :ref:`profile-name` of the :term:`Profiles` used by this server -:revalPending: A boolean value which, if ``true`` indicates that this server has pending content invalidation/revalidation +:offlineReason: A user-entered reason why the server is in ADMIN_DOWN or OFFLINE status +:physicalLocation: The name of the :term:`Physical Location` where the server resides + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``physLocation`` - improperly formatted camelCase. + +:physicalLocationID: An integral, unique identifier for the :term:`Physical Location` where the server resides + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``physLocationId`` - improperly formatted camelCase. + +:profiles: List of :ref:`profile-name` of the :term:`Profiles` used by this server + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``profileNames`` - it has been changed because now that this is the only identifying information for a :term:Profile that exists on a server, there is no need to distinguish it from, say, an ID. + :revalUpdateTime: The last time a content invalidation/revalidation request was submitted for this server. This field defaults to standard epoch :revalApplyTime: The last time a content invalidation/revalidation request was applied by this server. This field defaults to standard epoch :rack: A string indicating "server rack" location @@ -225,17 +279,23 @@ Response Structure .. seealso:: :ref:`health-proto` -:statusId: The integral, unique identifier of the status of this server +:statusID: The integral, unique identifier of the status of this server .. seealso:: :ref:`health-proto` + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``statusId`` - improperly formatted camelCase. + :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 -:typeId: The integral, unique identifier of the 'type' of this server -: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) +:type: The name of the 'type' of this server +:typeID: The integral, unique identifier of the 'type' of this server + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``typeId`` - improperly formatted camelCase. + :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 @@ -262,10 +322,10 @@ Response Structure } ], "response": { - "cachegroup": "CDN_in_a_Box_Mid", - "cachegroupId": 6, - "cdnId": 2, - "cdnName": "CDN-in-a-Box", + "cacheGroup": "CDN_in_a_Box_Mid", + "cacheGroupID": 6, + "cdnID": 2, + "cdn": "CDN-in-a-Box", "configUpdateTime": "2022-02-28T15:44:15.895145-07:00", "configApplyTime": "2022-02-18T13:52:47.129174-07:00", "domainName": "infra.ciab.test", @@ -283,19 +343,17 @@ Response Structure "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": "", - "physLocation": "Apachecon North America 2018", - "physLocationId": 1, - "profileNames": ["ATS_MID_TIER_CACHE"], + "physicalLocation": "Apachecon North America 2018", + "physicalLocationID": 1, + "profiles": ["ATS_MID_TIER_CACHE"], "rack": null, - "revalPending": false, "revalUpdateTime": "1969-12-31T17:00:00-07:00", "revalApplyTime": "1969-12-31T17:00:00-07:00", "status": "REPORTED", - "statusId": 3, + "statusID": 3, "tcpPort": 80, "type": "MID", - "typeId": 12, - "updPending": true, + "typeID": 12, "xmppId": null, "xmppPasswd": null, "interfaces": [ @@ -355,10 +413,26 @@ Request Structure Response Structure ------------------ -:cachegroup: A string that is the :ref:`name of the Cache Group ` to which the server belonged -:cachegroupId: An integer that is the :ref:`ID of the Cache Group ` to which the server belonged -:cdnId: The integral, unique identifier of the CDN to which the server belonged -:cdnName: Name of the CDN to which the server belonged +:cacheGroup: A string that is the :ref:`name of the Cache Group ` to which the server belonged + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``cachegroup`` - improperly formatted camelCase. + +:cacheGroupID: An integer that is the :ref:`ID of the Cache Group ` to which the server belonged + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``cachegroupId`` - improperly formatted camelCase. + +:cdnID: The integral, unique identifier of the CDN to which the server belonged + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``cdnId`` - improperly formatted camelCase. + +:cdn: Name of the CDN to which the server belonged + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``cdnName``. It has been changed for consistency with others e.g. ``type``, ``status``, etc. + :configUpdateTime: The last time an update was requested for this server. This field defaults to standard epoch :configApplyTime: The last time an update was applied for this server. This field defaults to standard epoch :domainName: The domain part of the server's :abbr:`FQDN (Fully Qualified Domain Name)` @@ -392,7 +466,11 @@ Response Structure :routerPortName: 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. -:lastUpdated: The date and time at which this server description was last modified +:lastUpdated: The date and time at which this server description was last modified, in :RFC:3339 format + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was given in :ref:`non-rfc-datetime`. + :mgmtIpAddress: The IPv4 address of some network interface on the server that was used for 'management' .. deprecated:: 3.0 @@ -408,11 +486,22 @@ Response Structure .. deprecated:: 3.0 This field is deprecated and will be removed in a future API version. Operators should migrate this data into the ``interfaces`` property of the server. -:offlineReason: A user-entered reason why the server was in ADMIN_DOWN or OFFLINE status -:physLocation: The name of the physical location where the server resided -:physLocationId: An integral, unique identifier for the physical location where the server resided -:profileNames: List of :ref:`profile-name` of the :term:`Profiles` which was used by this server -:revalPending: A boolean value which, if ``true`` indicates that this server has pending content invalidation/revalidation +:offlineReason: A user-entered reason why the server was in ADMIN_DOWN or OFFLINE status +:physicalLocation: The name of the physical location where the server resided + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``physLocation`` - improperly formatted camelCase. + +:physicalLocationID: An integral, unique identifier for the physical location where the server resided + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``physLocationId`` - improperly formatted camelCase. + +:profiles: List of :ref:`profile-name` of the :term:`Profiles` which was used by this server + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``profileNames`` - it has been changed because now that this is the only identifying information for a :term:Profile that exists on a server, there is no need to distinguish it from, say, an ID. + :revalUpdateTime: The last time a content invalidation/revalidation request was submitted for this server. This field defaults to standard epoch :revalApplyTime: The last time a content invalidation/revalidation request was applied by this server. This field defaults to standard epoch :rack: A string indicating "server rack" location @@ -420,17 +509,23 @@ Response Structure .. seealso:: :ref:`health-proto` -:statusId: The integral, unique identifier of the status of this server +:statusID: The integral, unique identifier of the status of this server .. seealso:: :ref:`health-proto` + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``statusId`` - improperly formatted camelCase. + :tcpPort: The port on which this server listened 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 :term:`Type` of this server -:typeId: The integral, unique identifier of the 'type' of this server -: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 (T3C, formerly ORT) +:type: The name of the :term:`Type` of this server +:typeID: The integral, unique identifier of the 'type' of this server + + .. versionchanged:: 5.0 + In earlier versions of the API, this field was known by the name ``typeId`` - improperly formatted camelCase. + :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 @@ -453,10 +548,10 @@ Response Structure } ], "response": { - "cachegroup": "CDN_in_a_Box_Mid", - "cachegroupId": 6, - "cdnId": 2, - "cdnName": "CDN-in-a-Box", + "cacheGroup": "CDN_in_a_Box_Mid", + "cacheGroupID": 6, + "cdnID": 2, + "cdn": "CDN-in-a-Box", "configUpdateTime": "1969-12-31T17:00:00-07:00", "configApplyTime": "1969-12-31T17:00:00-07:00", "domainName": "infra.ciab.test", @@ -474,19 +569,17 @@ Response Structure "mgmtIpGateway": "", "mgmtIpNetmask": "", "offlineReason": "", - "physLocation": "Apachecon North America 2018", - "physLocationId": 1, - "profileNames": ["ATS_MID_TIER_CACHE"], + "physicalLocation": "Apachecon North America 2018", + "physicalLocationID": 1, + "profiles": ["ATS_MID_TIER_CACHE"], "rack": null, - "revalPending": false, "revalUpdateTime": "1969-12-31T17:00:00-07:00", "revalApplyTime": "1969-12-31T17:00:00-07:00", "status": "REPORTED", - "statusId": 3, + "statusID": 3, "tcpPort": 80, "type": "MID", - "typeId": 12, - "updPending": false, + "typeID": 12, "xmppId": null, "xmppPasswd": null, "interfaces": [ From 8fa5f2241477ae7dccdd7d18d05e6e80f537b16b Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Wed, 16 Aug 2023 16:23:55 -0600 Subject: [PATCH 27/31] fix incorrect naming in fixtures --- traffic_ops/testing/api/v5/tc-fixtures.json | 130 ++++++++++---------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/traffic_ops/testing/api/v5/tc-fixtures.json b/traffic_ops/testing/api/v5/tc-fixtures.json index cc00f0b1e1..5e97ee55ca 100644 --- a/traffic_ops/testing/api/v5/tc-fixtures.json +++ b/traffic_ops/testing/api/v5/tc-fixtures.json @@ -2674,7 +2674,7 @@ "servers": [ { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, "hostName": "atlanta-edge-01", @@ -2721,7 +2721,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "ga.atlanta.kabletown.net", "guid": null, "hostName": "cdn2-test-edge", @@ -2763,7 +2763,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "kabletown.net", "guid": null, "hostName": "influxdb02", @@ -2810,7 +2810,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, "hostName": "atlanta-router-01", @@ -2857,7 +2857,7 @@ }, { "cacheGroup": "cachegroup2", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, "hostName": "atlanta-edge-03", @@ -2904,7 +2904,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, "hostName": "atlanta-edge-14", @@ -2951,7 +2951,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, "hostName": "atlanta-edge-15", @@ -2999,7 +2999,7 @@ }, { "cacheGroup": "parentCachegroup", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, "hostName": "atlanta-mid-16", @@ -3046,7 +3046,7 @@ }, { "cacheGroup": "parentCachegroup", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "ga.atlanta.kabletown.net", "guid": null, "hostName": "atlanta-mid-17", @@ -3093,7 +3093,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, "hostName": "atlanta-org-1", @@ -3140,7 +3140,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, "hostName": "atlanta-org-2", @@ -3187,7 +3187,7 @@ }, { "cacheGroup": "parentCachegroup", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, "hostName": "atlanta-mid-01", @@ -3234,7 +3234,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "kabletown.net", "guid": null, "hostName": "rascal01", @@ -3281,7 +3281,7 @@ }, { "cacheGroup": "cachegroup2", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "kabletown2.net", "guid": null, "hostName": "edge1-cdn2", @@ -3328,7 +3328,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "infra.ciab.test", "guid": null, "hostName": "trafficvault", @@ -3375,7 +3375,7 @@ }, { "cacheGroup": "multiOriginCachegroup", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "ga.denver.kabletown.net", "guid": null, "hostName": "denver-mso-org-01", @@ -3422,7 +3422,7 @@ }, { "cacheGroup": "multiOriginCachegroup", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "ga.denver.kabletown.net", "guid": null, "hostName": "test-mso-org-01", @@ -3469,7 +3469,7 @@ }, { "cacheGroup": "multiOriginCachegroup", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "ga.denver.kabletown.net", "guid": null, "hostName": "denver-mso-org-02", @@ -3516,7 +3516,7 @@ }, { "cacheGroup": "cachegroup3", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "kabletown2.net", "guid": null, "hostName": "edge1-cdn1-cg3", @@ -3563,7 +3563,7 @@ }, { "cacheGroup": "cachegroup3", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "kabletown2.net", "guid": null, "hostName": "edge2-cdn1-cg3", @@ -3610,7 +3610,7 @@ }, { "cacheGroup": "topology-edge-cg-01", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "edge-01.forked-topology.kabletown.net", "hostName": "topology-edge-01", "httpsPort": 443, @@ -3644,7 +3644,7 @@ }, { "cacheGroup": "topology-edge-cg-02", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "edge-02.forked-topology.kabletown.net", "hostName": "topology-edge-02", "httpsPort": 443, @@ -3678,7 +3678,7 @@ }, { "cacheGroup": "topology-mid-cg-04", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "mid-04.forked-topology.kabletown.net", "hostName": "topology-mid-04", "httpsPort": 443, @@ -3712,7 +3712,7 @@ }, { "cacheGroup": "dtrc1", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "kabletown.net", "hostName": "dtrc-mid-01", "httpsPort": 443, @@ -3746,7 +3746,7 @@ }, { "cacheGroup": "dtrc1", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "kabletown.net", "hostName": "dtrc-mid-02", "httpsPort": 443, @@ -3780,7 +3780,7 @@ }, { "cacheGroup": "dtrc1", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "kabletown.net", "hostName": "dtrc-mid-03", "httpsPort": 443, @@ -3814,7 +3814,7 @@ }, { "cacheGroup": "dtrc2", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "kabletown.net", "hostName": "dtrc-edge-01", "httpsPort": 443, @@ -3848,7 +3848,7 @@ }, { "cacheGroup": "dtrc2", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "kabletown.net", "hostName": "dtrc-edge-02", "httpsPort": 443, @@ -3882,7 +3882,7 @@ }, { "cacheGroup": "dtrc2", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "kabletown.net", "hostName": "dtrc-edge-03", "httpsPort": 443, @@ -3916,7 +3916,7 @@ }, { "cacheGroup": "dtrc3", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "kabletown.net", "hostName": "dtrc-edge-04", "httpsPort": 443, @@ -3950,7 +3950,7 @@ }, { "cacheGroup": "dtrc3", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "kabletown.net", "hostName": "dtrc-edge-05", "httpsPort": 443, @@ -3984,7 +3984,7 @@ }, { "cacheGroup": "dtrc3", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "kabletown.net", "hostName": "dtrc-edge-06", "httpsPort": 443, @@ -4018,7 +4018,7 @@ }, { "cacheGroup": "dtrc2", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "kabletown.net", "hostName": "dtrc-edge-07", "httpsPort": 443, @@ -4052,7 +4052,7 @@ }, { "cacheGroup": "dtrc3", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "kabletown.net", "hostName": "dtrc-edge-08", "httpsPort": 443, @@ -4086,7 +4086,7 @@ }, { "cacheGroup": "dtrc1", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "kabletown.net", "hostName": "dtrc-mid-04", "httpsPort": 443, @@ -4120,7 +4120,7 @@ }, { "cacheGroup": "cachegroup3", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "kabletown.net", "hostName": "edgeInCachegroup3", "httpsPort": 443, @@ -4154,7 +4154,7 @@ }, { "cacheGroup": "parentCachegroup", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "kabletown.net", "hostName": "midInParentCachegroup", "httpsPort": 443, @@ -4188,7 +4188,7 @@ }, { "cacheGroup": "secondaryCachegroup", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "kabletown.net", "hostName": "midInSecondaryCachegroup", "httpsPort": 443, @@ -4222,7 +4222,7 @@ }, { "cacheGroup": "secondaryCachegroup", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "kabletown.net", "hostName": "midInSecondaryCachegroupInCDN1", "httpsPort": 443, @@ -4256,7 +4256,7 @@ }, { "cacheGroup": "cdn1-only", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "foo.kabletown.net", "guid": null, "hostName": "edge-in-cdn1-only", @@ -4303,7 +4303,7 @@ }, { "cacheGroup": "fallback1", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "kabletown.net", "hostName": "edgeInFallback1", "httpsPort": 443, @@ -4337,7 +4337,7 @@ }, { "cacheGroup": "fallback2", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "kabletown.net", "hostName": "edgeInFallback2", "httpsPort": 443, @@ -4371,7 +4371,7 @@ }, { "cacheGroup": "parentCachegroup2", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "kabletown.net", "hostName": "midInParentCachegroup2", "httpsPort": 443, @@ -4405,7 +4405,7 @@ }, { "cacheGroup": "topology-edge-cg-01", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "kabletown.net", "hostName": "edgeInTopologyEdgeCg01", "httpsPort": 443, @@ -4439,7 +4439,7 @@ }, { "cacheGroup": "topology-edge-cg-02", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "kabletown.net", "hostName": "edgeInTopologyEdgeCg02", "httpsPort": 443, @@ -4473,7 +4473,7 @@ }, { "cacheGroup": "topology-mid-cg-01", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "kabletown.net", "hostName": "midInTopologyMidCg01", "httpsPort": 443, @@ -4507,7 +4507,7 @@ }, { "cacheGroup": "topology-mid-cg-01", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "kabletown.net", "hostName": "midInTopologyMidCg01InCDN1", "httpsPort": 443, @@ -4541,7 +4541,7 @@ }, { "cacheGroup": "topology-mid-cg-02", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "kabletown.net", "hostName": "midInTopologyMidCg02", "httpsPort": 443, @@ -4575,7 +4575,7 @@ }, { "cacheGroup": "topology-mid-cg-03", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "kabletown.net", "hostName": "midInTopologyMidCg03", "httpsPort": 443, @@ -4609,7 +4609,7 @@ }, { "cacheGroup": "topology-mid-cg-04", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "kabletown.net", "hostName": "midInTopologyMidCg04", "httpsPort": 443, @@ -4643,7 +4643,7 @@ }, { "cacheGroup": "topology-mid-cg-05", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "kabletown.net", "hostName": "midInTopologyMidCg05", "httpsPort": 443, @@ -4677,7 +4677,7 @@ }, { "cacheGroup": "topology-mid-cg-06", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "kabletown.net", "hostName": "midInTopologyMidCg06", "httpsPort": 443, @@ -4711,7 +4711,7 @@ }, { "cacheGroup": "topology-mid-cg-07", - "cdnName": "cdn2", + "cdn": "cdn2", "domainName": "kabletown.net", "hostName": "midInTopologyMidCg07", "httpsPort": 443, @@ -4745,7 +4745,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "test-ds-server-assignments", "hostName": "test-ds-server-assignments", "httpsPort": 443, @@ -4771,7 +4771,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, "hostName": "atlanta-edge-16", @@ -4818,7 +4818,7 @@ }, { "cacheGroup": "parentCachegroup3", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, "hostName": "atlanta-mid-02", @@ -4865,7 +4865,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, "hostName": "revalpnd-legacy-true", @@ -4912,7 +4912,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, "hostName": "updatepnd-legacy-true", @@ -4959,7 +4959,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "configUpdateTime": "2022-01-01T17:00:00-07:00", "configApplyTime": "1969-12-31T17:00:00-07:00", "domainName": "ga.atlanta.kabletown.net", @@ -5008,7 +5008,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "configUpdateTime": "2022-01-01T17:00:00-07:00", "configApplyTime": "1969-12-31T17:00:00-07:00", "domainName": "ga.atlanta.kabletown.net", @@ -5057,7 +5057,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, "hostName": "reval-update-time", @@ -5106,7 +5106,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, "hostName": "reval-update-time-no-revalpend", @@ -5155,7 +5155,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "configUpdateTime": "2022-01-01T17:00:00-07:00", "configApplyTime": "1969-12-31T17:00:00-07:00", "domainName": "ga.atlanta.kabletown.net", @@ -5206,7 +5206,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "configUpdateTime": "2022-01-01T17:00:00-07:00", "configApplyTime": "1969-12-31T17:00:00-07:00", "domainName": "ga.atlanta.kabletown.net", @@ -5257,7 +5257,7 @@ }, { "cacheGroup": "cachegroup1", - "cdnName": "cdn1", + "cdn": "cdn1", "domainName": "ga.atlanta.kabletown.net", "guid": null, "hostName": "admin-down-server", From fde2d874332ecde6db70a6bcf5bd32209de566ab Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Mon, 21 Aug 2023 09:15:10 -0600 Subject: [PATCH 28/31] Fix unhelpful error message --- traffic_ops/traffic_ops_golang/server/servers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/traffic_ops/traffic_ops_golang/server/servers.go b/traffic_ops/traffic_ops_golang/server/servers.go index b6f470f686..f45b577746 100644 --- a/traffic_ops/traffic_ops_golang/server/servers.go +++ b/traffic_ops/traffic_ops_golang/server/servers.go @@ -416,7 +416,7 @@ func validateCommonV40(s *tc.ServerV40, tx *sql.Tx) ([]error, error) { } if len(s.ProfileNames) == 0 { - errs = append(errs, fmt.Errorf("no profiles exists")) + errs = append(errs, fmt.Errorf("a server must have at least one Profile")) } var cdnID int From 53ac3456fea5ada45cd767cdee3580da27b5f9b9 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Mon, 21 Aug 2023 09:29:15 -0600 Subject: [PATCH 29/31] Fix hard-coded v4 structures inside v5 tests --- traffic_ops/testing/api/v5/cdn_locks_test.go | 18 +-- traffic_ops/testing/api/v5/servers_test.go | 115 +++++++++---------- 2 files changed, 61 insertions(+), 72 deletions(-) diff --git a/traffic_ops/testing/api/v5/cdn_locks_test.go b/traffic_ops/testing/api/v5/cdn_locks_test.go index b2f4fe8395..f4f94a10c7 100644 --- a/traffic_ops/testing/api/v5/cdn_locks_test.go +++ b/traffic_ops/testing/api/v5/cdn_locks_test.go @@ -359,16 +359,16 @@ func TestCDNLocks(t *testing.T) { "OK when USER OWNS LOCK": { ClientSession: opsUserWithLockSession, RequestBody: generateServer(t, map[string]interface{}{ - "cdnId": GetCDNID(t, "cdn2")(), - "profileNames": []string{"EDGEInCDN2"}, + "cdnID": GetCDNID(t, "cdn2")(), + "profiles": []string{"EDGEInCDN2"}, }), Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusCreated)), }, "FORBIDDEN when ADMIN USER DOESNT OWN LOCK": { ClientSession: TOSession, RequestBody: generateServer(t, map[string]interface{}{ - "cdnId": GetCDNID(t, "cdn2")(), - "profileNames": []string{"EDGEInCDN2"}, + "cdnID": GetCDNID(t, "cdn2")(), + "profiles": []string{"EDGEInCDN2"}, "interfaces": []map[string]interface{}{{ "ipAddresses": []map[string]interface{}{{ "address": "127.0.0.2/30", @@ -385,9 +385,9 @@ func TestCDNLocks(t *testing.T) { EndpointID: GetServerID(t, "edge1-cdn2"), ClientSession: opsUserWithLockSession, RequestBody: generateServer(t, map[string]interface{}{ - "id": GetServerID(t, "edge1-cdn2")(), - "cdnId": GetCDNID(t, "cdn2")(), - "profileNames": []string{"EDGEInCDN2"}, + "id": GetServerID(t, "edge1-cdn2")(), + "cdnID": GetCDNID(t, "cdn2")(), + "profiles": []string{"EDGEInCDN2"}, "interfaces": []map[string]interface{}{{ "ipAddresses": []map[string]interface{}{{ "address": "0.0.0.1", @@ -403,9 +403,9 @@ func TestCDNLocks(t *testing.T) { ClientSession: TOSession, RequestBody: generateServer(t, map[string]interface{}{ "id": GetServerID(t, "dtrc-edge-07")(), - "cdnId": GetCDNID(t, "cdn2")(), + "cdnID": GetCDNID(t, "cdn2")(), "cachegroupId": GetCacheGroupId(t, "dtrc2")(), - "profileNames": []string{"CDN2_EDGE"}, + "profiles": []string{"CDN2_EDGE"}, "interfaces": []map[string]interface{}{{ "ipAddresses": []map[string]interface{}{{ "address": "192.0.2.11/24", diff --git a/traffic_ops/testing/api/v5/servers_test.go b/traffic_ops/testing/api/v5/servers_test.go index 4e8a0efc22..56a6886cb1 100644 --- a/traffic_ops/testing/api/v5/servers_test.go +++ b/traffic_ops/testing/api/v5/servers_test.go @@ -58,13 +58,13 @@ func TestServers(t *testing.T) { ClientSession: TOSession, RequestOpts: client.RequestOptions{QueryParameters: url.Values{"cachegroup": {strconv.Itoa(GetCacheGroupId(t, "cachegroup1")())}}}, Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseLengthGreaterOrEqual(1), - validateServerFields(map[string]interface{}{"CachegroupID": GetCacheGroupId(t, "cachegroup1")()})), + validateServerFields(map[string]interface{}{"CacheGroupID": GetCacheGroupId(t, "cachegroup1")()})), }, "OK when VALID CACHEGROUPNAME parameter": { ClientSession: TOSession, RequestOpts: client.RequestOptions{QueryParameters: url.Values{"cachegroupName": {"topology-mid-cg-01"}}}, Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), utils.ResponseLengthGreaterOrEqual(1), - validateServerFields(map[string]interface{}{"Cachegroup": "topology-mid-cg-01"})), + validateServerFields(map[string]interface{}{"CacheGroup": "topology-mid-cg-01"})), }, "OK when VALID CDN parameter": { ClientSession: TOSession, @@ -164,7 +164,7 @@ func TestServers(t *testing.T) { "POST": { "BAD REQUEST when BLANK PROFILENAMES": { ClientSession: TOSession, - RequestBody: generateServer(t, map[string]interface{}{"profileNames": []string{""}}), + RequestBody: generateServer(t, map[string]interface{}{"profiles": []string{""}}), Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)), }, }, @@ -174,8 +174,8 @@ func TestServers(t *testing.T) { ClientSession: TOSession, RequestBody: map[string]interface{}{ "id": GetServerID(t, "atlanta-edge-03")(), - "cdnId": GetCDNID(t, "cdn1")(), - "cachegroupId": GetCacheGroupId(t, "cachegroup1")(), + "cdnID": GetCDNID(t, "cdn1")(), + "cacheGroupID": GetCacheGroupId(t, "cachegroup1")(), "domainName": "updateddomainname", "hostName": "atl-edge-01", "httpsPort": 8080, @@ -198,18 +198,18 @@ func TestServers(t *testing.T) { "routerHostName": "router5", "routerPort": "9004", }}, - "physLocationId": GetPhysicalLocationID(t, "Denver")(), - "profileNames": []string{"EDGE1"}, - "rack": "RR 119.03", - "statusId": GetStatusID(t, "REPORTED")(), - "tcpPort": 8080, - "typeId": GetTypeId(t, "EDGE"), - "updPending": true, + "physicalLocationID": GetPhysicalLocationID(t, "Denver")(), + "profiles": []string{"EDGE1"}, + "rack": "RR 119.03", + "statusID": GetStatusID(t, "REPORTED")(), + "tcpPort": 8080, + "typeID": GetTypeId(t, "EDGE"), + "updPending": true, }, Expectations: utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK), validateServerFieldsForUpdate("atl-edge-01", map[string]interface{}{ - "CDNName": "cdn1", "Cachegroup": "cachegroup1", "DomainName": "updateddomainname", "HostName": "atl-edge-01", - "HTTPSPort": 8080, "InterfaceName": "bond1", "MTU": uint64(1280), "PhysLocation": "Denver", "Rack": "RR 119.03", + "CDN": "cdn1", "CacheGroup": "cachegroup1", "DomainName": "updateddomainname", "HostName": "atl-edge-01", + "HTTPSPort": 8080, "InterfaceName": "bond1", "MTU": uint64(1280), "PhysicalLocation": "Denver", "Rack": "RR 119.03", "TCPPort": 8080, "TypeID": GetTypeId(t, "EDGE"), })), }, @@ -227,8 +227,8 @@ func TestServers(t *testing.T) { ClientSession: TOSession, RequestBody: generateServer(t, map[string]interface{}{ "id": GetServerID(t, "test-ds-server-assignments")(), - "cachegroupId": GetCacheGroupId(t, "cachegroup1")(), - "typeId": GetTypeId(t, "MID"), + "cacheGroupID": GetCacheGroupId(t, "cachegroup1")(), + "typeID": GetTypeId(t, "MID"), }), Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusConflict)), }, @@ -237,7 +237,7 @@ func TestServers(t *testing.T) { ClientSession: TOSession, RequestBody: generateServer(t, map[string]interface{}{ "id": GetServerID(t, "test-ds-server-assignments")(), - "statusId": GetStatusID(t, "ADMIN_DOWN")(), + "statusID": GetStatusID(t, "ADMIN_DOWN")(), }), Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusConflict)), }, @@ -246,7 +246,7 @@ func TestServers(t *testing.T) { ClientSession: TOSession, RequestBody: generateServer(t, map[string]interface{}{ "id": GetServerID(t, "test-mso-org-01")(), - "statusId": GetStatusID(t, "ADMIN_DOWN")(), + "statusID": GetStatusID(t, "ADMIN_DOWN")(), }), Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusConflict)), }, @@ -255,9 +255,9 @@ func TestServers(t *testing.T) { ClientSession: TOSession, RequestBody: generateServer(t, map[string]interface{}{ "id": GetServerID(t, "midInTopologyMidCg01")(), - "cdnId": GetCDNID(t, "cdn1")(), - "profileNames": []string{"MID1"}, - "cachegroupId": GetCacheGroupId(t, "topology-mid-cg-01")(), + "cdnID": GetCDNID(t, "cdn1")(), + "profiles": []string{"MID1"}, + "cacheGroupID": GetCacheGroupId(t, "topology-mid-cg-01")(), }), Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)), }, @@ -267,9 +267,9 @@ func TestServers(t *testing.T) { RequestBody: generateServer(t, map[string]interface{}{ "id": GetServerID(t, "midInTopologyMidCg01")(), "hostName": "midInTopologyMidCg01", - "cdnId": GetCDNID(t, "cdn2")(), - "profileNames": []string{"CDN2_MID"}, - "cachegroupId": GetCacheGroupId(t, "topology-mid-cg-02")(), + "cdnID": GetCDNID(t, "cdn2")(), + "profiles": []string{"CDN2_MID"}, + "cacheGroupID": GetCacheGroupId(t, "topology-mid-cg-02")(), }), Expectations: utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)), }, @@ -277,7 +277,7 @@ func TestServers(t *testing.T) { EndpointID: GetServerID(t, "atlanta-edge-16"), ClientSession: TOSession, RequestBody: generateServer(t, map[string]interface{}{ - "profileNames": []string{"EDGE1"}, + "profiles": []string{"EDGE1"}, "interfaces": []map[string]interface{}{{ "ipAddresses": []map[string]interface{}{{ "address": "127.0.0.11/22", @@ -395,28 +395,22 @@ 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.ServerV4) + serverResp := resp.([]tc.ServerV5) for field, expected := range expectedResp { for _, server := range serverResp { switch field { - case "CachegroupID": - assert.RequireNotNil(t, server.CachegroupID, "Expected CachegroupID to not be nil") - assert.Equal(t, expected, *server.CachegroupID, "Expected CachegroupID to be %d, but got %d", expected, *server.CachegroupID) - case "Cachegroup": - assert.RequireNotNil(t, server.Cachegroup, "Expected Cachegroup to not be nil") - assert.Equal(t, expected, *server.Cachegroup, "Expected Cachegroup to be %s, but got %s", expected, *server.Cachegroup) - case "CDNName": - assert.RequireNotNil(t, server.CDNName, "Expected CDNName to not be nil") - assert.Equal(t, expected, *server.CDNName, "Expected CDNName to be %s, but got %s", expected, *server.CDNName) + case "CacheGroupID": + assert.Equal(t, expected, server.CacheGroupID, "Expected CacheGroupID to be %d, but got %d", expected, server.CacheGroupID) + case "CacheGroup": + assert.Equal(t, expected, server.CacheGroup, "Expected CacheGroup to be %s, but got %s", expected, server.CacheGroup) + case "CDN": + assert.Equal(t, expected, server.CDN, "Expected CDN to be %s, but got %s", expected, server.CDN) case "CDNID": - assert.RequireNotNil(t, server.CDNID, "Expected CDNID to not be nil") - assert.Equal(t, expected, *server.CDNID, "Expected CDNID to be %d, but got %d", expected, *server.CDNID) + assert.Equal(t, expected, server.CDNID, "Expected CDNID to be %d, but got %d", expected, server.CDNID) case "DomainName": - assert.RequireNotNil(t, server.DomainName, "Expected DomainName to not be nil") - assert.Equal(t, expected, *server.DomainName, "Expected DomainName to be %s, but got %s", expected, *server.DomainName) + assert.Equal(t, expected, server.DomainName, "Expected DomainName to be %s, but got %s", expected, server.DomainName) case "HostName": - assert.RequireNotNil(t, server.HostName, "Expected HostName to not be nil") - assert.Equal(t, expected, *server.HostName, "Expected HostName to be %s, but got %s", expected, *server.HostName) + assert.Equal(t, expected, server.HostName, "Expected HostName to be %s, but got %s", expected, server.HostName) case "HTTPSPort": assert.RequireNotNil(t, server.HTTPSPort, "Expected HTTPSPort to not be nil") assert.Equal(t, expected, *server.HTTPSPort, "Expected HTTPSPort to be %d, but got %d", expected, *server.HTTPSPort) @@ -427,25 +421,22 @@ func validateServerFields(expectedResp map[string]interface{}) utils.CkReqFunc { assert.RequireGreaterOrEqual(t, len(server.Interfaces), 1, "Expected Interfaces to have at least 1 interface") assert.RequireNotNil(t, server.Interfaces[0].MTU, "Expected MTU to not be nil") assert.Equal(t, expected, *server.Interfaces[0].MTU, "Expected MTU to be %d, but got %d", expected, *server.Interfaces[0].MTU) - case "PhysLocation": - assert.RequireNotNil(t, server.PhysLocation, "Expected PhysLocation to not be nil") - assert.Equal(t, expected, *server.PhysLocation, "Expected PhysLocation to be %s, but got %s", expected, *server.PhysLocation) - case "ProfileNames": - assert.Exactly(t, expected, server.ProfileNames, "Expected ProfileNames to be %v, but got %v", expected, server.ProfileNames) + case "PhysicalLocation": + assert.Equal(t, expected, server.PhysicalLocation, "Expected Physical Location to be %s, but got %s", expected, server.PhysicalLocation) + case "Profiles": + assert.Exactly(t, expected, server.Profiles, "Expected Profiles to be %v, but got %v", expected, server.Profiles) case "Rack": assert.RequireNotNil(t, server.Rack, "Expected Rack to not be nil") assert.Equal(t, expected, *server.Rack, "Expected Rack to be %s, but got %s", expected, *server.Rack) case "Status": - assert.RequireNotNil(t, server.Status, "Expected Status to not be nil") - assert.Equal(t, expected, *server.Status, "Expected Status to be %s, but got %s", expected, *server.Status) + assert.Equal(t, expected, server.Status, "Expected Status to be %s, but got %s", expected, server.Status) case "TCPPort": assert.RequireNotNil(t, server.TCPPort, "Expected TCPPort to not be nil") assert.Equal(t, expected, *server.TCPPort, "Expected TCPPort to be %d, but got %d", expected, *server.TCPPort) case "Type": assert.Equal(t, expected, server.Type, "Expected Type to be %s, but got %s", expected, server.Type) case "TypeID": - assert.RequireNotNil(t, server.TypeID, "Expected TypeID to not be nil") - assert.Equal(t, expected, *server.TypeID, "Expected Type to be %d, but got %d", expected, *server.TypeID) + assert.Equal(t, expected, server.TypeID, "Expected Type to be %d, but got %d", expected, server.TypeID) case "XMPPPasswd": assert.RequireNotNil(t, server.XMPPPasswd, "Expected XMPPPasswd to not be nil") assert.Equal(t, expected, *server.XMPPPasswd, "Expected XMPPPasswd to be %s, but got %s", expected, *server.XMPPPasswd) @@ -474,12 +465,11 @@ 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.ServerV4) + serverResp := resp.([]tc.ServerV5) var notInResponse []string serverMap := make(map[string]struct{}) for _, server := range serverResp { - assert.RequireNotNil(t, server.HostName, "Expected server host name to not be nil.") - serverMap[*server.HostName] = struct{}{} + serverMap[server.HostName] = struct{}{} } for _, expected := range expectedHostnames { if _, exists := serverMap[expected]; !exists { @@ -493,10 +483,9 @@ 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.ServerV4) + serverResp := resp.([]tc.ServerV5) 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) + assert.NotEqual(t, server.Type, tc.CacheTypeMid.String(), "Expected to find no %s-typed servers but found server %s", tc.CacheTypeMid, server.HostName) } } } @@ -504,7 +493,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.ServerV4) + paginationResp := resp.([]tc.ServerV5) opts := client.NewRequestOptions() opts.QueryParameters.Set("orderby", "id") respBase, _, err := TOSession.GetServers(opts) @@ -526,8 +515,8 @@ func validateServerPagination(paginationParam string) utils.CkReqFunc { func generateServer(t *testing.T, requestServer map[string]interface{}) map[string]interface{} { // map for the most basic Server a user can create genericServer := map[string]interface{}{ - "cdnId": GetCDNID(t, "cdn1")(), - "cachegroupId": GetCacheGroupId(t, "cachegroup1")(), + "cdnID": GetCDNID(t, "cdn1")(), + "cacheGroupID": GetCacheGroupId(t, "cachegroup1")(), "domainName": "localhost", "hostName": "testserver", "interfaces": []map[string]interface{}{{ @@ -537,10 +526,10 @@ func generateServer(t *testing.T, requestServer map[string]interface{}) map[stri }}, "name": "eth0", }}, - "physLocationId": GetPhysicalLocationID(t, "Denver")(), - "profileNames": []string{"EDGE1"}, - "statusId": GetStatusID(t, "REPORTED")(), - "typeId": GetTypeId(t, "EDGE"), + "physicalLocationID": GetPhysicalLocationID(t, "Denver")(), + "profiles": []string{"EDGE1"}, + "statusID": GetStatusID(t, "REPORTED")(), + "typeID": GetTypeId(t, "EDGE"), } for k, v := range requestServer { From caf8f6c1487fefdfd704d9b418fc4ca2411fa6b1 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Tue, 22 Aug 2023 13:18:17 -0600 Subject: [PATCH 30/31] Remove unused function --- traffic_ops/v5-client/server.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/traffic_ops/v5-client/server.go b/traffic_ops/v5-client/server.go index 6e20e57bbb..e88032bd70 100644 --- a/traffic_ops/v5-client/server.go +++ b/traffic_ops/v5-client/server.go @@ -31,10 +31,6 @@ const ( apiServers = "/servers" ) -func needAndCanFetch(id *int, name *string) bool { - return (id == nil || *id == 0) && name != nil && *name != "" -} - // CreateServer creates the given Server. func (to *Session) CreateServer(server tc.ServerV5, opts RequestOptions) (tc.Alerts, toclientlib.ReqInf, error) { var alerts tc.Alerts From 0a9f4d4d93c7f84370bb75622b01e91578777544 Mon Sep 17 00:00:00 2001 From: ocket8888 Date: Fri, 25 Aug 2023 12:46:37 -0600 Subject: [PATCH 31/31] Use version methods for comparisons (where possible) --- .../traffic_ops_golang/server/servers.go | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/traffic_ops/traffic_ops_golang/server/servers.go b/traffic_ops/traffic_ops_golang/server/servers.go index f45b577746..dfee235f90 100644 --- a/traffic_ops/traffic_ops_golang/server/servers.go +++ b/traffic_ops/traffic_ops_golang/server/servers.go @@ -681,7 +681,7 @@ func Read(inf *api.APIInfo) (int, error, error) { if userErr != nil || sysErr != nil { return errCode, userErr, sysErr } - if version.Major >= 5 { + if version.GreaterThanOrEqualTo(&api.Version{Major: 5}) { return inf.WriteOKResponse(servers) } @@ -758,7 +758,7 @@ func getServers(h http.Header, params map[string]string, tx *sqlx.Tx, user *auth "dsId": {Column: "dss.deliveryservice", Checker: nil}, } - if version.Major >= 4 { + if version.GreaterThanOrEqualTo(&api.Version{Major: 4}) { queryParamsToSQLCols["profileName"] = dbhelpers.WhereColumnInfo{ Column: "sp.profile_name", Checker: nil, @@ -834,7 +834,7 @@ func getServers(h http.Header, params map[string]string, tx *sqlx.Tx, user *auth var queryString, countQueryString string queryString = selectQuery countQueryString = serverCountQuery - if version.Major >= 4 { + if version.GreaterThanOrEqualTo(&api.Version{Major: 4}) { if _, ok := params["profileName"]; ok { queryString = queryString + ` JOIN server_profile sp ON s.id = sp.server` @@ -1305,7 +1305,7 @@ func Update(inf *api.APIInfo) (int, error, error) { var statusLastUpdatedTime time.Time tx := inf.Tx.Tx - if inf.Version.Major >= 5 { + if inf.Version.GreaterThanOrEqualTo(&api.Version{Major: 5}) { server.ID = inf.IntParams["id"] if err := inf.DecodeBody(&server); err != nil { return http.StatusBadRequest, err, nil @@ -1327,7 +1327,7 @@ func Update(inf *api.APIInfo) (int, error, error) { return http.StatusBadRequest, userErr, sysErr } server = tmp.Upgrade() - } else if inf.Version.Major >= 4 { + } else if inf.Version.GreaterThanOrEqualTo(&api.Version{Major: 4}) { var serverV4 tc.ServerV4 serverV4.ID = util.Ptr(inf.IntParams["id"]) if err := inf.DecodeBody(&serverV4); err != nil { @@ -1451,7 +1451,7 @@ func Update(inf *api.APIInfo) (int, error, error) { } } - if inf.Version.Major >= 4 { + if inf.Version.GreaterThanOrEqualTo(&api.Version{Major: 4}) { if err = dbhelpers.UpdateServerProfilesForV4(server.ID, server.Profiles, tx); err != nil { userErr, sysErr, errCode := api.ParseDBError(err) return errCode, userErr, sysErr @@ -1477,7 +1477,7 @@ func Update(inf *api.APIInfo) (int, error, error) { where := `WHERE s.id = $1` var selquery string - if inf.Version.Major <= 4 { + if inf.Version.Major == 4 || inf.Version.LessThan(&api.Version{Major: 4}) { selquery = selectQuery + joinProfileV4 + where } else { selquery = selectQuery + where @@ -1537,11 +1537,11 @@ func Update(inf *api.APIInfo) (int, error, error) { if userErr, sysErr, errCode = updateStatusLastUpdatedTime(id, &statusLastUpdatedTime, tx); userErr != nil || sysErr != nil { return errCode, userErr, sysErr } - if inf.Version.Major >= 5 { + if inf.Version.GreaterThanOrEqualTo(&api.Version{Major: 5}) { inf.WriteSuccessResponse(server, "Server updated") - } else if inf.Version.Major >= 4 { + } else if inf.Version.GreaterThanOrEqualTo(&api.Version{Major: 4}) { inf.WriteSuccessResponse(server.Downgrade(), "Server updated") - } else if inf.Version.Major >= 3 { + } else if inf.Version.GreaterThanOrEqualTo(&api.Version{Major: 3}) { downgraded := server.Downgrade() csp, err := dbhelpers.GetCommonServerPropertiesFromV4(downgraded, inf.Tx.Tx) if err != nil { @@ -2281,12 +2281,12 @@ func Delete(inf *api.APIInfo) (int, error, error) { } inf.CreateChangeLog(fmt.Sprintf("SERVER: %s.%s, ID: %d, ACTION: deleted", server.HostName, server.DomainName, server.ID)) - if inf.Version.Major >= 5 { + if inf.Version.GreaterThanOrEqualTo(&api.Version{Major: 5}) { return inf.WriteSuccessResponse(server, "Server deleted") } downgraded := server.Downgrade() - if inf.Version.Major >= 4 { + if inf.Version.GreaterThanOrEqualTo(&api.Version{Major: 4}) { return inf.WriteSuccessResponse(downgraded, "Server deleted") }