func updateV50()

in traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go [1004:1310]


func updateV50(w http.ResponseWriter, r *http.Request, inf *api.Info, ds *tc.DeliveryServiceV5, omitExtraLongDescFields bool, longDesc1, longDesc2 *string) (*tc.DeliveryServiceV5, int, error, error) {
	tx := inf.Tx.Tx
	user := inf.User
	userErr, sysErr := Validate(tx, ds)
	if userErr != nil {
		return nil, http.StatusBadRequest, fmt.Errorf("invalid request: %w", userErr), nil
	}
	if sysErr != nil {
		return nil, http.StatusInternalServerError, nil, sysErr
	}

	if authorized, err := isTenantAuthorized(inf, ds); err != nil {
		return nil, http.StatusInternalServerError, nil, fmt.Errorf("checking tenant: %w", err)
	} else if !authorized {
		return nil, http.StatusForbidden, errors.New("not authorized on this tenant"), nil
	}

	if ds.ID == nil {
		return nil, http.StatusBadRequest, errors.New("missing id"), nil
	}

	dsType, ok, err := getDSType(tx, ds.XMLID)
	if !ok {
		return nil, http.StatusNotFound, errors.New("delivery service '" + ds.XMLID + "' not found"), nil
	}
	if err != nil {
		return nil, http.StatusInternalServerError, nil, fmt.Errorf("getting delivery service type during update: %w", err)
	}

	var errCode int
	var oldDetails TODeliveryServiceOldDetails
	if dsType.HasSSLKeys() {
		oldDetails, userErr, sysErr, errCode = getOldDetails(*ds.ID, tx)
		if userErr != nil || sysErr != nil {
			return nil, errCode, userErr, sysErr
		}
		sslKeysExist, err := getSSLVersion(ds.XMLID, tx)
		if err != nil {
			return nil, http.StatusInternalServerError, nil, fmt.Errorf("querying delivery service with sslKeyVersion failed: %w", err)
		}
		if sslKeysExist {
			errStr := "delivery service has ssl keys that cannot be automatically changed, therefore %s is immutable"
			if oldDetails.OldCDNID != ds.CDNID {
				return nil, http.StatusBadRequest, fmt.Errorf(errStr, "CDN ID"), nil
			}
			if ds.CDNName != nil && oldDetails.OldCDNName != *ds.CDNName {
				return nil, http.StatusBadRequest, fmt.Errorf(errStr, "CDN Name"), nil
			}
			if oldDetails.OldRoutingName != ds.RoutingName {
				return nil, http.StatusBadRequest, fmt.Errorf(errStr, "Routing Name"), nil
			}
			ds.SSLKeyVersion = oldDetails.OldSSLKeyVersion
		}
	}

	// TODO change DeepCachingType to implement sql.Valuer and sql.Scanner, so sqlx struct scan can be used.
	deepCachingType := tc.DeepCachingType("").String()
	if ds.DeepCachingType != "" {
		deepCachingType = ds.DeepCachingType.String() // necessary, because DeepCachingType's default needs to insert the string, not "", and Query doesn't call .String().
	}

	userErr, sysErr, errCode = api.CheckIfUnModified(r.Header, inf.Tx, *ds.ID, "deliveryservice")
	if userErr != nil || sysErr != nil {
		return nil, errCode, userErr, sysErr
	}

	if errCode, userErr, sysErr = dbhelpers.CheckTopology(inf.Tx, *ds); userErr != nil || sysErr != nil {
		return nil, errCode, userErr, sysErr
	}

	if ds.Topology != nil {
		if len(ds.RequiredCapabilities) > 0 {
			if userErr, sysErr, status := EnsureTopologyBasedRequiredCapabilities(tx, *ds.ID, *ds.Topology, ds.RequiredCapabilities); userErr != nil || sysErr != nil {
				return nil, status, userErr, sysErr
			}
		}

		userErr, sysErr, status := dbhelpers.CheckOriginServerInDSCG(tx, *ds.ID, *ds.Topology)
		if userErr != nil || sysErr != nil {
			return nil, status, userErr, sysErr
		}
	}

	var geoLimitCountries string
	if ds.GeoLimitCountries != nil {
		geoLimitCountries = strings.Join(ds.GeoLimitCountries, ",")
	}
	var resultRows *sql.Rows
	if omitExtraLongDescFields {
		if longDesc1 != nil || longDesc2 != nil {
			return nil, http.StatusBadRequest, errors.New("the longDesc1 and longDesc2 fields are no longer supported in API 4.0 onwards"), nil
		}
		resultRows, err = tx.Query(updateDSQueryWithoutLD1AndLD2(),
			ds.Active,
			ds.CCRDNSTTL,
			ds.CDNID,
			ds.CheckPath,
			deepCachingType,
			ds.DisplayName,
			ds.DNSBypassCNAME,
			ds.DNSBypassIP,
			ds.DNSBypassIP6,
			ds.DNSBypassTTL,
			ds.DSCP,
			ds.EdgeHeaderRewrite,
			ds.GeoLimitRedirectURL,
			ds.GeoLimit,
			geoLimitCountries,
			ds.GeoProvider,
			ds.GlobalMaxMBPS,
			ds.GlobalMaxTPS,
			ds.FQPacingRate,
			ds.HTTPBypassFQDN,
			ds.InfoURL,
			ds.InitialDispersion,
			ds.IPV6RoutingEnabled,
			ds.LogsEnabled,
			ds.LongDesc,
			ds.MaxDNSAnswers,
			ds.MidHeaderRewrite,
			ds.MissLat,
			ds.MissLong,
			ds.MultiSiteOrigin,
			ds.OriginShield,
			ds.ProfileID,
			ds.Protocol,
			ds.QStringIgnore,
			ds.RangeRequestHandling,
			ds.RegexRemap,
			ds.Regional,
			ds.RegionalGeoBlocking,
			ds.RemapText,
			ds.RoutingName,
			ds.SigningAlgorithm,
			ds.SSLKeyVersion,
			ds.TenantID,
			ds.TRRequestHeaders,
			ds.TRResponseHeaders,
			ds.TypeID,
			ds.XMLID,
			ds.AnonymousBlockingEnabled,
			ds.ConsistentHashRegex,
			ds.MaxOriginConnections,
			ds.EcsEnabled,
			ds.RangeSliceBlockSize,
			ds.Topology,
			ds.FirstHeaderRewrite,
			ds.InnerHeaderRewrite,
			ds.LastHeaderRewrite,
			ds.ServiceCategory,
			ds.MaxRequestHeaderBytes,
			pq.Array(ds.RequiredCapabilities),
			ds.ID,
		)
	} else {
		resultRows, err = tx.Query(updateDSQuery(),
			ds.Active,
			ds.CCRDNSTTL,
			ds.CDNID,
			ds.CheckPath,
			deepCachingType,
			ds.DisplayName,
			ds.DNSBypassCNAME,
			ds.DNSBypassIP,
			ds.DNSBypassIP6,
			ds.DNSBypassTTL,
			ds.DSCP,
			ds.EdgeHeaderRewrite,
			ds.GeoLimitRedirectURL,
			ds.GeoLimit,
			geoLimitCountries,
			ds.GeoProvider,
			ds.GlobalMaxMBPS,
			ds.GlobalMaxTPS,
			ds.FQPacingRate,
			ds.HTTPBypassFQDN,
			ds.InfoURL,
			ds.InitialDispersion,
			ds.IPV6RoutingEnabled,
			ds.LogsEnabled,
			ds.LongDesc,
			longDesc1,
			longDesc2,
			ds.MaxDNSAnswers,
			ds.MidHeaderRewrite,
			ds.MissLat,
			ds.MissLong,
			ds.MultiSiteOrigin,
			ds.OriginShield,
			ds.ProfileID,
			ds.Protocol,
			ds.QStringIgnore,
			ds.RangeRequestHandling,
			ds.RegexRemap,
			ds.Regional,
			ds.RegionalGeoBlocking,
			ds.RemapText,
			ds.RoutingName,
			ds.SigningAlgorithm,
			ds.SSLKeyVersion,
			ds.TenantID,
			ds.TRRequestHeaders,
			ds.TRResponseHeaders,
			ds.TypeID,
			ds.XMLID,
			ds.AnonymousBlockingEnabled,
			ds.ConsistentHashRegex,
			ds.MaxOriginConnections,
			ds.EcsEnabled,
			ds.RangeSliceBlockSize,
			ds.Topology,
			ds.FirstHeaderRewrite,
			ds.InnerHeaderRewrite,
			ds.LastHeaderRewrite,
			ds.ServiceCategory,
			ds.MaxRequestHeaderBytes,
			pq.Array(ds.RequiredCapabilities),
			ds.ID,
		)
	}

	if err != nil {
		usrErr, sysErr, code := api.ParseDBError(err)
		return nil, code, usrErr, sysErr
	}
	defer log.Close(resultRows, "updating Delivery Service")
	if !resultRows.Next() {
		return nil, http.StatusNotFound, errors.New("no delivery service found with this id"), nil
	}
	var lastUpdated time.Time
	if err := resultRows.Scan(&lastUpdated); err != nil {
		return nil, http.StatusInternalServerError, nil, fmt.Errorf("scan updating delivery service: %w", err)
	}
	if resultRows.Next() {
		return nil, http.StatusInternalServerError, nil, errors.New("updating delivery service " + ds.XMLID + ": this update affected too many rows: > 1")
	}
	if ds.ID == nil {
		return nil, http.StatusInternalServerError, nil, errors.New("missing id after update")
	}

	if len(ds.TLSVersions) < 1 {
		ds.TLSVersions = nil
	}
	err = recreateTLSVersions(ds.TLSVersions, *ds.ID, tx)
	if err != nil {
		return nil, http.StatusInternalServerError, nil, fmt.Errorf("updating TLS versions for DS #%d: %w", *ds.ID, err)
	}

	newDSType, err := getTypeFromID(ds.TypeID, tx)
	if err != nil {
		return nil, http.StatusInternalServerError, nil, fmt.Errorf("getting delivery service type after update: %w", err)
	}
	ds.Type = (*string)(&newDSType)

	cdnDomain, err := getCDNDomain(*ds.ID, tx) // need to get the domain again, in case it changed.
	if err != nil {
		return nil, http.StatusInternalServerError, nil, fmt.Errorf("getting CDN domain: (%s) after update: %w", cdnDomain, err)
	}

	matchLists, err := GetDeliveryServicesMatchLists([]string{ds.XMLID}, tx)
	if err != nil {
		return nil, http.StatusInternalServerError, nil, fmt.Errorf("getting matchlists after update: %w", err)
	}
	ml, ok := matchLists[ds.XMLID]
	if !ok {
		return nil, http.StatusInternalServerError, nil, errors.New("no matchlists after update")
	}
	ds.MatchList = ml

	if err := EnsureParams(tx, *ds.ID, ds.XMLID, ds.EdgeHeaderRewrite, ds.MidHeaderRewrite, ds.RegexRemap, ds.SigningAlgorithm, newDSType, ds.MaxOriginConnections); err != nil {
		return nil, http.StatusInternalServerError, nil, fmt.Errorf("ensuring ds parameters: %w", err)
	}

	if oldDetails.OldOrgServerFQDN != nil && ds.OrgServerFQDN != nil && *oldDetails.OldOrgServerFQDN != *ds.OrgServerFQDN {
		if err := updatePrimaryOrigin(tx, user, *ds); err != nil {
			return nil, http.StatusInternalServerError, nil, fmt.Errorf("updating delivery service: %w", err)
		}
	}

	ds.LastUpdated = lastUpdated

	// the update may change or delete the query params -- delete existing and re-add if any provided
	q := `DELETE FROM deliveryservice_consistent_hash_query_param WHERE deliveryservice_id = $1`
	if res, err := tx.Exec(q, *ds.ID); err != nil {
		return nil, http.StatusInternalServerError, nil, fmt.Errorf("deleting consistent hash query params for ds %s: %w", ds.XMLID, err)
	} else if c, _ := res.RowsAffected(); c > 0 {
		api.CreateChangeLogRawTx(api.ApiChange, "DS: "+ds.XMLID+", ID: "+strconv.Itoa(*ds.ID)+", ACTION: Deleted "+strconv.FormatInt(c, 10)+" consistent hash query params", user, tx)
	}

	if _, err = createConsistentHashQueryParams(tx, *ds.ID, ds.ConsistentHashQueryParams); err != nil {
		usrErr, sysErr, code := api.ParseDBError(err)
		return nil, code, usrErr, sysErr
	}

	if err := api.CreateChangeLogRawErr(api.ApiChange, "Updated ds: "+ds.XMLID+" id: "+strconv.Itoa(*ds.ID), user, tx); err != nil {
		return nil, http.StatusInternalServerError, nil, fmt.Errorf("writing change log entry: %w", err)
	}

	if inf.Config.TrafficVaultEnabled && ds.Protocol != nil && (*ds.Protocol == tc.DSProtocolHTTPS || *ds.Protocol == tc.DSProtocolHTTPAndHTTPS || *ds.Protocol == tc.DSProtocolHTTPToHTTPS) {
		err, errCode := GeneratePlaceholderSelfSignedCert(*ds, inf, r.Context())
		if err != nil || errCode != http.StatusOK {
			return nil, errCode, nil, fmt.Errorf("creating self signed default cert: %w", err)
		}
	}

	return ds, http.StatusOK, nil, nil
}