func Update()

in traffic_ops/traffic_ops_golang/server/servers.go [1276:1564]


func Update(inf *api.Info) (int, error, error) {
	id := inf.IntParams["id"]

	// Get original server
	originals, _, userErr, sysErr, errCode, _ := getServers(inf.RequestHeaders(), inf.Params, inf.Tx, inf.User, false, *inf.Version, inf.Config.RoleBasedPermissions)
	if userErr != nil || sysErr != nil {
		return errCode, userErr, sysErr
	}
	if len(originals) < 1 {
		return http.StatusNotFound, errors.New("the server doesn't exist, cannot update"), nil
	}
	if len(originals) > 1 {
		return http.StatusInternalServerError, nil, fmt.Errorf("too many servers by ID %d: %d", id, len(originals))
	}

	original := originals[0]
	if original.XMPPID == nil || *original.XMPPID == "" {
		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")
		original.StatusLastUpdated = util.Ptr(original.LastUpdated)
	}

	var originalXMPPID string
	if original.XMPPID != nil {
		originalXMPPID = *original.XMPPID
	}
	originalStatusID := original.StatusID

	var server tc.ServerV5
	var serverV3 tc.ServerV30
	var statusLastUpdatedTime time.Time
	tx := inf.Tx.Tx

	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
		}
		if server.StatusID != originalStatusID {
			currentTime := time.Now()
			server.StatusLastUpdated = &currentTime
			statusLastUpdatedTime = currentTime
		} else {
			server.StatusLastUpdated = original.StatusLastUpdated
			statusLastUpdatedTime = *original.StatusLastUpdated
		}
		tmp := server.Downgrade()
		_, userErr, sysErr := validateV4(&tmp, tx)
		if userErr != nil || sysErr != nil {
			if sysErr != nil {
				return http.StatusInternalServerError, userErr, sysErr
			}
			return http.StatusBadRequest, userErr, sysErr
		}
		server = tmp.Upgrade()
	} 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 {
			return http.StatusBadRequest, err, nil
		}
		if serverV4.StatusID == nil || *serverV4.StatusID != originalStatusID {
			currentTime := time.Now()
			server.StatusLastUpdated = &currentTime
			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 := inf.DecodeBody(&serverV3); err != nil {
			return http.StatusBadRequest, err, nil
		}
		if serverV3.StatusID != nil && *serverV3.StatusID != originalStatusID {
			currentTime := time.Now()
			serverV3.StatusLastUpdated = &currentTime
			statusLastUpdatedTime = currentTime
		} else {
			serverV3.StatusLastUpdated = original.StatusLastUpdated
			statusLastUpdatedTime = *original.StatusLastUpdated
		}
		_, userErr, sysErr := validateV3(&serverV3, tx)
		if userErr != nil || sysErr != nil {
			if sysErr != nil {
				return http.StatusInternalServerError, userErr, sysErr
			}
			return http.StatusBadRequest, userErr, sysErr
		}

		profileName, exists, err := dbhelpers.GetProfileNameFromID(*serverV3.ProfileID, tx)
		if err != nil {
			return http.StatusInternalServerError, nil, err
		}
		if !exists {
			return http.StatusNotFound, errors.New("profile does not exist"), nil
		}
		profileNames := []string{profileName}

		upgraded, err := serverV3.UpgradeToV40(profileNames)
		if err != nil {
			return http.StatusInternalServerError, nil, fmt.Errorf("error upgrading valid V3 server to V4 structure: %w", err)
		}
		server = upgraded.Upgrade()
	}

	if original.CacheGroupID != server.CacheGroupID || original.CDNID != server.CDNID {
		hasDSOnCDN, err := dbhelpers.CachegroupHasTopologyBasedDeliveryServicesOnCDN(tx, original.CacheGroupID, original.CDNID)
		if err != nil {
			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 {
			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 {
		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)
		return http.StatusBadRequest, fmt.Errorf("no such Status: #%d", server.StatusID), nil
	}
	if status.Name == nil {
		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 {
			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)
			return http.StatusConflict, errors.New(alertText), nil
		}
	}

	if userErr, sysErr, errCode = checkTypeChangeSafety(server, inf.Tx); userErr != nil || sysErr != nil {
		return errCode, userErr, sysErr
	}

	if server.XMPPID != nil && *server.XMPPID != "" && originalXMPPID != "" && *server.XMPPID != originalXMPPID {
		return http.StatusBadRequest, errors.New("server cannot be updated due to requested XMPPID change. XMPIDD is immutable"), nil
	}

	userErr, sysErr, statusCode := api.CheckIfUnModified(inf.RequestHeaders(), inf.Tx, server.ID, "server")
	if userErr != nil || sysErr != nil {
		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 {
			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
		}
	}

	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
		}
	} else {
		if err = dbhelpers.UpdateServerProfileTableForV3(serverV3.ID, serverV3.ProfileID, (original.Profiles)[0], tx); err != nil {
			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 {
		return errCode, userErr, sysErr
	}

	if userErr, sysErr, errCode = deleteInterfaces(id, tx); userErr != nil || sysErr != nil {
		return errCode, userErr, sysErr
	}

	if userErr, sysErr, errCode = createInterfaces(id, server.Interfaces, tx); userErr != nil || sysErr != nil {
		return errCode, userErr, sysErr
	}

	where := `WHERE s.id = $1`
	var selquery string
	if inf.Version.Major == 4 || inf.Version.LessThan(&api.Version{Major: 4}) {
		selquery = selectQuery + joinProfileV4 + where
	} else {
		selquery = selectQuery + 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.RevalUpdateFailed,
		&server.Status,
		&server.StatusID,
		&server.TCPPort,
		&server.Type,
		&server.TypeID,
		&server.ConfigUpdateTime,
		&server.ConfigApplyTime,
		&server.ConfigUpdateFailed,
		&server.XMPPID,
		&server.XMPPPasswd,
		&server.StatusLastUpdated,
	)
	if err != nil {
		return http.StatusInternalServerError, nil, err
	}

	serversInterfaces, err := dbhelpers.GetServersInterfaces([]int{server.ID}, inf.Tx.Tx)
	if err != nil {
		return http.StatusInternalServerError, nil, err
	}
	if interfacesMap, ok := serversInterfaces[server.ID]; ok {
		for _, intfc := range interfacesMap {
			server.Interfaces = append(server.Interfaces, intfc)
		}
	}

	if userErr, sysErr, errCode = updateStatusLastUpdatedTime(id, &statusLastUpdatedTime, tx); userErr != nil || sysErr != nil {
		return errCode, userErr, sysErr
	}
	if inf.Version.GreaterThanOrEqualTo(&api.Version{Major: 5}) {
		inf.WriteSuccessResponse(server, "Server updated")
	} else if inf.Version.GreaterThanOrEqualTo(&api.Version{Major: 4}) {
		inf.WriteSuccessResponse(server.Downgrade(), "Server updated")
	} else if inf.Version.GreaterThanOrEqualTo(&api.Version{Major: 3}) {
		downgraded := server.Downgrade()
		csp, err := dbhelpers.GetCommonServerPropertiesFromV4(downgraded, inf.Tx.Tx)
		if err != nil {
			return http.StatusInternalServerError, nil, err
		}
		serverV30, err := downgraded.ToServerV3FromV4(csp)
		if err != nil {
			return http.StatusInternalServerError, nil, err
		}
		inf.WriteSuccessResponse(serverV30, "Server updated")
	}

	inf.CreateChangeLog(fmt.Sprintf("SERVER: %s.%s, ID: %d, ACTION: updated", server.HostName, server.DomainName, server.ID))
	return http.StatusOK, nil, nil
}