rawlinp commented on a change in pull request #5345:
URL: https://github.com/apache/trafficcontrol/pull/5345#discussion_r543803113



##########
File path: lib/go-tc/deliveryservices.go
##########
@@ -428,8 +445,8 @@ func (ds *DeliveryServiceNullable) validateTypeFields(tx 
*sql.Tx) error {
                                        if *ds.RangeRequestHandling == 3 {
                                                return 
validation.Validate(ds.RangeSliceBlockSize, validation.Required,
                                                        // Per Slice Plugin 
implementation
-                                                       validation.Min(262144), 
  // 256KiB
-                                                       
validation.Max(33554432), // 32MiB
+                                                       
validation.Min(MinRangeSliceBlockSize), // 256KiB

Review comment:
       We should do some basic validation for `MaxRequestHeaderBytes` as well 
-- like it should be greater than zero. 

##########
File path: traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
##########
@@ -699,62 +759,301 @@ WHERE
 }
 
 func updateV15(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, reqDS 
*tc.DeliveryServiceNullableV15) (*tc.DeliveryServiceNullableV15, int, error, 
error) {
-       dsV30 := tc.DeliveryServiceNullableV30{DeliveryServiceNullableV15: 
*reqDS}
+       dsV31 := tc.DeliveryServiceV31{
+               DeliveryServiceV30: tc.DeliveryServiceV30{
+                       DeliveryServiceNullableV15: *reqDS,
+               },
+       }
        // query the DB for existing 3.0 fields in order to "upgrade" this 1.5 
request into a 3.0 request
        query := `
 SELECT
   ds.topology,
   ds.first_header_rewrite,
   ds.inner_header_rewrite,
   ds.last_header_rewrite,
-  ds.service_category
+  ds.service_category,
+  ds.max_request_header_bytes
 FROM
   deliveryservice ds
 WHERE
   ds.id = $1`
        if err := inf.Tx.Tx.QueryRow(query, *reqDS.ID).Scan(
-               &dsV30.Topology,
-               &dsV30.FirstHeaderRewrite,
-               &dsV30.InnerHeaderRewrite,
-               &dsV30.LastHeaderRewrite,
-               &dsV30.ServiceCategory,
+               &dsV31.Topology,
+               &dsV31.FirstHeaderRewrite,
+               &dsV31.InnerHeaderRewrite,
+               &dsV31.LastHeaderRewrite,
+               &dsV31.ServiceCategory,
+               &dsV31.MaxRequestHeaderBytes,
        ); err != nil {
                if err == sql.ErrNoRows {
-                       return nil, http.StatusNotFound, fmt.Errorf("delivery 
service ID %d not found", *dsV30.ID), nil
+                       return nil, http.StatusNotFound, fmt.Errorf("delivery 
service ID %d not found", *dsV31.ID), nil
                }
-               return nil, http.StatusInternalServerError, nil, 
fmt.Errorf("querying delivery service ID %d: %s", *dsV30.ID, err.Error())
+               return nil, http.StatusInternalServerError, nil, 
fmt.Errorf("querying delivery service ID %d: %s", *dsV31.ID, err.Error())
        }
-       res, status, userErr, sysErr := updateV30(w, r, inf, &dsV30)
+       res, status, userErr, sysErr := updateV31(w, r, inf, &dsV31)
        if res != nil {
                return &res.DeliveryServiceNullableV15, status, userErr, sysErr
        }
        return nil, status, userErr, sysErr
 }
 
-func updateV30(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, ds 
*tc.DeliveryServiceNullableV30) (*tc.DeliveryServiceNullableV30, int, error, 
error) {
+func updateV31(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, ds 
*tc.DeliveryServiceV31) (*tc.DeliveryServiceV31, int, error, error) {
        tx := inf.Tx.Tx
        user := inf.User
+       dsNullableV30 := tc.DeliveryServiceNullableV30(*ds)
+       if err := dsNullableV30.Validate(tx); err != nil {
+               return nil, http.StatusBadRequest, errors.New("invalid request: 
" + err.Error()), nil
+       }
+
+       if authorized, err := isTenantAuthorized(inf, &dsNullableV30); err != 
nil {
+               return nil, http.StatusInternalServerError, nil, 
errors.New("checking tenant: " + err.Error())
+       } else if !authorized {
+               return nil, http.StatusForbidden, errors.New("not authorized on 
this tenant"), nil
+       }
+
+       if dsNullableV30.XMLID == nil {
+               return nil, http.StatusBadRequest, errors.New("missing 
xml_id"), nil
+       }
+       if dsNullableV30.ID == nil {
+               return nil, http.StatusBadRequest, errors.New("missing id"), nil
+       }
+
+       dsType, ok, err := getDSType(tx, *dsNullableV30.XMLID)
+       if !ok {
+               return nil, http.StatusNotFound, errors.New("delivery service 
'" + *dsNullableV30.XMLID + "' not found"), nil
+       }
+       if err != nil {
+               return nil, http.StatusInternalServerError, nil, 
errors.New("getting delivery service type during update: " + err.Error())
+       }
 
-       if err := ds.Validate(tx); err != nil {
+       // oldHostName will be used to determine if SSL Keys need updating - 
this will be empty if the DS doesn't have SSL keys, because DS types without 
SSL keys may not have regexes, and thus will fail to get a host name.
+       oldHostName := ""
+       oldCDNName := ""
+       oldRoutingName := ""
+       if dsType.HasSSLKeys() {
+               oldHostName, oldCDNName, oldRoutingName, err = 
getOldDetails(*dsNullableV30.ID, tx)
+               if err != nil {
+                       return nil, http.StatusInternalServerError, nil, 
errors.New("getting existing delivery service hostname: " + err.Error())
+               }
+       }
+
+       // TODO change DeepCachingType to implement sql.Valuer and sql.Scanner, 
so sqlx struct scan can be used.
+       deepCachingType := tc.DeepCachingType("").String()
+       if dsNullableV30.DeepCachingType != nil {
+               deepCachingType = dsNullableV30.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, 
*dsNullableV30.ID, "deliveryservice")
+       if userErr != nil || sysErr != nil {
+               return nil, errCode, userErr, sysErr
+       }
+
+       if errCode, userErr, sysErr = dbhelpers.CheckTopology(inf.Tx, 
dsNullableV30); userErr != nil || sysErr != nil {
+               return nil, errCode, userErr, sysErr
+       }
+
+       if dsNullableV30.Topology != nil {
+               requiredCapabilities, err := 
dbhelpers.GetDSRequiredCapabilitiesFromID(*dsNullableV30.ID, tx)
+               if err != nil {
+                       return nil, http.StatusInternalServerError, nil, 
errors.New("getting existing DS required capabilities: " + err.Error())
+               }
+               if len(requiredCapabilities) > 0 {
+                       if userErr, sysErr, status := 
EnsureTopologyBasedRequiredCapabilities(tx, *dsNullableV30.ID, 
*dsNullableV30.Topology, requiredCapabilities); userErr != nil || sysErr != nil 
{
+                               return nil, status, userErr, sysErr
+                       }
+               }
+       }
+
+       resultRows, err := tx.Query(updateDSQuery(),
+               &dsNullableV30.Active,
+               &dsNullableV30.CacheURL,
+               &dsNullableV30.CCRDNSTTL,
+               &dsNullableV30.CDNID,
+               &dsNullableV30.CheckPath,
+               &deepCachingType,
+               &dsNullableV30.DisplayName,
+               &dsNullableV30.DNSBypassCNAME,
+               &dsNullableV30.DNSBypassIP,
+               &dsNullableV30.DNSBypassIP6,
+               &dsNullableV30.DNSBypassTTL,
+               &dsNullableV30.DSCP,
+               &dsNullableV30.EdgeHeaderRewrite,
+               &dsNullableV30.GeoLimitRedirectURL,
+               &dsNullableV30.GeoLimit,
+               &dsNullableV30.GeoLimitCountries,
+               &dsNullableV30.GeoProvider,
+               &dsNullableV30.GlobalMaxMBPS,
+               &dsNullableV30.GlobalMaxTPS,
+               &dsNullableV30.FQPacingRate,
+               &dsNullableV30.HTTPBypassFQDN,
+               &dsNullableV30.InfoURL,
+               &dsNullableV30.InitialDispersion,
+               &dsNullableV30.IPV6RoutingEnabled,
+               &dsNullableV30.LogsEnabled,
+               &dsNullableV30.LongDesc,
+               &dsNullableV30.LongDesc1,
+               &dsNullableV30.LongDesc2,
+               &dsNullableV30.MaxDNSAnswers,
+               &dsNullableV30.MidHeaderRewrite,
+               &dsNullableV30.MissLat,
+               &dsNullableV30.MissLong,
+               &dsNullableV30.MultiSiteOrigin,
+               &dsNullableV30.OriginShield,
+               &dsNullableV30.ProfileID,
+               &dsNullableV30.Protocol,
+               &dsNullableV30.QStringIgnore,
+               &dsNullableV30.RangeRequestHandling,
+               &dsNullableV30.RegexRemap,
+               &dsNullableV30.RegionalGeoBlocking,
+               &dsNullableV30.RemapText,
+               &dsNullableV30.RoutingName,
+               &dsNullableV30.SigningAlgorithm,
+               &dsNullableV30.SSLKeyVersion,
+               &dsNullableV30.TenantID,
+               &dsNullableV30.TRRequestHeaders,
+               &dsNullableV30.TRResponseHeaders,
+               &dsNullableV30.TypeID,
+               &dsNullableV30.XMLID,
+               &dsNullableV30.AnonymousBlockingEnabled,
+               &dsNullableV30.ConsistentHashRegex,
+               &dsNullableV30.MaxOriginConnections,
+               &dsNullableV30.EcsEnabled,
+               &dsNullableV30.RangeSliceBlockSize,
+               &dsNullableV30.Topology,
+               &dsNullableV30.FirstHeaderRewrite,
+               &dsNullableV30.InnerHeaderRewrite,
+               &dsNullableV30.LastHeaderRewrite,
+               &dsNullableV30.ServiceCategory,
+               &dsNullableV30.MaxRequestHeaderBytes,
+               &dsNullableV30.ID)
+
+       if err != nil {
+               usrErr, sysErr, code := api.ParseDBError(err)
+               return nil, code, usrErr, sysErr
+       }
+       defer resultRows.Close()
+       if !resultRows.Next() {
+               return nil, http.StatusNotFound, errors.New("no delivery 
service found with this id"), nil
+       }
+       lastUpdated := tc.TimeNoMod{}
+       if err := resultRows.Scan(&lastUpdated); err != nil {
+               return nil, http.StatusInternalServerError, nil, 
errors.New("scan updating delivery service: " + err.Error())
+       }
+       if resultRows.Next() {
+               xmlID := ""
+               if dsNullableV30.XMLID != nil {
+                       xmlID = *dsNullableV30.XMLID
+               }
+               return nil, http.StatusInternalServerError, nil, 
errors.New("updating delivery service " + xmlID + ": " + "this update affected 
too many rows: > 1")
+       }
+
+       if dsNullableV30.ID == nil {
+               return nil, http.StatusInternalServerError, nil, 
errors.New("missing id after update")
+       }
+       if dsNullableV30.XMLID == nil {
+               return nil, http.StatusInternalServerError, nil, 
errors.New("missing xml_id after update")
+       }
+       if dsNullableV30.TypeID == nil {
+               return nil, http.StatusInternalServerError, nil, 
errors.New("missing type after update")
+       }
+       if dsNullableV30.RoutingName == nil {
+               return nil, http.StatusInternalServerError, nil, 
errors.New("missing routing name after update")
+       }
+       newDSType, err := getTypeFromID(*dsNullableV30.TypeID, tx)
+       if err != nil {
+               return nil, http.StatusInternalServerError, nil, 
errors.New("getting delivery service type after update: " + err.Error())
+       }
+       dsNullableV30.Type = &newDSType
+
+       cdnDomain, err := getCDNDomain(*dsNullableV30.ID, tx) // need to get 
the domain again, in case it changed.
+       if err != nil {
+               return nil, http.StatusInternalServerError, nil, 
errors.New("getting CDN domain after update: " + err.Error())
+       }
+
+       matchLists, err := 
GetDeliveryServicesMatchLists([]string{*dsNullableV30.XMLID}, tx)
+       if err != nil {
+               return nil, http.StatusInternalServerError, nil, 
errors.New("getting matchlists after update: " + err.Error())
+       }
+       if ml, ok := matchLists[*dsNullableV30.XMLID]; !ok {
+               return nil, http.StatusInternalServerError, nil, errors.New("no 
matchlists after update")
+       } else {
+               dsNullableV30.MatchList = &ml
+       }
+
+       // newHostName will be used to determine if SSL Keys need updating - 
this will be empty if the DS doesn't have SSL keys, because DS types without 
SSL keys may not have regexes, and thus will fail to get a host name.
+       newHostName := ""
+       newCDNName := ""
+       if dsType.HasSSLKeys() {
+               newCDNName, err = getCDNName(*dsNullableV30.CDNID, tx)
+               newHostName, err = getHostName(dsNullableV30.Protocol, 
*dsNullableV30.Type, *dsNullableV30.RoutingName, *dsNullableV30.MatchList, 
cdnDomain)
+               if err != nil {
+                       return nil, http.StatusInternalServerError, nil, 
errors.New("getting cdnname/hostname after update: " + err.Error())
+               }
+       }
+
+       if newDSType.HasSSLKeys() && (oldHostName != newHostName || oldCDNName 
!= newCDNName || oldRoutingName != *dsNullableV30.RoutingName) {
+               return nil, http.StatusForbidden, nil, errors.New("delivery 
service has ssl keys that cannot be automatically changed")
+       }
+
+       if err := EnsureParams(tx, *dsNullableV30.ID, *dsNullableV30.XMLID, 
dsNullableV30.EdgeHeaderRewrite, dsNullableV30.MidHeaderRewrite, 
dsNullableV30.RegexRemap, dsNullableV30.CacheURL, 
dsNullableV30.SigningAlgorithm, newDSType, dsNullableV30.MaxOriginConnections); 
err != nil {
+               return nil, http.StatusInternalServerError, nil, 
errors.New("ensuring dsNullableV30 parameters:: " + err.Error())
+       }
+
+       if err := updatePrimaryOrigin(tx, user, dsNullableV30); err != nil {
+               return nil, http.StatusInternalServerError, nil, 
errors.New("updating delivery service: " + err.Error())
+       }
+
+       dsNullableV30.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, *dsNullableV30.ID); err != nil {
+               return nil, http.StatusInternalServerError, nil, 
fmt.Errorf("deleting consistent hash query params for dsNullableV30 %s: %s", 
*dsNullableV30.XMLID, err.Error())
+       } else if c, _ := res.RowsAffected(); c > 0 {
+               api.CreateChangeLogRawTx(api.ApiChange, "DS: 
"+*dsNullableV30.XMLID+", ID: "+strconv.Itoa(*dsNullableV30.ID)+", ACTION: 
Deleted "+strconv.FormatInt(c, 10)+" consistent hash query params", user, tx)
+       }
+
+       if c, err := createConsistentHashQueryParams(tx, *dsNullableV30.ID, 
dsNullableV30.ConsistentHashQueryParams); err != nil {
+               usrErr, sysErr, code := api.ParseDBError(err)
+               return nil, code, usrErr, sysErr
+       } else {
+               api.CreateChangeLogRawTx(api.ApiChange, "DS: 
"+*dsNullableV30.XMLID+", ID: "+strconv.Itoa(*dsNullableV30.ID)+", ACTION: 
Created "+strconv.Itoa(c)+" consistent hash query params", user, tx)
+       }
+
+       if err := api.CreateChangeLogRawErr(api.ApiChange, "Updated 
dsNullableV30: "+*dsNullableV30.XMLID+" id: "+strconv.Itoa(*dsNullableV30.ID), 
user, tx); err != nil {
+               return nil, http.StatusInternalServerError, nil, 
errors.New("writing change log entry: " + err.Error())
+       }
+       ds = (*tc.DeliveryServiceV31)(&dsNullableV30)
+       return ds, http.StatusOK, nil, nil
+}
+
+func updateV30(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, ds 
*tc.DeliveryServiceV30) (*tc.DeliveryServiceV30, int, error, error) {

Review comment:
       Per my earlier comment above, this function should look just like 
`createV30`, except with the DB query that reads the existing value of the new 
field.

##########
File path: lib/go-tc/deliveryservices.go
##########
@@ -32,6 +32,9 @@ import (
 */
 
 const DefaultRoutingName = "cdn"
+const DefaultMaxHeaderSize = 131072

Review comment:
       we should rename this `DefaultMaxRequestHeaderBytes` to match the new 
name

##########
File path: traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
##########
@@ -239,91 +261,101 @@ func createV15(w http.ResponseWriter, r *http.Request, 
inf *api.APIInfo, reqDS t
 }
 
 // create creates the given ds in the database, and returns the DS with its id 
and other fields created on insert set. On error, the HTTP status code, user 
error, and system error are returned. The status code SHOULD NOT be used, if 
both errors are nil.
-func createV30(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, ds 
tc.DeliveryServiceNullableV30) (*tc.DeliveryServiceNullableV30, int, error, 
error) {
+func createV30(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, ds 
tc.DeliveryServiceV30) (*tc.DeliveryServiceV30, int, error, error) {
+       dsV31 := tc.DeliveryServiceV31{DeliveryServiceV30: ds}
+       res, status, userErr, sysErr := createV31(w, r, inf, dsV31)
+       if res != nil {
+               return &res.DeliveryServiceV30, status, userErr, sysErr
+       }
+       return nil, status, userErr, sysErr
+}
+
+// create creates the given ds in the database, and returns the DS with its id 
and other fields created on insert set. On error, the HTTP status code, user 
error, and system error are returned. The status code SHOULD NOT be used, if 
both errors are nil.
+func createV31(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, ds 
tc.DeliveryServiceV31) (*tc.DeliveryServiceV31, int, error, error) {

Review comment:
       We should rename `ds` to `dsV13`, then we can use `ds` instead of 
`dsNullableV30` on L278. Essentially, we don't want to be distracted by the 
versioning stuff throughout the method. Also it cuts down on the number of 
lines we have to change when adding new versions.

##########
File path: traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
##########
@@ -699,62 +759,301 @@ WHERE
 }
 
 func updateV15(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, reqDS 
*tc.DeliveryServiceNullableV15) (*tc.DeliveryServiceNullableV15, int, error, 
error) {

Review comment:
       So this function should actually stay exactly the same as it was before. 
Basically, we want to rename `updateV30` to `updateV31`, change it to accept 
and return V31 structs, then copy `updateV15` to `updateV30`, change it to 
accept V30 structs, upgrade them to V31 by changing the query to only select 
the new V31 field from the DB, then pass it to `updateV31` for the actual 
handling.
   
   It's kind of hard to draw in a review, but the functions are designed to 
chain together like so:
   ```
   UpdateV12 -> updateV12
                 |     ^
                 V     |
   UpdateV13 -> updateV13
                 |     ^
                 V     |
      ...          ...
   UpdateV31 -> updateV31  <------(this latest handler does all the actual 
handling)
   ```
   So each API version is like a link in a chain. For adding a new version, you 
just add a new link to the end of the chain, and you don't have to worry about 
earlier links in the chain (other than the one you're linking to). Does that 
make sense?

##########
File path: traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
##########
@@ -699,62 +759,301 @@ WHERE
 }
 
 func updateV15(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, reqDS 
*tc.DeliveryServiceNullableV15) (*tc.DeliveryServiceNullableV15, int, error, 
error) {
-       dsV30 := tc.DeliveryServiceNullableV30{DeliveryServiceNullableV15: 
*reqDS}
+       dsV31 := tc.DeliveryServiceV31{
+               DeliveryServiceV30: tc.DeliveryServiceV30{
+                       DeliveryServiceNullableV15: *reqDS,
+               },
+       }
        // query the DB for existing 3.0 fields in order to "upgrade" this 1.5 
request into a 3.0 request
        query := `
 SELECT
   ds.topology,
   ds.first_header_rewrite,
   ds.inner_header_rewrite,
   ds.last_header_rewrite,
-  ds.service_category
+  ds.service_category,
+  ds.max_request_header_bytes
 FROM
   deliveryservice ds
 WHERE
   ds.id = $1`
        if err := inf.Tx.Tx.QueryRow(query, *reqDS.ID).Scan(
-               &dsV30.Topology,
-               &dsV30.FirstHeaderRewrite,
-               &dsV30.InnerHeaderRewrite,
-               &dsV30.LastHeaderRewrite,
-               &dsV30.ServiceCategory,
+               &dsV31.Topology,
+               &dsV31.FirstHeaderRewrite,
+               &dsV31.InnerHeaderRewrite,
+               &dsV31.LastHeaderRewrite,
+               &dsV31.ServiceCategory,
+               &dsV31.MaxRequestHeaderBytes,
        ); err != nil {
                if err == sql.ErrNoRows {
-                       return nil, http.StatusNotFound, fmt.Errorf("delivery 
service ID %d not found", *dsV30.ID), nil
+                       return nil, http.StatusNotFound, fmt.Errorf("delivery 
service ID %d not found", *dsV31.ID), nil
                }
-               return nil, http.StatusInternalServerError, nil, 
fmt.Errorf("querying delivery service ID %d: %s", *dsV30.ID, err.Error())
+               return nil, http.StatusInternalServerError, nil, 
fmt.Errorf("querying delivery service ID %d: %s", *dsV31.ID, err.Error())
        }
-       res, status, userErr, sysErr := updateV30(w, r, inf, &dsV30)
+       res, status, userErr, sysErr := updateV31(w, r, inf, &dsV31)
        if res != nil {
                return &res.DeliveryServiceNullableV15, status, userErr, sysErr
        }
        return nil, status, userErr, sysErr
 }
 
-func updateV30(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, ds 
*tc.DeliveryServiceNullableV30) (*tc.DeliveryServiceNullableV30, int, error, 
error) {
+func updateV31(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, ds 
*tc.DeliveryServiceV31) (*tc.DeliveryServiceV31, int, error, error) {

Review comment:
       similar to my comment for `createV31`, we should rename this parameter 
to `dsV31`, then use `ds` instead of `dsNullableV30` on L803




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to