in traffic_ops/traffic_ops_golang/server/servers.go [460:563]
func validateV4(s *tc.ServerV40, tx *sql.Tx) (string, error, error) {
if len(s.Interfaces) == 0 {
return "", errors.New("a server must have at least one interface"), nil
}
var errs []error
var serviceAddrV4Found bool
var ipv4 string
var serviceAddrV6Found bool
var ipv6 string
var serviceInterface string
for _, iface := range s.Interfaces {
ruleName := fmt.Sprintf("interface '%s' ", iface.Name)
errs = append(errs, tovalidate.ToErrors(validation.Errors{
ruleName + "name": validation.Validate(iface.Name, validation.Required),
ruleName + "mtu": validation.Validate(iface.MTU, validation.By(validateMTU)),
ruleName + "ipAddresses": validation.Validate(iface.IPAddresses, validation.Required),
})...)
for _, addr := range iface.IPAddresses {
ruleName += fmt.Sprintf("address '%s'", addr.Address)
var parsedIP net.IP
var err error
if parsedIP, _, err = net.ParseCIDR(addr.Address); err != nil {
if parsedIP = net.ParseIP(addr.Address); parsedIP == nil {
errs = append(errs, fmt.Errorf("%s: address: %v", ruleName, err))
continue
}
}
if addr.Gateway != nil {
if gateway := net.ParseIP(*addr.Gateway); gateway == nil {
errs = append(errs, fmt.Errorf("%s: gateway: could not parse '%s' as a network gateway", ruleName, *addr.Gateway))
} else if (gateway.To4() == nil && parsedIP.To4() != nil) || (gateway.To4() != nil && parsedIP.To4() == nil) {
errs = append(errs, errors.New(ruleName+": address family mismatch between address and gateway"))
}
}
if addr.ServiceAddress {
if serviceInterface != "" && serviceInterface != iface.Name {
errs = append(errs, fmt.Errorf("interfaces: both %s and %s interfaces contain service addresses - only one service-address-containing-interface is allowed", serviceInterface, iface.Name))
}
serviceInterface = iface.Name
if parsedIP.To4() != nil {
if serviceAddrV4Found {
errs = append(errs, fmt.Errorf("interfaces: address '%s' of interface '%s' is marked as a service address, but an IPv4 service address appears earlier in the list", addr.Address, iface.Name))
}
serviceAddrV4Found = true
ipv4 = addr.Address
} else {
if serviceAddrV6Found {
errs = append(errs, fmt.Errorf("interfaces: address '%s' of interface '%s' is marked as a service address, but an IPv6 service address appears earlier in the list", addr.Address, iface.Name))
}
serviceAddrV6Found = true
ipv6 = addr.Address
}
}
}
}
if !serviceAddrV6Found && !serviceAddrV4Found {
errs = append(errs, errors.New("a server must have at least one service address"))
}
usrErr, sysErr := validateCommonV40(s, tx)
errs = append(errs, usrErr...)
if sysErr != nil || len(errs) > 0 {
return serviceInterface, util.JoinErrs(errs), sysErr
}
query := `
SELECT tmp.server, ip.address
FROM (
SELECT server, ARRAY_AGG(profile_name order by priority) AS profiles
FROM server_profile
GROUP BY server
) AS tmp
JOIN ip_address ip on ip.server = tmp.server
WHERE (profiles = $1::text[])
`
var rows *sql.Rows
var err error
//ProfileID already validated
if s.ID != nil {
rows, err = tx.Query(query+" and tmp.server != $2", pq.Array(s.ProfileNames), *s.ID)
} else {
rows, err = tx.Query(query, pq.Array(s.ProfileNames))
}
if err != nil {
errs = append(errs, errors.New("unable to determine service address uniqueness"))
} else if rows != nil {
defer rows.Close()
for rows.Next() {
var id int
var ipaddress string
err = rows.Scan(&id, &ipaddress)
if err != nil {
errs = append(errs, errors.New("unable to determine service address uniqueness"))
} else if (ipaddress == ipv4 || ipaddress == ipv6) && (s.ID == nil || *s.ID != id) {
errs = append(errs, fmt.Errorf("there exists a server with id %v on the same profile that has the same service address %s", id, ipaddress))
}
}
}
return serviceInterface, util.JoinErrs(errs), nil
}