This is an automated email from the ASF dual-hosted git repository.

zrhoffman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new 56b9d3c0a2 Fix APIv4.0 returning APIv4.1 DS structures (#7156)
56b9d3c0a2 is described below

commit 56b9d3c0a21883f1f071ac2473769a414ef00064
Author: ocket8888 <[email protected]>
AuthorDate: Wed Oct 26 08:58:53 2022 -0600

    Fix APIv4.0 returning APIv4.1 DS structures (#7156)
    
    * Fix APIv4.0 returning APIv4.1 DS structures
    
    * Fix compilation issue in TRUTH
    
    * Fix version matching in read handler
    
    * Fix improper struct version used in client tests
---
 cache-config/t3cutil/toreq/conversions.go          |   4 +-
 cache-config/t3cutil/toreq/toreqold/conversions.go |   2 +-
 lib/go-atscfg/atscfg.go                            |   6 +-
 lib/go-tc/deliveryservices.go                      |  99 +++-
 lib/go-tc/deliveryservices_test.go                 |   2 +-
 .../testing/api/v4/deliveryservices_test.go        |   6 +-
 .../api/v4/servers_id_deliveryservices_test.go     |   2 +-
 .../testing/api/v5/deliveryservices_test.go        |   6 +-
 .../api/v5/servers_id_deliveryservices_test.go     |   2 +-
 .../deliveryservice/deliveryservices.go            | 615 ++++++++++++---------
 .../traffic_ops_golang/deliveryservice/safe.go     |   6 +-
 traffic_ops/traffic_ops_golang/routing/routes.go   |   6 +-
 traffic_router/ultimate-test-harness/http_test.go  |   2 +-
 13 files changed, 454 insertions(+), 304 deletions(-)

diff --git a/cache-config/t3cutil/toreq/conversions.go 
b/cache-config/t3cutil/toreq/conversions.go
index 7520a0498f..c3e6240772 100644
--- a/cache-config/t3cutil/toreq/conversions.go
+++ b/cache-config/t3cutil/toreq/conversions.go
@@ -46,8 +46,8 @@ func serverToLatest(oldSv *tc.ServerV40) (*atscfg.Server, 
error) {
        return &asv, nil
 }
 
-func dsesToLatest(dses []tc.DeliveryServiceV40) []atscfg.DeliveryService {
-       return atscfg.V40ToDeliveryServices(dses)
+func dsesToLatest(dses []tc.DeliveryServiceV4) []atscfg.DeliveryService {
+       return atscfg.V4ToDeliveryServices(dses)
 }
 
 func jobsToLatest(jobs []tc.InvalidationJobV4) []atscfg.InvalidationJob {
diff --git a/cache-config/t3cutil/toreq/toreqold/conversions.go 
b/cache-config/t3cutil/toreq/toreqold/conversions.go
index a44c58b3b9..019a3b8b42 100644
--- a/cache-config/t3cutil/toreq/toreqold/conversions.go
+++ b/cache-config/t3cutil/toreq/toreqold/conversions.go
@@ -50,7 +50,7 @@ func serverToLatest(oldSv *tc.ServerV30) (*atscfg.Server, 
error) {
 }
 
 func dsesToLatest(dses []tc.DeliveryServiceNullableV30) 
[]atscfg.DeliveryService {
-       newDSes := []tc.DeliveryServiceV40{}
+       newDSes := []tc.DeliveryServiceV4{}
        for _, ds := range dses {
                newDSes = append(newDSes, ds.UpgradeToV4())
        }
diff --git a/lib/go-atscfg/atscfg.go b/lib/go-atscfg/atscfg.go
index 45287c3f91..55467a93c2 100644
--- a/lib/go-atscfg/atscfg.go
+++ b/lib/go-atscfg/atscfg.go
@@ -59,7 +59,7 @@ type Server tc.ServerV40
 // DeliveryService is a tc.DeliveryService for the latest lib/go-tc and 
traffic_ops/vx-client type.
 // This allows atscfg to not have to change the type everywhere it's used, 
every time ATC changes the base type,
 // but to only have to change it here, and the places where breaking symbol 
changes were made.
-type DeliveryService tc.DeliveryServiceV40
+type DeliveryService tc.DeliveryServiceV4
 
 // InvalidationJob is a tc.InvalidationJob for the latest lib/go-tc and 
traffic_ops/vx-client type.
 // This allows atscfg to not have to change the type everywhere it's used, 
every time ATC changes the base type,
@@ -72,7 +72,7 @@ type InvalidationJob tc.InvalidationJobV4
 type ServerUpdateStatus tc.ServerUpdateStatusV4
 
 // ToDeliveryServices converts a slice of the latest lib/go-tc and 
traffic_ops/vx-client type to the local alias.
-func ToDeliveryServices(dses []tc.DeliveryServiceV40) []DeliveryService {
+func ToDeliveryServices(dses []tc.DeliveryServiceV4) []DeliveryService {
        ad := make([]DeliveryService, 0, len(dses))
        for _, ds := range dses {
                ad = append(ad, DeliveryService(ds))
@@ -81,7 +81,7 @@ func ToDeliveryServices(dses []tc.DeliveryServiceV40) 
[]DeliveryService {
 }
 
 // V40ToDeliveryServices converts a slice of the old traffic_ops/v4-client 
type to the local alias.
-func V40ToDeliveryServices(dses []tc.DeliveryServiceV40) []DeliveryService {
+func V4ToDeliveryServices(dses []tc.DeliveryServiceV4) []DeliveryService {
        ad := make([]DeliveryService, 0, len(dses))
        for _, ds := range dses {
                ad = append(ad, DeliveryService(ds))
diff --git a/lib/go-tc/deliveryservices.go b/lib/go-tc/deliveryservices.go
index 64c8353578..082501a237 100644
--- a/lib/go-tc/deliveryservices.go
+++ b/lib/go-tc/deliveryservices.go
@@ -60,10 +60,17 @@ type DeliveryServicesResponseV40 struct {
        Alerts
 }
 
+// DeliveryServicesResponseV41 is the type of a response from the
+// /api/4.1/deliveryservices Traffic Ops endpoint.
+type DeliveryServicesResponseV41 struct {
+       Response []DeliveryServiceV41 `json:"response"`
+       Alerts
+}
+
 // DeliveryServicesResponseV4 is the type of a response from the
 // /api/4.x/deliveryservices Traffic Ops endpoint.
 // It always points to the type for the latest minor version of APIv4.
-type DeliveryServicesResponseV4 = DeliveryServicesResponseV40
+type DeliveryServicesResponseV4 = DeliveryServicesResponseV41
 
 // DeliveryServicesNullableResponse roughly models the structure of responses
 // from Traffic Ops to GET requests made to its
@@ -253,9 +260,20 @@ type DeliveryServiceV40 struct {
        GeoLimitCountries GeoLimitCountriesType `json:"geoLimitCountries"`
 }
 
+// DeliveryServiceV41 is a Delivery Service as it appears in version 4.1 of the
+// Traffic Ops API.
+type DeliveryServiceV41 struct {
+       DeliveryServiceV40
+
+       // Regional indicates whether the Delivery Service's 
MaxOriginConnections is
+       // only per Cache Group, rather than divided over all Cache Servers in 
child
+       // Cache Groups of the Origin.
+       Regional bool `json:"regional" db:"regional"`
+}
+
 // DeliveryServiceV4 is a Delivery Service as it appears in version 4 of the
 // Traffic Ops API - it always points to the highest minor version in APIv4.
-type DeliveryServiceV4 = DeliveryServiceV40
+type DeliveryServiceV4 = DeliveryServiceV41
 
 // These are the TLS Versions known by Apache Traffic Control to exist.
 const (
@@ -301,17 +319,7 @@ func newerTLSVersionsDisallowedMessage(old string, newer 
[]string) string {
        return msg.String()
 }
 
-// TLSVersionsAlerts generates warning-level alerts for the Delivery Service's
-// TLS versions array. It will warn if newer versions are disallowed while
-// older, less secure versions are allowed, if there are unrecognized versions
-// present, if the Delivery Service's Protocol does not make use of TLS
-// Versions, and whenever TLSVersions are explicitly set at all.
-//
-// This does NOT verify that the Delivery Service's TLS versions are _valid_,
-// it ONLY creates warnings based on conditions that are possibly detrimental
-// to CDN operation, but can, in fact, work.
-func (ds DeliveryServiceV4) TLSVersionsAlerts() Alerts {
-       vers := ds.TLSVersions
+func tlsVersionsAlerts(vers []string, protocol int) Alerts {
        messages := []string{}
 
        if len(vers) > 0 {
@@ -380,13 +388,46 @@ func (ds DeliveryServiceV4) TLSVersionsAlerts() Alerts {
                }
        }
 
-       if ds.Protocol != nil && *ds.Protocol == DSProtocolHTTP {
+       if protocol == DSProtocolHTTP {
                messages = append(messages, "tlsVersions has no effect on 
Delivery Services with Protocol '0' (HTTP_ONLY)")
        }
 
        return CreateAlerts(WarnLevel, messages...)
 }
 
+// TLSVersionsAlerts generates warning-level alerts for the Delivery Service's
+// TLS versions array. It will warn if newer versions are disallowed while
+// older, less secure versions are allowed, if there are unrecognized versions
+// present, if the Delivery Service's Protocol does not make use of TLS
+// Versions, and whenever TLSVersions are explicitly set at all.
+//
+// This does NOT verify that the Delivery Service's TLS versions are _valid_,
+// it ONLY creates warnings based on conditions that are possibly detrimental
+// to CDN operation, but can, in fact, work.
+func (ds DeliveryServiceV40) TLSVersionsAlerts() Alerts {
+       vers := ds.TLSVersions
+       var protocol int
+       if ds.Protocol != nil {
+               protocol = *ds.Protocol
+       } else {
+               protocol = -1
+       }
+       return tlsVersionsAlerts(vers, protocol)
+}
+
+// TLSVersionsAlerts generates warning-level alerts for the Delivery Service's
+// TLS versions array. It will warn if newer versions are disallowed while
+// older, less secure versions are allowed, if there are unrecognized versions
+// present, if the Delivery Service's Protocol does not make use of TLS
+// Versions, and whenever TLSVersions are explicitly set at all.
+//
+// This does NOT verify that the Delivery Service's TLS versions are _valid_,
+// it ONLY creates warnings based on conditions that are possibly detrimental
+// to CDN operation, but can, in fact, work.
+func (ds DeliveryServiceV41) TLSVersionsAlerts() Alerts {
+       return ds.DeliveryServiceV40.TLSVersionsAlerts()
+}
+
 // DeliveryServiceV30 represents a Delivery Service as they appear in version
 // 3.0 of the Traffic Ops API.
 //
@@ -843,7 +884,16 @@ type DeliveryServiceRemovedFieldsV11 struct {
        CacheURL *string `json:"cacheurl" db:"cacheurl"`
 }
 
-// RemoveLD1AndLD2 removes the Long Description 1 and Long Description 2 
fields from a V 4.x DS, and returns the resulting struct.
+// RemoveLD1AndLD2 removes the Long Description 1 and Long Description 2 fields
+// from a V4.0 Delivery Service, and returns the resulting struct.
+func (ds *DeliveryServiceV40) RemoveLD1AndLD2() DeliveryServiceV40 {
+       ds.LongDesc1 = nil
+       ds.LongDesc2 = nil
+       return *ds
+}
+
+// RemoveLD1AndLD2 removes the Long Description 1 and Long Description 2 fields
+// from a V 4.x DS, and returns the resulting struct.
 func (ds *DeliveryServiceV4) RemoveLD1AndLD2() DeliveryServiceV4 {
        ds.LongDesc1 = nil
        ds.LongDesc2 = nil
@@ -883,18 +933,19 @@ func (ds *DeliveryServiceNullableV30) UpgradeToV4() 
DeliveryServiceV4 {
        var geo GeoLimitCountriesType
        if ds.GeoLimitCountries != nil {
                str := *ds.GeoLimitCountries
-               geo = make([]string, 0)
                geo = strings.Split(str, ",")
        }
        return DeliveryServiceV4{
-               DeliveryServiceFieldsV31:         ds.DeliveryServiceFieldsV31,
-               DeliveryServiceFieldsV30:         ds.DeliveryServiceFieldsV30,
-               DeliveryServiceFieldsV15:         ds.DeliveryServiceFieldsV15,
-               DeliveryServiceFieldsV14:         ds.DeliveryServiceFieldsV14,
-               DeliveryServiceFieldsV13:         ds.DeliveryServiceFieldsV13,
-               DeliveryServiceNullableFieldsV11: 
ds.DeliveryServiceNullableFieldsV11,
-               TLSVersions:                      nil,
-               GeoLimitCountries:                geo,
+               DeliveryServiceV40: DeliveryServiceV40{
+                       DeliveryServiceFieldsV31:         
ds.DeliveryServiceFieldsV31,
+                       DeliveryServiceFieldsV30:         
ds.DeliveryServiceFieldsV30,
+                       DeliveryServiceFieldsV15:         
ds.DeliveryServiceFieldsV15,
+                       DeliveryServiceFieldsV14:         
ds.DeliveryServiceFieldsV14,
+                       DeliveryServiceFieldsV13:         
ds.DeliveryServiceFieldsV13,
+                       DeliveryServiceNullableFieldsV11: 
ds.DeliveryServiceNullableFieldsV11,
+                       TLSVersions:                      nil,
+                       GeoLimitCountries:                geo,
+               },
        }
 }
 
diff --git a/lib/go-tc/deliveryservices_test.go 
b/lib/go-tc/deliveryservices_test.go
index b15e1d6974..55179041d1 100644
--- a/lib/go-tc/deliveryservices_test.go
+++ b/lib/go-tc/deliveryservices_test.go
@@ -837,7 +837,7 @@ func BenchmarkTLSVersionsAlerts(b *testing.B) {
                        versions = append(versions, fmt.Sprintf("%d.%d", major, 
minor))
                }
        }
-       ds := DeliveryServiceV4{TLSVersions: versions}
+       ds := DeliveryServiceV40{TLSVersions: versions}
 
        b.ReportAllocs()
        b.ResetTimer()
diff --git a/traffic_ops/testing/api/v4/deliveryservices_test.go 
b/traffic_ops/testing/api/v4/deliveryservices_test.go
index e4baa3a60e..98446dff88 100644
--- a/traffic_ops/testing/api/v4/deliveryservices_test.go
+++ b/traffic_ops/testing/api/v4/deliveryservices_test.go
@@ -578,7 +578,7 @@ func TestDeliveryServices(t *testing.T) {
 
 func validateDSExpectedFields(expectedResp map[string]interface{}) 
utils.CkReqFunc {
        return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ 
tc.Alerts, _ error) {
-               dsResp := resp.([]tc.DeliveryServiceV40)
+               dsResp := resp.([]tc.DeliveryServiceV4)
                for field, expected := range expectedResp {
                        for _, ds := range dsResp {
                                switch field {
@@ -658,7 +658,7 @@ func validateDSExpectedFields(expectedResp 
map[string]interface{}) utils.CkReqFu
 
 func validatePagination(paginationParam string) utils.CkReqFunc {
        return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ 
tc.Alerts, _ error) {
-               paginationResp := resp.([]tc.DeliveryServiceV40)
+               paginationResp := resp.([]tc.DeliveryServiceV4)
 
                opts := client.NewRequestOptions()
                opts.QueryParameters.Set("orderby", "id")
@@ -680,7 +680,7 @@ func validatePagination(paginationParam string) 
utils.CkReqFunc {
 
 func validateDescSort() utils.CkReqFunc {
        return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, 
alerts tc.Alerts, _ error) {
-               dsDescResp := resp.([]tc.DeliveryServiceV40)
+               dsDescResp := resp.([]tc.DeliveryServiceV4)
                var descSortedList []string
                var ascSortedList []string
                assert.GreaterOrEqual(t, len(dsDescResp), 2, "Need at least 2 
XMLIDs in Traffic Ops to test desc sort, found: %d", len(dsDescResp))
diff --git a/traffic_ops/testing/api/v4/servers_id_deliveryservices_test.go 
b/traffic_ops/testing/api/v4/servers_id_deliveryservices_test.go
index 4e159cc17e..c8787fabf0 100644
--- a/traffic_ops/testing/api/v4/servers_id_deliveryservices_test.go
+++ b/traffic_ops/testing/api/v4/servers_id_deliveryservices_test.go
@@ -159,7 +159,7 @@ func validateServersDeliveryServices(expectedDSID int) 
utils.CkReqFunc {
        return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ 
tc.Alerts, _ error) {
                assert.RequireNotNil(t, resp, "Expected Server Delivery Service 
response to not be nil.")
                var found bool
-               deliveryServices := resp.([]tc.DeliveryServiceV40)
+               deliveryServices := resp.([]tc.DeliveryServiceV4)
                for _, ds := range deliveryServices {
                        if ds.ID != nil && *ds.ID == expectedDSID {
                                found = true
diff --git a/traffic_ops/testing/api/v5/deliveryservices_test.go 
b/traffic_ops/testing/api/v5/deliveryservices_test.go
index bf3bb1c02c..5e400b0be4 100644
--- a/traffic_ops/testing/api/v5/deliveryservices_test.go
+++ b/traffic_ops/testing/api/v5/deliveryservices_test.go
@@ -578,7 +578,7 @@ func TestDeliveryServices(t *testing.T) {
 
 func validateDSExpectedFields(expectedResp map[string]interface{}) 
utils.CkReqFunc {
        return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ 
tc.Alerts, _ error) {
-               dsResp := resp.([]tc.DeliveryServiceV40)
+               dsResp := resp.([]tc.DeliveryServiceV4)
                for field, expected := range expectedResp {
                        for _, ds := range dsResp {
                                switch field {
@@ -658,7 +658,7 @@ func validateDSExpectedFields(expectedResp 
map[string]interface{}) utils.CkReqFu
 
 func validatePagination(paginationParam string) utils.CkReqFunc {
        return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ 
tc.Alerts, _ error) {
-               paginationResp := resp.([]tc.DeliveryServiceV40)
+               paginationResp := resp.([]tc.DeliveryServiceV4)
 
                opts := client.NewRequestOptions()
                opts.QueryParameters.Set("orderby", "id")
@@ -680,7 +680,7 @@ func validatePagination(paginationParam string) 
utils.CkReqFunc {
 
 func validateDescSort() utils.CkReqFunc {
        return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, 
alerts tc.Alerts, _ error) {
-               dsDescResp := resp.([]tc.DeliveryServiceV40)
+               dsDescResp := resp.([]tc.DeliveryServiceV4)
                var descSortedList []string
                var ascSortedList []string
                assert.GreaterOrEqual(t, len(dsDescResp), 2, "Need at least 2 
XMLIDs in Traffic Ops to test desc sort, found: %d", len(dsDescResp))
diff --git a/traffic_ops/testing/api/v5/servers_id_deliveryservices_test.go 
b/traffic_ops/testing/api/v5/servers_id_deliveryservices_test.go
index 3b3826d780..50fa745ca4 100644
--- a/traffic_ops/testing/api/v5/servers_id_deliveryservices_test.go
+++ b/traffic_ops/testing/api/v5/servers_id_deliveryservices_test.go
@@ -159,7 +159,7 @@ func validateServersDeliveryServices(expectedDSID int) 
utils.CkReqFunc {
        return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _ 
tc.Alerts, _ error) {
                assert.RequireNotNil(t, resp, "Expected Server Delivery Service 
response to not be nil.")
                var found bool
-               deliveryServices := resp.([]tc.DeliveryServiceV40)
+               deliveryServices := resp.([]tc.DeliveryServiceV4)
                for _, ds := range deliveryServices {
                        if ds.ID != nil && *ds.ID == expectedDSID {
                                found = true
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go 
b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
index 7235c08649..b918638c7a 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
@@ -195,6 +195,33 @@ func CreateV40(w http.ResponseWriter, r *http.Request) {
        api.WriteAlertsObj(w, r, http.StatusCreated, alerts, 
[]tc.DeliveryServiceV40{*res})
 }
 
+// CreateV41 is a handler for POST requests to create Delivery Services in
+// version 4.1 of the Traffic Ops API.
+func CreateV41(w http.ResponseWriter, r *http.Request) {
+       inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+               return
+       }
+       defer inf.Close()
+
+       var ds tc.DeliveryServiceV41
+       if err := json.NewDecoder(r.Body).Decode(&ds); err != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, 
fmt.Errorf("decoding: %w", err), nil)
+               return
+       }
+       res, status, userErr, sysErr := createV41(w, r, inf, ds, true)
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, status, userErr, sysErr)
+               return
+       }
+       alerts := res.TLSVersionsAlerts()
+       alerts.AddNewAlert(tc.SuccessLevel, "Delivery Service creation was 
successful")
+
+       w.Header().Set("Location", 
fmt.Sprintf("/api/4.0/deliveryservices?id=%d", *res.ID))
+       api.WriteAlertsObj(w, r, http.StatusCreated, alerts, 
[]tc.DeliveryServiceV41{*res})
+}
+
 func createV30(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV30 
tc.DeliveryServiceV30) (*tc.DeliveryServiceV30, int, error, error) {
        ds := tc.DeliveryServiceV31{DeliveryServiceV30: dsV30}
        res, status, userErr, sysErr := createV31(w, r, inf, ds)
@@ -207,16 +234,17 @@ func createV31(w http.ResponseWriter, r *http.Request, 
inf *api.APIInfo, dsV31 t
        tx := inf.Tx.Tx
        dsNullable := tc.DeliveryServiceNullableV30(dsV31)
        ds := dsNullable.UpgradeToV4()
-       res, status, userErr, sysErr := createV40(w, r, inf, 
tc.DeliveryServiceV40(ds), false)
+       res, status, userErr, sysErr := createV40(w, r, inf, 
ds.DeliveryServiceV40, false)
        if res == nil {
                return nil, status, userErr, sysErr
        }
 
-       ds = tc.DeliveryServiceV4(*res)
+       ds = tc.DeliveryServiceV41{DeliveryServiceV40: *res}
        if dsV31.CacheURL != nil {
                _, err := tx.Exec("UPDATE deliveryservice SET cacheurl = $1 
WHERE ID = $2",
-                       &dsV31.CacheURL,
-                       &ds.ID)
+                       dsV31.CacheURL,
+                       ds.ID,
+               )
                if err != nil {
                        usrErr, sysErr, code := api.ParseDBError(err)
                        return nil, code, usrErr, sysErr
@@ -261,10 +289,24 @@ func recreateTLSVersions(versions []string, dsid int, tx 
*sql.Tx) error {
 }
 
 // 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 createV40(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV40 
tc.DeliveryServiceV40, omitExtraLongDescFields bool) (*tc.DeliveryServiceV40, 
int, error, error) {
+func createV40(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV4 
tc.DeliveryServiceV40, omitExtraLongDescFields bool) (*tc.DeliveryServiceV40, 
int, error, error) {
+       ds, code, userErr, sysErr := createV41(w, r, inf, 
tc.DeliveryServiceV41{DeliveryServiceV40: dsV4}, omitExtraLongDescFields)
+       if userErr != nil || sysErr != nil || ds == nil {
+               return nil, code, userErr, sysErr
+       }
+       d := ds.DeliveryServiceV40
+       return &d, code, nil, nil
+}
+
+// createV41 creates the given Delivery Service in the database, and returns a
+// reference to the Delivery Service with its ID and other fields which are
+// created on insert set. On error, an HTTP status code, user error, and system
+// error are returned. The status code SHOULD NOT be used, if both errors are
+// nil.
+func createV41(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV4 
tc.DeliveryServiceV41, omitExtraLongDescFields bool) (*tc.DeliveryServiceV41, 
int, error, error) {
        user := inf.User
        tx := inf.Tx.Tx
-       ds := tc.DeliveryServiceV4(dsV40)
+       ds := tc.DeliveryServiceV4(dsV4)
        err := Validate(tx, &ds)
        var geoLimitCountries string
        if err != nil {
@@ -299,127 +341,127 @@ func createV40(w http.ResponseWriter, r *http.Request, 
inf *api.APIInfo, dsV40 t
                        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(insertQueryWithoutLD1AndLD2(),
-                       &ds.Active,
-                       &ds.AnonymousBlockingEnabled,
-                       &ds.CCRDNSTTL,
-                       &ds.CDNID,
-                       &ds.CheckPath,
-                       &ds.ConsistentHashRegex,
-                       &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.MaxOriginConnections,
-                       &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.Topology,
-                       &ds.TRRequestHeaders,
-                       &ds.TRResponseHeaders,
-                       &ds.TypeID,
-                       &ds.XMLID,
-                       &ds.EcsEnabled,
-                       &ds.RangeSliceBlockSize,
-                       &ds.FirstHeaderRewrite,
-                       &ds.InnerHeaderRewrite,
-                       &ds.LastHeaderRewrite,
-                       &ds.ServiceCategory,
-                       &ds.MaxRequestHeaderBytes,
+                       ds.Active,
+                       ds.AnonymousBlockingEnabled,
+                       ds.CCRDNSTTL,
+                       ds.CDNID,
+                       ds.CheckPath,
+                       ds.ConsistentHashRegex,
+                       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.MaxOriginConnections,
+                       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.Topology,
+                       ds.TRRequestHeaders,
+                       ds.TRResponseHeaders,
+                       ds.TypeID,
+                       ds.XMLID,
+                       ds.EcsEnabled,
+                       ds.RangeSliceBlockSize,
+                       ds.FirstHeaderRewrite,
+                       ds.InnerHeaderRewrite,
+                       ds.LastHeaderRewrite,
+                       ds.ServiceCategory,
+                       ds.MaxRequestHeaderBytes,
                )
        } else {
                resultRows, err = tx.Query(insertQuery(),
-                       &ds.Active,
-                       &ds.AnonymousBlockingEnabled,
-                       &ds.CCRDNSTTL,
-                       &ds.CDNID,
-                       &ds.CheckPath,
-                       &ds.ConsistentHashRegex,
-                       &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.LongDesc1,
-                       &ds.LongDesc2,
-                       &ds.MaxDNSAnswers,
-                       &ds.MaxOriginConnections,
-                       &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.Topology,
-                       &ds.TRRequestHeaders,
-                       &ds.TRResponseHeaders,
-                       &ds.TypeID,
-                       &ds.XMLID,
-                       &ds.EcsEnabled,
-                       &ds.RangeSliceBlockSize,
-                       &ds.FirstHeaderRewrite,
-                       &ds.InnerHeaderRewrite,
-                       &ds.LastHeaderRewrite,
-                       &ds.ServiceCategory,
-                       &ds.MaxRequestHeaderBytes,
+                       ds.Active,
+                       ds.AnonymousBlockingEnabled,
+                       ds.CCRDNSTTL,
+                       ds.CDNID,
+                       ds.CheckPath,
+                       ds.ConsistentHashRegex,
+                       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.LongDesc1,
+                       ds.LongDesc2,
+                       ds.MaxDNSAnswers,
+                       ds.MaxOriginConnections,
+                       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.Topology,
+                       ds.TRRequestHeaders,
+                       ds.TRResponseHeaders,
+                       ds.TypeID,
+                       ds.XMLID,
+                       ds.EcsEnabled,
+                       ds.RangeSliceBlockSize,
+                       ds.FirstHeaderRewrite,
+                       ds.InnerHeaderRewrite,
+                       ds.LastHeaderRewrite,
+                       ds.ServiceCategory,
+                       ds.MaxRequestHeaderBytes,
                )
        }
 
@@ -515,16 +557,16 @@ func createV40(w http.ResponseWriter, r *http.Request, 
inf *api.APIInfo, dsV40 t
                return nil, http.StatusInternalServerError, nil, 
errors.New("error writing to audit log: " + err.Error())
        }
 
-       dsV40 = ds
+       dsV4 = ds
 
        if inf.Config.TrafficVaultEnabled && ds.Protocol != nil && 
(*ds.Protocol == tc.DSProtocolHTTPS || *ds.Protocol == 
tc.DSProtocolHTTPAndHTTPS || *ds.Protocol == tc.DSProtocolHTTPToHTTPS) {
-               err, errCode := GeneratePlaceholderSelfSignedCert(dsV40, inf, 
r.Context())
+               err, errCode := GeneratePlaceholderSelfSignedCert(dsV4, inf, 
r.Context())
                if err != nil || errCode != http.StatusOK {
                        return nil, errCode, nil, fmt.Errorf("creating self 
signed default cert: %v", err)
                }
        }
 
-       return &dsV40, http.StatusOK, nil, nil
+       return &dsV4, http.StatusOK, nil, nil
 }
 
 func createDefaultRegex(tx *sql.Tx, dsID int, xmlID string) error {
@@ -572,8 +614,10 @@ func (ds *TODeliveryService) Read(h http.Header, useIMS 
bool) ([]interface{}, er
        for _, ds := range dses {
                switch {
                // NOTE: it's required to handle minor version cases in a 
descending >= manner
-               case version.Major > 3:
+               case version.Major > 4 || (version.Major == 4 && version.Minor 
>= 1):
                        returnable = append(returnable, ds.RemoveLD1AndLD2())
+               case version.Major >= 4:
+                       returnable = append(returnable, 
ds.DeliveryServiceV40.RemoveLD1AndLD2())
                case version.Major >= 3 && version.Minor >= 1:
                        returnable = append(returnable, ds.DowngradeToV31())
                case version.Major >= 3:
@@ -668,6 +712,43 @@ func UpdateV40(w http.ResponseWriter, r *http.Request) {
        api.WriteAlertsObj(w, r, http.StatusOK, alerts, 
[]tc.DeliveryServiceV40{*res})
 }
 
+// UpdateV41 is a handler for PUT requests used to update a Delivery Service.
+func UpdateV41(w http.ResponseWriter, r *http.Request) {
+       inf, userErr, sysErr, errCode := api.NewInfo(r, nil, []string{"id"})
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+               return
+       }
+       defer inf.Close()
+       id := inf.IntParams["id"]
+
+       var ds tc.DeliveryServiceV41
+       if err := json.NewDecoder(r.Body).Decode(&ds); err != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, 
fmt.Errorf("malformed JSON: %w", err), nil)
+               return
+       }
+       ds.ID = &id
+       _, cdn, _, err := dbhelpers.GetDSNameAndCDNFromID(inf.Tx.Tx, id)
+       if err != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, 
nil, fmt.Errorf("deliveryservice update: getting CDN from DS ID: %w", err))
+               return
+       }
+       userErr, sysErr, statusCode := 
dbhelpers.CheckIfCurrentUserCanModifyCDN(inf.Tx.Tx, string(cdn), 
inf.User.UserName)
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+               return
+       }
+       res, status, userErr, sysErr := updateV41(w, r, inf, &ds, true)
+       if userErr != nil || sysErr != nil {
+               api.HandleErr(w, r, inf.Tx.Tx, status, userErr, sysErr)
+               return
+       }
+       alerts := res.TLSVersionsAlerts()
+       alerts.AddNewAlert(tc.SuccessLevel, "Delivery Service update was 
successful")
+
+       api.WriteAlertsObj(w, r, http.StatusOK, alerts, 
[]tc.DeliveryServiceV41{*res})
+}
+
 func updateV30(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV30 
*tc.DeliveryServiceV30) (*tc.DeliveryServiceV30, int, error, error) {
        dsV31 := tc.DeliveryServiceV31{DeliveryServiceV30: *dsV30}
        // query the DB for existing 3.1 fields in order to "upgrade" this 3.0 
request into a 3.1 request
@@ -695,26 +776,27 @@ WHERE
 func updateV31(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV31 
*tc.DeliveryServiceV31) (*tc.DeliveryServiceV31, int, error, error) {
        dsNull := tc.DeliveryServiceNullableV30(*dsV31)
        ds := dsNull.UpgradeToV4()
-       dsV40 := ds
-       if dsV40.ID == nil {
+       dsV41 := ds
+       if dsV41.ID == nil {
                return nil, http.StatusInternalServerError, nil, 
errors.New("cannot update a Delivery Service with nil ID")
        }
 
        tx := inf.Tx.Tx
        var sysErr error
-       if dsV40.TLSVersions, sysErr = GetDSTLSVersions(*dsV40.ID, tx); sysErr 
!= nil {
-               return nil, http.StatusInternalServerError, nil, 
fmt.Errorf("getting TLS versions for DS #%d in API version < 4.0: %w", 
*dsV40.ID, sysErr)
+       if dsV41.TLSVersions, sysErr = GetDSTLSVersions(*dsV41.ID, tx); sysErr 
!= nil {
+               return nil, http.StatusInternalServerError, nil, 
fmt.Errorf("getting TLS versions for DS #%d in API version < 4.0: %w", 
*dsV41.ID, sysErr)
        }
 
-       res, status, usrErr, sysErr := updateV40(w, r, inf, &dsV40, false)
+       res, status, usrErr, sysErr := updateV40(w, r, inf, 
&dsV41.DeliveryServiceV40, false)
        if res == nil || usrErr != nil || sysErr != nil {
                return nil, status, usrErr, sysErr
        }
-       ds = *res
+       ds.DeliveryServiceV40 = *res
        if dsV31.CacheURL != nil {
                _, err := tx.Exec("UPDATE deliveryservice SET cacheurl = $1 
WHERE ID = $2",
-                       &dsV31.CacheURL,
-                       &ds.ID)
+                       dsV31.CacheURL,
+                       ds.ID,
+               )
                if err != nil {
                        usrErr, sysErr, code := api.ParseDBError(err)
                        return nil, code, usrErr, sysErr
@@ -729,9 +811,17 @@ func updateV31(w http.ResponseWriter, r *http.Request, inf 
*api.APIInfo, dsV31 *
        return &oldRes, http.StatusOK, nil, nil
 }
 func updateV40(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV40 
*tc.DeliveryServiceV40, omitExtraLongDescFields bool) (*tc.DeliveryServiceV40, 
int, error, error) {
+       ds, code, userErr, sysErr := updateV41(w, r, inf, 
&tc.DeliveryServiceV41{DeliveryServiceV40: *dsV40}, omitExtraLongDescFields)
+       if userErr != nil || sysErr != nil || ds == nil {
+               return nil, code, userErr, sysErr
+       }
+       d := ds.DeliveryServiceV40
+       return &d, code, nil, nil
+}
+func updateV41(w http.ResponseWriter, r *http.Request, inf *api.APIInfo, dsV4 
*tc.DeliveryServiceV41, omitExtraLongDescFields bool) (*tc.DeliveryServiceV41, 
int, error, error) {
        tx := inf.Tx.Tx
        user := inf.User
-       ds := tc.DeliveryServiceV4(*dsV40)
+       ds := tc.DeliveryServiceV4(*dsV4)
        if err := Validate(tx, &ds); err != nil {
                return nil, http.StatusBadRequest, errors.New("invalid request: 
" + err.Error()), nil
        }
@@ -833,128 +923,130 @@ func updateV40(w http.ResponseWriter, r *http.Request, 
inf *api.APIInfo, dsV40 *
                        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,
-                       &ds.ID)
+                       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,
+                       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,
-                       &ds.LongDesc1,
-                       &ds.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,
-                       &ds.ID)
+                       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.LongDesc1,
+                       ds.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,
+                       ds.ID,
+               )
        }
 
        if err != nil {
@@ -1048,16 +1140,16 @@ func updateV40(w http.ResponseWriter, r *http.Request, 
inf *api.APIInfo, dsV40 *
                return nil, http.StatusInternalServerError, nil, 
errors.New("writing change log entry: " + err.Error())
        }
 
-       dsV40 = (*tc.DeliveryServiceV40)(&ds)
+       dsV4 = &ds
 
        if inf.Config.TrafficVaultEnabled && ds.Protocol != nil && 
(*ds.Protocol == tc.DSProtocolHTTPS || *ds.Protocol == 
tc.DSProtocolHTTPAndHTTPS || *ds.Protocol == tc.DSProtocolHTTPToHTTPS) {
-               err, errCode := GeneratePlaceholderSelfSignedCert(*dsV40, inf, 
r.Context())
+               err, errCode := GeneratePlaceholderSelfSignedCert(*dsV4, inf, 
r.Context())
                if err != nil || errCode != http.StatusOK {
                        return nil, errCode, nil, fmt.Errorf("creating self 
signed default cert: %v", err)
                }
        }
 
-       return dsV40, http.StatusOK, nil, nil
+       return dsV4, http.StatusOK, nil, nil
 }
 
 // Delete is the DeliveryService implementation of the Deleter interface.
@@ -1589,7 +1681,8 @@ func GetDeliveryServices(query string, queryValues 
map[string]interface{}, tx *s
        for rows.Next() {
                ds := tc.DeliveryServiceV4{}
                cdnDomain := ""
-               err := rows.Scan(&ds.Active,
+               err := rows.Scan(
+                       &ds.Active,
                        &ds.AnonymousBlockingEnabled,
                        &ds.CCRDNSTTL,
                        &ds.CDNID,
diff --git a/traffic_ops/traffic_ops_golang/deliveryservice/safe.go 
b/traffic_ops/traffic_ops_golang/deliveryservice/safe.go
index 9f890cff1c..0b000a9511 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/safe.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/safe.go
@@ -145,7 +145,11 @@ func UpdateSafe(w http.ResponseWriter, r *http.Request) {
                default:
                        fallthrough
                case 4:
-                       api.WriteRespAlertObj(w, r, tc.SuccessLevel, alertMsg, 
dses)
+                       if inf.Version.Minor >= 1 {
+                               api.WriteRespAlertObj(w, r, tc.SuccessLevel, 
alertMsg, dses)
+                       } else {
+                               api.WriteRespAlertObj(w, r, tc.SuccessLevel, 
alertMsg, []tc.DeliveryServiceV40{ds.DeliveryServiceV40})
+                       }
                case 3:
                        if inf.Version.Minor >= 1 {
                                api.WriteRespAlertObj(w, r, tc.SuccessLevel, 
alertMsg, []tc.DeliveryServiceV31{tc.DeliveryServiceV31(ds.DowngradeToV31())})
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go 
b/traffic_ops/traffic_ops_golang/routing/routes.go
index a19457bd2e..387e015cd6 100644
--- a/traffic_ops/traffic_ops_golang/routing/routes.go
+++ b/traffic_ops/traffic_ops_golang/routing/routes.go
@@ -484,8 +484,8 @@ func Routes(d ServerData) ([]Route, http.Handler, error) {
 
                ////DeliveryServices
                {Version: api.Version{Major: 5, Minor: 0}, Method: 
http.MethodGet, Path: `deliveryservices/?$`, Handler: 
api.ReadHandler(&deliveryservice.TODeliveryService{}), RequiredPrivLevel: 
auth.PrivLevelReadOnly, RequiredPermissions: []string{"DELIVERY-SERVICE:READ", 
"CDN:READ", "TYPE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 
423831729431},
-               {Version: api.Version{Major: 5, Minor: 0}, Method: 
http.MethodPost, Path: `deliveryservices/?$`, Handler: 
deliveryservice.CreateV40, RequiredPrivLevel: auth.PrivLevelOperations, 
RequiredPermissions: []string{"DELIVERY-SERVICE:CREATE", 
"DELIVERY-SERVICE:READ", "CDN:READ", "TYPE:READ"}, Authenticated: 
Authenticated, Middlewares: nil, ID: 40643153231},
-               {Version: api.Version{Major: 5, Minor: 0}, Method: 
http.MethodPut, Path: `deliveryservices/{id}/?$`, Handler: 
deliveryservice.UpdateV40, RequiredPrivLevel: auth.PrivLevelOperations, 
RequiredPermissions: []string{"DELIVERY-SERVICE:UPDATE", 
"DELIVERY-SERVICE:READ", "CDN:READ", "TYPE:READ"}, Authenticated: 
Authenticated, Middlewares: nil, ID: 476656756731},
+               {Version: api.Version{Major: 5, Minor: 0}, Method: 
http.MethodPost, Path: `deliveryservices/?$`, Handler: 
deliveryservice.CreateV41, RequiredPrivLevel: auth.PrivLevelOperations, 
RequiredPermissions: []string{"DELIVERY-SERVICE:CREATE", 
"DELIVERY-SERVICE:READ", "CDN:READ", "TYPE:READ"}, Authenticated: 
Authenticated, Middlewares: nil, ID: 40643153231},
+               {Version: api.Version{Major: 5, Minor: 0}, Method: 
http.MethodPut, Path: `deliveryservices/{id}/?$`, Handler: 
deliveryservice.UpdateV41, RequiredPrivLevel: auth.PrivLevelOperations, 
RequiredPermissions: []string{"DELIVERY-SERVICE:UPDATE", 
"DELIVERY-SERVICE:READ", "CDN:READ", "TYPE:READ"}, Authenticated: 
Authenticated, Middlewares: nil, ID: 476656756731},
                {Version: api.Version{Major: 5, Minor: 0}, Method: 
http.MethodPut, Path: `deliveryservices/{id}/safe/?$`, Handler: 
deliveryservice.UpdateSafe, RequiredPrivLevel: auth.PrivLevelUnauthenticated, 
RequiredPermissions: []string{"DELIVERY-SERVICE-SAFE:UPDATE", 
"DELIVERY-SERVICE:READ", "TYPE:READ"}, Authenticated: Authenticated, 
Middlewares: nil, ID: 44721093131},
                {Version: api.Version{Major: 5, Minor: 0}, Method: 
http.MethodDelete, Path: `deliveryservices/{id}/?$`, Handler: 
api.DeleteHandler(&deliveryservice.TODeliveryService{}), RequiredPrivLevel: 
auth.PrivLevelOperations, RequiredPermissions: 
[]string{"DELIVERY-SERVICE:DELETE", "DELIVERY-SERVICE:READ", "CDN:READ", 
"TYPE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 42264207431},
                {Version: api.Version{Major: 5, Minor: 0}, Method: 
http.MethodGet, Path: `deliveryservices/{id}/servers/eligible/?$`, Handler: 
deliveryservice.GetServersEligible, RequiredPrivLevel: auth.PrivLevelReadOnly, 
RequiredPermissions: []string{"DELIVERY-SERVICE:READ", "SERVER:READ", 
"CACHE-GROUP:READ", "TYPE:READ", "CDN:READ"}, Authenticated: Authenticated, 
Middlewares: nil, ID: 47476158431},
@@ -886,6 +886,8 @@ func Routes(d ServerData) ([]Route, http.Handler, error) {
 
                ////DeliveryServices
                {Version: api.Version{Major: 4, Minor: 0}, Method: 
http.MethodGet, Path: `deliveryservices/?$`, Handler: 
api.ReadHandler(&deliveryservice.TODeliveryService{}), RequiredPrivLevel: 
auth.PrivLevelReadOnly, RequiredPermissions: []string{"DELIVERY-SERVICE:READ", 
"CDN:READ", "TYPE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID: 
42383172943},
+               {Version: api.Version{Major: 4, Minor: 1}, Method: 
http.MethodPost, Path: `deliveryservices/?$`, Handler: 
deliveryservice.CreateV41, RequiredPrivLevel: auth.PrivLevelOperations, 
RequiredPermissions: []string{"DELIVERY-SERVICE:CREATE", 
"DELIVERY-SERVICE:READ", "CDN:READ", "TYPE:READ"}, Authenticated: 
Authenticated, Middlewares: nil, ID: 4064315324},
+               {Version: api.Version{Major: 4, Minor: 1}, Method: 
http.MethodPut, Path: `deliveryservices/{id}/?$`, Handler: 
deliveryservice.UpdateV41, RequiredPrivLevel: auth.PrivLevelOperations, 
RequiredPermissions: []string{"DELIVERY-SERVICE:UPDATE", 
"DELIVERY-SERVICE:READ", "CDN:READ", "TYPE:READ"}, Authenticated: 
Authenticated, Middlewares: nil, ID: 47665675674},
                {Version: api.Version{Major: 4, Minor: 0}, Method: 
http.MethodPost, Path: `deliveryservices/?$`, Handler: 
deliveryservice.CreateV40, RequiredPrivLevel: auth.PrivLevelOperations, 
RequiredPermissions: []string{"DELIVERY-SERVICE:CREATE", 
"DELIVERY-SERVICE:READ", "CDN:READ", "TYPE:READ"}, Authenticated: 
Authenticated, Middlewares: nil, ID: 4064315323},
                {Version: api.Version{Major: 4, Minor: 0}, Method: 
http.MethodPut, Path: `deliveryservices/{id}/?$`, Handler: 
deliveryservice.UpdateV40, RequiredPrivLevel: auth.PrivLevelOperations, 
RequiredPermissions: []string{"DELIVERY-SERVICE:UPDATE", 
"DELIVERY-SERVICE:READ", "CDN:READ", "TYPE:READ"}, Authenticated: 
Authenticated, Middlewares: nil, ID: 47665675673},
                {Version: api.Version{Major: 4, Minor: 0}, Method: 
http.MethodPut, Path: `deliveryservices/{id}/safe/?$`, Handler: 
deliveryservice.UpdateSafe, RequiredPrivLevel: auth.PrivLevelUnauthenticated, 
RequiredPermissions: []string{"DELIVERY-SERVICE-SAFE:UPDATE", 
"DELIVERY-SERVICE:READ", "TYPE:READ"}, Authenticated: Authenticated, 
Middlewares: nil, ID: 4472109313},
diff --git a/traffic_router/ultimate-test-harness/http_test.go 
b/traffic_router/ultimate-test-harness/http_test.go
index 140a2976c6..f878768086 100644
--- a/traffic_router/ultimate-test-harness/http_test.go
+++ b/traffic_router/ultimate-test-harness/http_test.go
@@ -347,7 +347,7 @@ func getTrafficRouters(trafficRouterName string, cdnName 
tc.CDNName) ([]tc.Serve
        return trafficRoutersV40, nil
 }
 
-func getDSes(t *testing.T, cdnId int, dsTypeName tc.DSType, dsName 
tc.DeliveryServiceName) []tc.DeliveryServiceV40 {
+func getDSes(t *testing.T, cdnId int, dsTypeName tc.DSType, dsName 
tc.DeliveryServiceName) []tc.DeliveryServiceV4 {
        requestOptions := client.RequestOptions{QueryParameters: 
url.Values{"name": {dsTypeName.String()}}}
        var dsType tc.Type
        {


Reply via email to