in traffic_ops/traffic_ops_golang/server/servers.go [565:669]
func validateV3(s *tc.ServerV30, 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"))
}
commonErrs, sysErr := validateCommon(&s.CommonServerProperties, tx)
errs = append(errs, commonErrs...)
if len(errs) > 0 || sysErr != nil {
return serviceInterface, util.JoinErrs(errs), sysErr
}
query := `
SELECT s.ID, ip.address FROM server s
JOIN profile p on p.Id = s.Profile
JOIN interface i on i.server = s.ID
JOIN ip_address ip on ip.Server = s.ID and ip.interface = i.name
WHERE ip.service_address = true
and p.id = $1
`
var rows *sql.Rows
var err error
//ProfileID already validated
if s.ID != nil {
rows, err = tx.Query(query+" and s.id != $2", *s.ProfileID, *s.ID)
} else {
rows, err = tx.Query(query, *s.ProfileID)
}
if err != nil {
return serviceInterface, util.JoinErrs(errs), fmt.Errorf("unable to determine service address uniqueness: querying: %w", err)
} else if rows != nil {
defer rows.Close()
for rows.Next() {
var id int
var ipaddress string
err = rows.Scan(&id, &ipaddress)
if err != nil {
return serviceInterface, util.JoinErrs(errs), fmt.Errorf("unable to determine service address uniqueness: scanning: %w", err)
} 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
}