Skip to content
This repository was archived by the owner on Nov 24, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions lib/go-tc/deliveryservices.go
Original file line number Diff line number Diff line change
Expand Up @@ -599,3 +599,33 @@ type AssignedDsResponse struct {
DSIds []int `json:"dsIds"`
Replace bool `json:"replace"`
}

// DeliveryServiceSafeUpdate is the type deserialized from a PUT deliveryservices/{{ID}}/safe request.
type DeliveryServiceSafeUpdate struct {
DisplayName *string `json:"displayName"`
InfoURL *string `json:"infoUrl"`
LongDesc *string `json:"longDesc"`
LongDesc1 *string `json:"longDesc1"`
}

// Sanitize sets any nil member pointer to a default-initialized value.
func (ds *DeliveryServiceSafeUpdate) Sanitize() {
if ds.DisplayName == nil {
ds.DisplayName = util.StrPtr("")
}
if ds.InfoURL == nil {
ds.InfoURL = util.StrPtr("")
}
if ds.LongDesc == nil {
ds.LongDesc = util.StrPtr("")
}
if ds.LongDesc1 == nil {
ds.LongDesc1 = util.StrPtr("")
}
}

// Validate calls Sanitize and returns nil. There is nothing to validate for this object. This fulfills the traffic_ops_golang.api.ParseValidator interface.
func (ds *DeliveryServiceSafeUpdate) Validate(tx *sql.Tx) error {
ds.Sanitize()
return nil
}
20 changes: 20 additions & 0 deletions traffic_ops/client/deliveryservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,3 +454,23 @@ func (to *Session) GetDeliveryServiceURISigningKeys(dsName string) ([]byte, ReqI
}
return []byte(data), reqInf, nil
}

// UpdateDeliveryServiceSafe updates the given Delivery Service ID with the given safe fields.
func (to *Session) UpdateDeliveryServiceSafe(id int, ds *tc.DeliveryServiceSafeUpdate) (*tc.DeliveryServiceNullable, error) {
path := apiBase + `/deliveryservices/` + strconv.Itoa(id) + `/safe`
resp := struct {
Response []tc.DeliveryServiceNullable
}{}

req, err := json.Marshal(ds)
if err != nil {
return nil, err
}
if _, err = put(to, path, req, &resp); err != nil {
return nil, err
}
if len(resp.Response) < 1 {
return nil, errors.New("Traffic Ops returned success, but response was missing the delivery service")
}
return &resp.Response[0], nil
}
75 changes: 75 additions & 0 deletions traffic_ops/testing/api/v14/deliveryservicesafe_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package v14

/*

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import (
"strconv"
"testing"

"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/lib/go-util"
)

func TestPutDeliveryServiceSafe(t *testing.T) {
WithObjs(t, []TCObj{CDNs, Types, Tenants, Users, Parameters, Profiles, Statuses, Divisions, Regions, PhysLocations, CacheGroups, Servers, DeliveryServices}, func() {
UpdateTestDeliveryServiceSafe(t)
})
}

func UpdateTestDeliveryServiceSafe(t *testing.T) {
firstDS := testData.DeliveryServices[0]

dses, _, err := TOSession.GetDeliveryServices()
if err != nil {
t.Fatalf("cannot GET Delivery Services: %v\n", err)
}

remoteDS := tc.DeliveryService{}
found := false
for _, ds := range dses {
if ds.XMLID == firstDS.XMLID {
found = true
remoteDS = ds
break
}
}
if !found {
t.Fatalf("GET Delivery Services missing: %v\n", firstDS.XMLID)
}

req := tc.DeliveryServiceSafeUpdate{
DisplayName: util.StrPtr("safe update display name"),
InfoURL: util.StrPtr("safe update info URL"),
LongDesc: util.StrPtr("safe update long desc"),
LongDesc1: util.StrPtr("safe update long desc one"),
}

if _, err := TOSession.UpdateDeliveryServiceSafe(remoteDS.ID, &req); err != nil {
t.Fatalf("cannot UPDATE DeliveryService safe: %v\n", err)
}

updatedDS, _, err := TOSession.GetDeliveryService(strconv.Itoa(remoteDS.ID))
if err != nil {
t.Fatalf("cannot GET Delivery Service by ID: id %v xmlid '%v' - %v\n", remoteDS.ID, remoteDS.XMLID, err)
}
if updatedDS == nil {
t.Fatalf("GET Delivery Service by ID returned nil err, but nil DS: %v - nil\n", remoteDS.XMLID)
}

if *req.DisplayName != updatedDS.DisplayName || *req.InfoURL != updatedDS.InfoURL || *req.LongDesc != updatedDS.LongDesc || *req.LongDesc1 != updatedDS.LongDesc1 {
t.Fatalf("ds safe update succeeded, but get delivery service didn't match. expected: {'%v' '%v' '%v' '%v'} actual: {'%v' '%v' '%v' '%v'}\n", *req.DisplayName, *req.InfoURL, *req.LongDesc, *req.LongDesc1, updatedDS.DisplayName, updatedDS.InfoURL, updatedDS.LongDesc, updatedDS.LongDesc1)
Comment thread
ocket8888 marked this conversation as resolved.
}
}
128 changes: 128 additions & 0 deletions traffic_ops/traffic_ops_golang/deliveryservice/safe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package deliveryservice

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import (
"database/sql"
"errors"
"fmt"
"net/http"

"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant"
)

func UpdateSafeV14(w http.ResponseWriter, r *http.Request) {
ds, ok := UpdateSafe(w, r)
if !ok {
return
}
api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice safe update was successful.", []tc.DeliveryServiceNullableV14{tc.DeliveryServiceNullableV14(ds)})
}

func UpdateSafeV13(w http.ResponseWriter, r *http.Request) {
Comment thread
ocket8888 marked this conversation as resolved.
Outdated
ds, ok := UpdateSafe(w, r)
if !ok {
return
}
api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice safe update was successful.", []tc.DeliveryServiceNullableV13{ds.DeliveryServiceNullableV13})
}

func UpdateSafeV12(w http.ResponseWriter, r *http.Request) {
ds, ok := UpdateSafe(w, r)
if !ok {
return
}
api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice safe update was successful.", []tc.DeliveryServiceNullableV12{ds.DeliveryServiceNullableV12})
}

// UpdateSafe updates the delivery service, writing any errors. Returns true on success, or false on error. If an error occured, it will be written to the client and logged appropriately, and the caller shouldn't write anything further. On success, the caller should write the delivery service response to the client.
func UpdateSafe(w http.ResponseWriter, r *http.Request) (tc.DeliveryServiceNullable, bool) {
inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"id"}, []string{"id"})
if userErr != nil || sysErr != nil {
api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
return tc.DeliveryServiceNullable{}, false
}
defer inf.Close()

dsID := inf.IntParams["id"]

userErr, sysErr, errCode = tenant.CheckID(inf.Tx.Tx, inf.User, dsID)
if userErr != nil || sysErr != nil {
api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
return tc.DeliveryServiceNullable{}, false
}

ds := tc.DeliveryServiceSafeUpdate{}
if err := api.Parse(r.Body, inf.Tx.Tx, &ds); err != nil {
api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest, errors.New("decoding: "+err.Error()), nil)
return tc.DeliveryServiceNullable{}, false
}

ok, err := updateDSSafe(inf.Tx.Tx, dsID, ds)
if err != nil {
api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, errors.New("updating delivery service safe: "+err.Error()))
return tc.DeliveryServiceNullable{}, false
}
if !ok {
api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound, nil, nil)
return tc.DeliveryServiceNullable{}, false
}

dses, userErr, sysErr, errCode := readGetDeliveryServices(inf.Params, inf.Tx, inf.User)
if userErr != nil || sysErr != nil {
api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
return tc.DeliveryServiceNullable{}, false
}
if len(dses) != 1 {
api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError, nil, fmt.Errorf("delivery service safe update, read expected 1 delivery service, got %v", len(dses)))
return tc.DeliveryServiceNullable{}, false
}
return dses[0], true
}

// updateDSSafe updates the given delivery service in the database. Returns whether the DS existed, and any error.
func updateDSSafe(tx *sql.Tx, dsID int, ds tc.DeliveryServiceSafeUpdate) (bool, error) {
q := `
UPDATE deliveryservice SET
display_name=$1,
info_url=$2,
long_desc=$3,
long_desc_1=$4
WHERE id = $5
RETURNING id
`
res, err := tx.Exec(q, ds.DisplayName, ds.InfoURL, ds.LongDesc, ds.LongDesc1, dsID)
Comment thread
ocket8888 marked this conversation as resolved.
if err != nil {
return false, errors.New("updating delivery service safe: " + err.Error())
}
rowsAffected, err := res.RowsAffected()
if err != nil {
return false, errors.New("updating delivery service safe, checking rows affected: " + err.Error())
}
if rowsAffected < 1 {
return false, nil
}
if rowsAffected > 1 {
return false, fmt.Errorf("updating delivery service safe, too many rows affected: %v", rowsAffected)
}
return true, nil
}
4 changes: 4 additions & 0 deletions traffic_ops/traffic_ops_golang/routing/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,10 @@ func Routes(d ServerData) ([]Route, []RawRoute, http.Handler, error) {
{1.3, http.MethodPost, `deliveryservices/?(\.json)?$`, deliveryservice.CreateV13, auth.PrivLevelOperations, Authenticated, nil, 1705681904, noPerlBypass},
{1.1, http.MethodPost, `deliveryservices/?(\.json)?$`, deliveryservice.CreateV12, auth.PrivLevelOperations, Authenticated, nil, 652813412, noPerlBypass},

{1.4, http.MethodPut, `deliveryservices/{id}/safe/?(\.json)?$`, deliveryservice.UpdateSafeV14, auth.PrivLevelOperations, Authenticated, nil, 547210934, noPerlBypass},
{1.3, http.MethodPut, `deliveryservices/{id}/safe/?(\.json)?$`, deliveryservice.UpdateSafeV13, auth.PrivLevelOperations, Authenticated, nil, 547210933, noPerlBypass},
{1.1, http.MethodPut, `deliveryservices/{id}/safe/?(\.json)?$`, deliveryservice.UpdateSafeV12, auth.PrivLevelOperations, Authenticated, nil, 547210931, noPerlBypass},
Comment thread
ocket8888 marked this conversation as resolved.

{1.4, http.MethodPut, `deliveryservices/{id}/?(\.json)?$`, deliveryservice.UpdateV14, auth.PrivLevelOperations, Authenticated, nil, 1766567526, noPerlBypass},
{1.3, http.MethodPut, `deliveryservices/{id}/?(\.json)?$`, deliveryservice.UpdateV13, auth.PrivLevelOperations, Authenticated, nil, 1559124565, noPerlBypass},
{1.1, http.MethodPut, `deliveryservices/{id}/?(\.json)?$`, deliveryservice.UpdateV12, auth.PrivLevelOperations, Authenticated, nil, 597160536, noPerlBypass},
Expand Down
2 changes: 1 addition & 1 deletion traffic_ops/traffic_ops_golang/tenant/tenancy.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func GetDeliveryServiceTenantInfoID(tx *sql.Tx, dsID int) (*DeliveryServiceTenan
ds.ID = util.IntPtr(dsID)
if err := tx.QueryRow(`SELECT tenant_id FROM deliveryservice where id = $1`, &ds.ID).Scan(&ds.TenantID); err != nil {
if err == sql.ErrNoRows {
return &ds, errors.New("a deliveryservice with id '" + strconv.Itoa(dsID) + "' was not found")
return &ds, fmt.Errorf("a deliveryservice with id %v was not found", dsID)
}
return nil, errors.New("querying tenant id from delivery service: " + err.Error())
}
Expand Down