This is an automated email from the ASF dual-hosted git repository.
dangogh 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 5120f76 Move TO DS nonversioned logic to separate file. (#3493)
5120f76 is described below
commit 5120f7612748d98d7a46564e95a0053fa796517e
Author: Robert Butts <[email protected]>
AuthorDate: Mon Apr 22 21:37:57 2019 -0600
Move TO DS nonversioned logic to separate file. (#3493)
* Change TO dses to separate versions
Puts all the logic in deliveryservices.go, and makes v12.go, v13.go,
v14.go purely boilerplate.
This should make it significantly easier to add new versions: you'll
only have to copy the previous v13.go file, rename v13->v14, and
add your logic to the main deliveryservices.go
Also adds a section on adding new versions to the README.md.
* Remove TO Nullable omitempty
Per PR Review.
---
lib/go-tc/deliveryservices.go | 22 +-
traffic_ops/traffic_ops_golang/README.md | 115 ++-
...{deliveryservicesv13.go => deliveryservices.go} | 471 +++++----
.../deliveryservice/deliveryservicesv12.go | 209 +---
.../deliveryservice/deliveryservicesv13.go | 1011 +-------------------
.../deliveryservice/deliveryservicesv14.go | 170 ----
traffic_ops/traffic_ops_golang/routing/routes.go | 10 +-
7 files changed, 425 insertions(+), 1583 deletions(-)
diff --git a/lib/go-tc/deliveryservices.go b/lib/go-tc/deliveryservices.go
index 7bc7865..128bda3 100644
--- a/lib/go-tc/deliveryservices.go
+++ b/lib/go-tc/deliveryservices.go
@@ -78,7 +78,7 @@ type DeliveryService struct {
}
type DeliveryServiceV13 struct {
- DeliveryServiceV12
+ DeliveryServiceV11
DeepCachingType DeepCachingType `json:"deepCachingType"`
FQPacingRate int `json:"fqPacingRate,omitempty"`
SigningAlgorithm string `json:"signingAlgorithm"
db:"signing_algorithm"`
@@ -87,10 +87,6 @@ type DeliveryServiceV13 struct {
TRResponseHeaders string `json:"trResponseHeaders,omitempty"`
}
-type DeliveryServiceV12 struct {
- DeliveryServiceV11
-}
-
// DeliveryService ...
// TODO move contents to DeliveryServiceV12, fix references, and remove
type DeliveryServiceV11 struct {
@@ -152,18 +148,18 @@ type DeliveryServiceV11 struct {
type DeliveryServiceNullable struct {
DeliveryServiceNullableV13
- MaxOriginConnections *int `json:"maxOriginConnections"
db:"max_origin_connections"`
+ ConsistentHashRegex *string `json:"consistentHashRegex"`
+ MaxOriginConnections *int `json:"maxOriginConnections"
db:"max_origin_connections"`
}
type DeliveryServiceNullableV13 struct {
DeliveryServiceNullableV12
- ConsistentHashRegex *string
`json:"consistentHashRegex,omitempty"`
- DeepCachingType *DeepCachingType `json:"deepCachingType"
db:"deep_caching_type"`
- FQPacingRate *int `json:"fqPacingRate,omitempty"`
- SigningAlgorithm *string `json:"signingAlgorithm"
db:"signing_algorithm"`
- Tenant *string `json:"tenant,omitempty"`
- TRResponseHeaders *string
`json:"trResponseHeaders,omitempty"`
- TRRequestHeaders *string `json:"trRequestHeaders,omitempty"`
+ DeepCachingType *DeepCachingType `json:"deepCachingType"
db:"deep_caching_type"`
+ FQPacingRate *int `json:"fqPacingRate"`
+ SigningAlgorithm *string `json:"signingAlgorithm"
db:"signing_algorithm"`
+ Tenant *string `json:"tenant"`
+ TRResponseHeaders *string `json:"trResponseHeaders"`
+ TRRequestHeaders *string `json:"trRequestHeaders"`
}
type DeliveryServiceNullableV12 struct {
diff --git a/traffic_ops/traffic_ops_golang/README.md
b/traffic_ops/traffic_ops_golang/README.md
index ac562cb..a368998 100644
--- a/traffic_ops/traffic_ops_golang/README.md
+++ b/traffic_ops/traffic_ops_golang/README.md
@@ -17,8 +17,9 @@
under the License.
-->
-Prequisites
-=======================================
+# Running
+
+## Prequisites
To run `traffic_ops_golang` proxy locally the following prerequisites are
needed:
@@ -27,8 +28,7 @@ To run `traffic_ops_golang` proxy locally the following
prerequisites are needed
* Because the Golang proxy is fronting Mojolicious Perl you need to have that
service setup and running as well [TO Perl Setup
Here](https://github.com/apache/trafficcontrol/blob/master/traffic_ops/INSTALL.md)
-Vendoring and Building
-=======================================
+## Vendoring and Building
### vendoring
We treat `golang.org/x` as a part of the Go compiler so that means that we
still vendor application dependencies for stability and reproducible builds.
The [govend](https://github.com/govend/govend) tool is helpful for managing
dependencies.
@@ -38,8 +38,8 @@ To download the remaining `golang.org/x` dependencies you
need to:
`$ go get -v`
-Configuration
-=======================================
+## Configuration
+
To run the Golang proxy locally the following represents a typical sequence
flow. */api/1.2* will proxy through to Mojo Perl. */api/1.3* will serve the
response from the Golang proxy directly and/or interact with Postgres
accordingly.
**/api/1.2** routes:
@@ -50,26 +50,24 @@ To run the Golang proxy locally the following represents a
typical sequence flow
`TO Golang Proxy (port 8443)`<-->`TO Database (Postgres)`
-### cdn.conf changes
-=======================================
+### cdn.conf changes
Copy `traffic_ops/app/conf/cdn.conf` to `$HOME/cdn.conf` so you can modify it
for development purposes.
`$HOME/cdn.conf`
-```
+```
"traffic_ops_golang" : {
"port" : "443",
```
-```
+```
"traffic_ops_golang" : {
"port" : "8443",
```
-### Logging
-=======================================
+## Logging
By default `/var/log/traffic_ops/error.log` is configured for output, to
change this modify your `$HOME/cdn.conf` for the following:
@@ -87,16 +85,86 @@ By default `/var/log/traffic_ops/error.log` is configured
for output, to change
}
```
-Development
-=======================================
+# Development
Go is a compiled language so any local changes will require you to CTRL-C the
console and re-run the `traffic_ops_golang` Go binary locally:
`go build && ./traffic_ops_golang -cfg $HOME/cdn.conf -dbcfg
../app/conf/development/database.conf`
+## Updating a Minor Version
+
+Traffic Control implements [Semantic Versioning](https://semver.org). When
adding new fields to the API, we must increase the minor version. If you're the
first one adding a new field to a particular object in a particular release,
you'll need to do this.
+
+The structs with no version in the name are the latest version.
+
+Most structs do not have versioning. If you are adding a field to a struct
with no existing versioning. see `lib/go-tc/deliveryservices.go` for an example.
+
+1. In `lib/go-tc`, rename the old struct to be the previous minor version.
+ - For example, if you are adding a field to Delivery Service and existing
minor version is 1.4 (so your new minor version is 1.5), in
`lib/go-tc/deliveryservices.go` rename `type DeliveryServiceNullable struct` to
`type DeliveryServiceNullableV14 struct`.
+ - Also rename any `Sanitize` and `Validate` functions to the old object.
+
+2. In `lib/go-tc`, create a new struct with an unversioned name, and
anonymously embed the previous struct (that you just renamed), along with your
new field.
+ - For example:
+```go
+type DeliveryServiceNullable struct {
+ DeliveryServiceNullableV14
+ MyNewField *int `json:"myNewField" db:"my_new_field"`
+}
+```
+
+3. Create a `Sanitize` function on the new struct, e.g. `func (ds
*DeliveryServiceNullable) Sanitize()`, which sets your new field to a default
value, if it is null.
+ - It must always be possible to create objects with previous API versions.
Therefore, this step is not optional.
+ - The new `Sanitize` function must call the previous version's `Sanitize`
as well, in order to sanitize all previous versions. E.g.
+```go
+ func (ds *DeliveryServiceNullable) Sanitize() {
+ ds.DeliveryServiceNullableV14.Sanitize()
+```
+
+4. Create a `Validate` function, which immediately calls the `Sanitize`
function, as well as doing any other validation on your new field.
+ - `Validate` is used to `Sanitize` by the API frameworks. If a `Validate`
function doesn't exist, your new field won't be checked and made valid, and may
result in nil panics. Therefore, this step is not optional.
+ - For example, if your new field is a port, `Validate` should verify it is
between 0 and 65535.
+ - Almost all fields can be invalid! Don't skip this step. Proper
validation is essential to Traffic Control functioning properly and rejecting
invalid input.
+
+ For example:
+
+```go
+func (ds *DeliveryServiceNullableV14) Validate(tx *sql.Tx) error {
+ ds.Sanitize()
+```
+
+
+5. Create a func to convert the previous version to the new latest struct. For
example, `func NewDeliveryServiceNullableFromV14(ds DeliveryServiceNullableV14)
DeliveryServiceNullable`. This function will typically do nothing more than
create the latest object with the older version, and sanitize new fields. E.g.
+```go
+func NewDeliveryServiceNullableFromV14(ds DeliveryServiceNullableV14)
DeliveryServiceNullable {
+ newDS := DeliveryServiceNullable{DeliveryServiceNullableV14: ds}
+ newDS.Sanitize()
+ return newDS
+}
+```
+
+6. In `traffic_ops/traffic_ops_golang`, copy the existing previous version
file, e.g. `cp
traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv1{3,4}.go`.
+ - If the object has no previous version, see `deliveryservice` for an
example. The "CRUDer" version file should contain only boilerplate, no logic,
and no reference to other versions except the latest. Hence, it should be
possible to copy and rename, with no logic changes. The logic and latest
version should all be in the main file, e.g.
`deliveryservice/deliveryservices.go`.
+
+7. In the new version file, rename all instances of the previous version to
the new version, e.g. `sed -i 's/v13/v14/' deliveryservicesv14.go`.
+
+8. Add the logic for your new field to the latest version file, e.g.
`deliveryservice/deliveryservices.go`.
+
+9. Add your new version to `traffic_ops/traffic_ops_golang/routing/routes.go`,
and add the versioned object to the previous route.
+ - The new latest route must go above the previous version. If the new
version is below the old, the new version will never be routed to!
+
+ For example, Change:
+```go
+{1.4, http.MethodGet, `deliveryservices/{id}/?(\.json)?$`,
api.ReadHandler(&deliveryservice.TODeliveryService{}), auth.PrivLevelReadOnly,
Authenticated, nil},
+```
+
+ To:
+
+```go
+{1.5, http.MethodGet, `deliveryservices/{id}/?(\.json)?$`,
api.ReadHandler(&deliveryservice.TODeliveryService{}), auth.PrivLevelReadOnly,
Authenticated, nil},
+{1.4, http.MethodGet, `deliveryservices/{id}/?(\.json)?$`,
api.ReadHandler(&deliveryservice.TODeliveryServiceV14{}),
auth.PrivLevelReadOnly, Authenticated, nil},
+```
-Converting Routes to Traffic Ops Golang
-=======================================
+## Converting Routes to Traffic Ops Golang
Traffic Ops is moving to Go! You can help!
@@ -104,11 +172,9 @@ We're in the process of migrating the Perl/Mojolicious
Traffic Ops to Go. This i
You'll need at least a basic understanding of Perl and Go, or be willing to
learn them. You'll also need a running Traffic Ops instance, to compare the old
and new routes and make sure they're identical.
-Converting an Endpoint
-======================
+### Converting an Endpoint
-Perl
-----
+#### Perl
If you don't already have an endpoint in mind, open
[TrafficOpsRoutes.pm](../app/lib/TrafficOpsRoutes.pm) and browse the routes.
Start with `/api/` routes. We'll be moving others, like config files, but
they're a bit more complex. We specifically won't be moving GUI routes (e.g.
`/asns`), they'll go away when the new
[Portal](https://github.com/apache/trafficcontrol/tree/master/traffic_portal)
is done.
@@ -118,16 +184,15 @@ As you can see, this is a very simple route. It queries
the database `CDN` table
If you go to `/api/1.2/cdns` in a browser, you'll see Perl is also wrapping it
in a `"response"` object.
-Go
---
+#### Go
Now we need to create the Go endpoint.
-#### Getting a "Handle" on Routes
+##### Getting a "Handle" on Routes
Open [routes.go](./routing/routes.go). Routes are defined in the `Routes`
function, of the form `{version, method, path, handler}`. Notice the path can
contain variables, of the form `/{var}/`. These variables will be made
available to your handler.
-#### Creating a Handler
+##### Creating a Handler
The first step is to create your handler. For an example, look at
`monitoringHandler` in `monitoring.go`. Your handler arguments can be any data
available to the router (the config and database, or what you can create from
them). Passing the `db` or prepared `Stmt`s is common. The handler function
must return a `RegexHandlerFunc`. In general, you want to return an inline
function, `return func(w http.ResponseWriter, r *http.Request, p ParamMap)
{...`.
@@ -139,7 +204,7 @@ This is the hard part, where you have to recreate the Perl
response. But it's al
Your handler should be in its own file, where you can create any structs and
helper functions you need.
-#### Registering the Handler
+##### Registering the Handler
Back to `routes.go`, you need to add your handler to the `Routes` function.
For example, `/api/1.2/cdns` would look like `{1.2, http.MethodGet, "cdns",
wrapHeaders(wrapAuth(cdnsHandler(d.DB), d.Insecure, d.TOSecret,
rd.PrivLevelStmt, CdnsPrivLevel))},`.
diff --git
a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv13.go
b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
similarity index 87%
copy from traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv13.go
copy to traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
index 9d5759b..84814ff 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv13.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservices.go
@@ -44,60 +44,32 @@ import (
//we need a type alias to define functions on
-type TODeliveryServiceV13 struct {
+type TODeliveryService struct {
api.APIInfoImpl
- tc.DeliveryServiceNullableV13
+ tc.DeliveryServiceNullable
}
-func (ds *TODeliveryServiceV13) V12() *TODeliveryServiceV12 {
- v12 := &TODeliveryServiceV12{}
- v12.DeliveryServiceNullableV12 = ds.DeliveryServiceNullableV12
- v12.SetInfo(ds.ReqInfo)
- return v12
+func (ds TODeliveryService) MarshalJSON() ([]byte, error) {
+ return json.Marshal(ds.DeliveryServiceNullable)
}
-func (ds TODeliveryServiceV13) MarshalJSON() ([]byte, error) {
- return json.Marshal(ds.DeliveryServiceNullableV13)
-}
-func (ds *TODeliveryServiceV13) UnmarshalJSON(data []byte) error {
- return json.Unmarshal(data, ds.DeliveryServiceNullableV13)
-}
-
-func (ds *TODeliveryServiceV13) APIInfo() *api.APIInfo { return ds.ReqInfo }
-
-func (ds TODeliveryServiceV13) GetKeyFieldsInfo() []api.KeyFieldInfo {
- return ds.V12().GetKeyFieldsInfo()
+func (ds *TODeliveryService) UnmarshalJSON(data []byte) error {
+ return json.Unmarshal(data, ds.DeliveryServiceNullable)
}
-//Implementation of the Identifier, Validator interface functions
-func (ds TODeliveryServiceV13) GetKeys() (map[string]interface{}, bool) {
- return ds.V12().GetKeys()
-}
+func (ds *TODeliveryService) APIInfo() *api.APIInfo { return ds.ReqInfo }
-func (ds *TODeliveryServiceV13) SetKeys(keys map[string]interface{}) {
+func (ds *TODeliveryService) SetKeys(keys map[string]interface{}) {
i, _ := keys["id"].(int) //this utilizes the non panicking type
assertion, if the thrown away ok variable is false i will be the zero of the
type, 0 here.
ds.ID = &i
}
-func (ds *TODeliveryServiceV13) GetAuditName() string {
- return ds.V12().GetAuditName()
-}
-
-func (ds *TODeliveryServiceV13) GetType() string {
- return ds.V12().GetType()
-}
-
-func (ds *TODeliveryServiceV13) Validate() error {
- return ds.DeliveryServiceNullableV13.Validate(ds.APIInfo().Tx.Tx)
-}
-
-// Create is unimplemented, needed to satisfy CRUDer, since the framework
doesn't allow a create to return an array of one
-func (ds *TODeliveryServiceV13) Create() (error, error, int) {
- return nil, nil, http.StatusNotImplemented
+func (ds *TODeliveryService) Validate() error {
+ return ds.DeliveryServiceNullable.Validate(ds.APIInfo().Tx.Tx)
}
// TODO allow users to post names (type, cdn, etc) and get the IDs from
the names. This isn't trivial to do in a single query, without dynamically
building the entire insert query, and ideally inserting would be one query. But
it'd be much more convenient for users. Alternatively, remove IDs from the
database entirely and use real candidate keys.
-func CreateV13(w http.ResponseWriter, r *http.Request) {
+func Create(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)
@@ -105,7 +77,7 @@ func CreateV13(w http.ResponseWriter, r *http.Request) {
}
defer inf.Close()
- ds := tc.DeliveryServiceNullableV13{}
+ ds := tc.DeliveryServiceNullable{}
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
@@ -118,18 +90,21 @@ func CreateV13(w http.ResponseWriter, r *http.Request) {
api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest,
errors.New("invalid request: "+err.Error()), nil)
return
}
- dsv14 := tc.NewDeliveryServiceNullableFromV13(ds)
- dsv14, errCode, userErr, sysErr = create(inf.Tx.Tx, *inf.Config,
inf.User, dsv14)
+ ds, errCode, userErr, sysErr = create(inf, ds)
if userErr != nil || sysErr != nil {
api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
return
}
- api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice creation
was successful.",
[]tc.DeliveryServiceNullableV13{dsv14.DeliveryServiceNullableV13})
+ api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice creation
was successful.", []tc.DeliveryServiceNullable{ds})
}
// 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 create(tx *sql.Tx, cfg config.Config, user *auth.CurrentUser, ds
tc.DeliveryServiceNullable) (tc.DeliveryServiceNullable, int, error, error) {
- if authorized, err := isTenantAuthorized(user, tx,
&ds.DeliveryServiceNullableV12); err != nil {
+func create(inf *api.APIInfo, ds tc.DeliveryServiceNullable)
(tc.DeliveryServiceNullable, int, error, error) {
+ user := inf.User
+ tx := inf.Tx.Tx
+ cfg := inf.Config
+
+ if authorized, err := isTenantAuthorized(inf, &ds); err != nil {
return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("checking tenant: " +
err.Error())
} else if !authorized {
return tc.DeliveryServiceNullable{}, http.StatusForbidden,
errors.New("not authorized on this tenant"), nil
@@ -202,7 +177,7 @@ func create(tx *sql.Tx, cfg config.Config, user
*auth.CurrentUser, ds tc.Deliver
}
if dnssecEnabled {
- if err := PutDNSSecKeys(tx, &cfg, *ds.XMLID, cdnName,
ds.ExampleURLs); err != nil {
+ if err := PutDNSSecKeys(tx, cfg, *ds.XMLID, cdnName,
ds.ExampleURLs); err != nil {
return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("creating DNSSEC keys: " +
err.Error())
}
}
@@ -218,7 +193,7 @@ func create(tx *sql.Tx, cfg config.Config, user
*auth.CurrentUser, ds tc.Deliver
return ds, http.StatusOK, nil, nil
}
-func (ds *TODeliveryServiceV13) Read() ([]interface{}, error, error, int) {
+func (ds *TODeliveryService) Read() ([]interface{}, error, error, int) {
returnable := []interface{}{}
dses, errs, _ := readGetDeliveryServices(ds.APIInfo().Params,
ds.APIInfo().Tx, ds.APIInfo().User)
if len(errs) > 0 {
@@ -231,134 +206,12 @@ func (ds *TODeliveryServiceV13) Read() ([]interface{},
error, error, int) {
}
for _, ds := range dses {
- returnable = append(returnable, ds.DeliveryServiceNullableV13)
+ returnable = append(returnable, ds)
}
return returnable, nil, nil, http.StatusOK
}
-func createDefaultRegex(tx *sql.Tx, dsID int, xmlID string) error {
- regexStr := `.*\.` + xmlID + `\..*`
- regexID := 0
- if err := tx.QueryRow(`INSERT INTO regex (type, pattern) VALUES
((select id from type where name = 'HOST_REGEXP'), $1::text) RETURNING id`,
regexStr).Scan(®exID); err != nil {
- return errors.New("insert regex: " + err.Error())
- }
- if _, err := tx.Exec(`INSERT INTO deliveryservice_regex
(deliveryservice, regex, set_number) VALUES ($1::bigint, $2::bigint, 0)`, dsID,
regexID); err != nil {
- return errors.New("executing parameter query to insert
location: " + err.Error())
- }
- return nil
-}
-
-func createPrimaryOrigin(tx *sql.Tx, user *auth.CurrentUser, ds
tc.DeliveryServiceNullable) error {
- if ds.OrgServerFQDN == nil {
- return nil
- }
-
- protocol, fqdn, port, err := tc.ParseOrgServerFQDN(*ds.OrgServerFQDN)
- if err != nil {
- return fmt.Errorf("creating primary origin: %v", err)
- }
-
- originID := 0
- q := `INSERT INTO origin (name, fqdn, protocol, is_primary, port,
deliveryservice, tenant) VALUES ($1, $2, $3, TRUE, $4, $5, $6) RETURNING id`
- if err := tx.QueryRow(q, ds.XMLID, fqdn, protocol, port, ds.ID,
ds.TenantID).Scan(&originID); err != nil {
- return fmt.Errorf("insert origin from '%s': %s",
*ds.OrgServerFQDN, err.Error())
- }
-
- api.CreateChangeLogRawTx(api.ApiChange, "Created primary origin id:
"+strconv.Itoa(originID)+" for delivery service: "+*ds.XMLID, user, tx)
-
- return nil
-}
-
-func updatePrimaryOrigin(tx *sql.Tx, user *auth.CurrentUser, ds
tc.DeliveryServiceNullable) error {
- count := 0
- q := `SELECT count(*) FROM origin WHERE deliveryservice = $1 AND
is_primary`
- if err := tx.QueryRow(q, *ds.ID).Scan(&count); err != nil {
- return fmt.Errorf("querying existing primary origin for ds %s:
%s", *ds.XMLID, err.Error())
- }
-
- if ds.OrgServerFQDN == nil || *ds.OrgServerFQDN == "" {
- if count == 1 {
- // the update is removing the existing orgServerFQDN,
so the existing row needs to be deleted
- q = `DELETE FROM origin WHERE deliveryservice = $1 AND
is_primary`
- if _, err := tx.Exec(q, *ds.ID); err != nil {
- return fmt.Errorf("deleting primary origin for
ds %s: %s", *ds.XMLID, err.Error())
- }
- api.CreateChangeLogRawTx(api.ApiChange, "Deleted
primary origin for delivery service: "+*ds.XMLID, user, tx)
- }
- return nil
- }
-
- if count == 0 {
- // orgServerFQDN is going from null to not null, so the primary
origin needs to be created
- return createPrimaryOrigin(tx, user, ds)
- }
-
- protocol, fqdn, port, err := tc.ParseOrgServerFQDN(*ds.OrgServerFQDN)
- if err != nil {
- return fmt.Errorf("updating primary origin: %v", err)
- }
-
- name := ""
- q = `UPDATE origin SET protocol = $1, fqdn = $2, port = $3 WHERE
is_primary AND deliveryservice = $4 RETURNING name`
- if err := tx.QueryRow(q, protocol, fqdn, port, *ds.ID).Scan(&name); err
!= nil {
- return fmt.Errorf("update primary origin for ds %s from '%s':
%s", *ds.XMLID, *ds.OrgServerFQDN, err.Error())
- }
-
- api.CreateChangeLogRawTx(api.ApiChange, "Updated primary origin:
"+name+" for delivery service: "+*ds.XMLID, user, tx)
-
- return nil
-}
-
-func getOldHostName(id int, tx *sql.Tx) (string, error) {
- q := `
-SELECT ds.xml_id, ds.protocol, type.name, ds.routing_name, cdn.domain_name
-FROM deliveryservice as ds
-JOIN type ON ds.type = type.id
-JOIN cdn ON ds.cdn_id = cdn.id
-WHERE ds.id=$1
-`
- xmlID := ""
- protocol := (*int)(nil)
- dsTypeStr := ""
- routingName := ""
- cdnDomain := ""
- if err := tx.QueryRow(q, id).Scan(&xmlID, &protocol, &dsTypeStr,
&routingName, &cdnDomain); err != nil {
- return "", fmt.Errorf("querying delivery service %v host name:
"+err.Error()+"\n", id)
- }
- dsType := tc.DSTypeFromString(dsTypeStr)
- if dsType == tc.DSTypeInvalid {
- return "", errors.New("getting delivery services matchlist: got
invalid delivery service type '" + dsTypeStr + "'")
- }
- matchLists, err := GetDeliveryServicesMatchLists([]string{xmlID}, tx)
- if err != nil {
- return "", errors.New("getting delivery services matchlist: " +
err.Error())
- }
- matchList, ok := matchLists[xmlID]
- if !ok {
- return "", errors.New("delivery service has no match lists (is
your delivery service missing regexes?)")
- }
- host, err := getHostName(protocol, dsType, routingName, matchList,
cdnDomain) // protocol defaults to 0: doesn't need to check Valid()
- if err != nil {
- return "", errors.New("getting hostname: " + err.Error())
- }
- return host, nil
-}
-
-func getTypeFromID(id int, tx *sql.Tx) (tc.DSType, error) {
- // TODO combine with getOldHostName, to only make one query?
- name := ""
- if err := tx.QueryRow(`SELECT name FROM type WHERE id = $1`,
id).Scan(&name); err != nil {
- return "", fmt.Errorf("querying type ID %v: "+err.Error()+"\n",
id)
- }
- return tc.DSTypeFromString(name), nil
-}
-
-// Update is unimplemented, needed to satisfy CRUDer, since the framework
doesn't allow an update to return an array of one
-func (ds *TODeliveryServiceV13) Update() (error, error, int) {
- return nil, nil, http.StatusNotImplemented
-}
-
-func UpdateV13(w http.ResponseWriter, r *http.Request) {
+func Update(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)
@@ -380,7 +233,7 @@ func UpdateV13(w http.ResponseWriter, r *http.Request) {
return
}
- ds, errCode, userErr, sysErr = update(inf.Tx.Tx, *inf.Config, inf.User,
&ds)
+ ds, errCode, userErr, sysErr = update(inf, &ds)
if userErr != nil || sysErr != nil {
api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
return
@@ -388,19 +241,24 @@ func UpdateV13(w http.ResponseWriter, r *http.Request) {
api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice update
was successful.", []tc.DeliveryServiceNullable{ds})
}
-func getDSType(tx *sql.Tx, xmlid string) (tc.DSType, bool, error) {
- name := ""
- if err := tx.QueryRow(`SELECT name FROM type WHERE id = (select type
from deliveryservice where xml_id = $1)`, xmlid).Scan(&name); err != nil {
- if err == sql.ErrNoRows {
- return "", false, nil
- }
- return "", false, fmt.Errorf("querying deliveryservice type
name: " + err.Error())
+func createDefaultRegex(tx *sql.Tx, dsID int, xmlID string) error {
+ regexStr := `.*\.` + xmlID + `\..*`
+ regexID := 0
+ if err := tx.QueryRow(`INSERT INTO regex (type, pattern) VALUES
((select id from type where name = 'HOST_REGEXP'), $1::text) RETURNING id`,
regexStr).Scan(®exID); err != nil {
+ return errors.New("insert regex: " + err.Error())
}
- return tc.DSTypeFromString(name), true, nil
+ if _, err := tx.Exec(`INSERT INTO deliveryservice_regex
(deliveryservice, regex, set_number) VALUES ($1::bigint, $2::bigint, 0)`, dsID,
regexID); err != nil {
+ return errors.New("executing parameter query to insert
location: " + err.Error())
+ }
+ return nil
}
-func update(tx *sql.Tx, cfg config.Config, user *auth.CurrentUser, ds
*tc.DeliveryServiceNullable) (tc.DeliveryServiceNullable, int, error, error) {
- if authorized, err := isTenantAuthorized(user, tx,
&ds.DeliveryServiceNullableV12); err != nil {
+func update(inf *api.APIInfo, ds *tc.DeliveryServiceNullable)
(tc.DeliveryServiceNullable, int, error, error) {
+ tx := inf.Tx.Tx
+ cfg := inf.Config
+ user := inf.User
+
+ if authorized, err := isTenantAuthorized(inf, ds); err != nil {
return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("checking tenant: " +
err.Error())
} else if !authorized {
return tc.DeliveryServiceNullable{}, http.StatusForbidden,
errors.New("not authorized on this tenant"), nil
@@ -522,17 +380,6 @@ func update(tx *sql.Tx, cfg config.Config, user
*auth.CurrentUser, ds *tc.Delive
return *ds, http.StatusOK, nil, nil
}
-// Delete is the DeliveryService implementation of the Deleter interface
-//all implementations of Deleter should use transactions and return the proper
errorType
-func (ds *TODeliveryServiceV13) Delete() (error, error, int) {
- return ds.V12().Delete()
-}
-
-// IsTenantAuthorized implements the Tenantable interface to ensure the user
is authorized on the deliveryservice tenant
-func (ds *TODeliveryServiceV13) IsTenantAuthorized(user *auth.CurrentUser)
(bool, error) {
- return ds.V12().IsTenantAuthorized(user)
-}
-
func readGetDeliveryServices(params map[string]string, tx *sqlx.Tx, user
*auth.CurrentUser) ([]tc.DeliveryServiceNullable, []error, tc.ApiErrorType) {
if strings.HasSuffix(params["id"], ".json") {
params["id"] = params["id"][:len(params["id"])-len(".json")]
@@ -561,11 +408,14 @@ func readGetDeliveryServices(params map[string]string, tx
*sqlx.Tx, user *auth.C
}
tenantIDs, err := tenant.GetUserTenantIDListTx(tx.Tx, user.TenantID)
+
if err != nil {
log.Errorln("received error querying for user's tenants: " +
err.Error())
return nil, []error{tc.DBError}, tc.SystemError
}
+
where, queryValues = dbhelpers.AddTenancyCheck(where, queryValues,
"ds.tenant_id", tenantIDs)
+
query := selectQuery() + where + orderBy
log.Debugln("generated deliveryServices query: " + query)
@@ -574,6 +424,122 @@ func readGetDeliveryServices(params map[string]string, tx
*sqlx.Tx, user *auth.C
return GetDeliveryServices(query, queryValues, tx)
}
+func getOldHostName(id int, tx *sql.Tx) (string, error) {
+ q := `
+SELECT ds.xml_id, ds.protocol, type.name, ds.routing_name, cdn.domain_name
+FROM deliveryservice as ds
+JOIN type ON ds.type = type.id
+JOIN cdn ON ds.cdn_id = cdn.id
+WHERE ds.id=$1
+`
+ xmlID := ""
+ protocol := (*int)(nil)
+ dsTypeStr := ""
+ routingName := ""
+ cdnDomain := ""
+ if err := tx.QueryRow(q, id).Scan(&xmlID, &protocol, &dsTypeStr,
&routingName, &cdnDomain); err != nil {
+ return "", fmt.Errorf("querying delivery service %v host name:
"+err.Error()+"\n", id)
+ }
+ dsType := tc.DSTypeFromString(dsTypeStr)
+ if dsType == tc.DSTypeInvalid {
+ return "", errors.New("getting delivery services matchlist: got
invalid delivery service type '" + dsTypeStr + "'")
+ }
+ matchLists, err := GetDeliveryServicesMatchLists([]string{xmlID}, tx)
+ if err != nil {
+ return "", errors.New("getting delivery services matchlist: " +
err.Error())
+ }
+ matchList, ok := matchLists[xmlID]
+ if !ok {
+ return "", errors.New("delivery service has no match lists (is
your delivery service missing regexes?)")
+ }
+ host, err := getHostName(protocol, dsType, routingName, matchList,
cdnDomain) // protocol defaults to 0: doesn't need to check Valid()
+ if err != nil {
+ return "", errors.New("getting hostname: " + err.Error())
+ }
+ return host, nil
+}
+
+func getTypeFromID(id int, tx *sql.Tx) (tc.DSType, error) {
+ // TODO combine with getOldHostName, to only make one query?
+ name := ""
+ if err := tx.QueryRow(`SELECT name FROM type WHERE id = $1`,
id).Scan(&name); err != nil {
+ return "", fmt.Errorf("querying type ID %v: "+err.Error()+"\n",
id)
+ }
+ return tc.DSTypeFromString(name), nil
+}
+
+func updatePrimaryOrigin(tx *sql.Tx, user *auth.CurrentUser, ds
tc.DeliveryServiceNullable) error {
+ count := 0
+ q := `SELECT count(*) FROM origin WHERE deliveryservice = $1 AND
is_primary`
+ if err := tx.QueryRow(q, *ds.ID).Scan(&count); err != nil {
+ return fmt.Errorf("querying existing primary origin for ds %s:
%s", *ds.XMLID, err.Error())
+ }
+
+ if ds.OrgServerFQDN == nil || *ds.OrgServerFQDN == "" {
+ if count == 1 {
+ // the update is removing the existing orgServerFQDN,
so the existing row needs to be deleted
+ q = `DELETE FROM origin WHERE deliveryservice = $1 AND
is_primary`
+ if _, err := tx.Exec(q, *ds.ID); err != nil {
+ return fmt.Errorf("deleting primary origin for
ds %s: %s", *ds.XMLID, err.Error())
+ }
+ api.CreateChangeLogRawTx(api.ApiChange, "Deleted
primary origin for delivery service: "+*ds.XMLID, user, tx)
+ }
+ return nil
+ }
+
+ if count == 0 {
+ // orgServerFQDN is going from null to not null, so the primary
origin needs to be created
+ return createPrimaryOrigin(tx, user, ds)
+ }
+
+ protocol, fqdn, port, err := tc.ParseOrgServerFQDN(*ds.OrgServerFQDN)
+ if err != nil {
+ return fmt.Errorf("updating primary origin: %v", err)
+ }
+
+ name := ""
+ q = `UPDATE origin SET protocol = $1, fqdn = $2, port = $3 WHERE
is_primary AND deliveryservice = $4 RETURNING name`
+ if err := tx.QueryRow(q, protocol, fqdn, port, *ds.ID).Scan(&name); err
!= nil {
+ return fmt.Errorf("update primary origin for ds %s from '%s':
%s", *ds.XMLID, *ds.OrgServerFQDN, err.Error())
+ }
+
+ api.CreateChangeLogRawTx(api.ApiChange, "Updated primary origin:
"+name+" for delivery service: "+*ds.XMLID, user, tx)
+
+ return nil
+}
+
+func createPrimaryOrigin(tx *sql.Tx, user *auth.CurrentUser, ds
tc.DeliveryServiceNullable) error {
+ if ds.OrgServerFQDN == nil {
+ return nil
+ }
+
+ protocol, fqdn, port, err := tc.ParseOrgServerFQDN(*ds.OrgServerFQDN)
+ if err != nil {
+ return fmt.Errorf("creating primary origin: %v", err)
+ }
+
+ originID := 0
+ q := `INSERT INTO origin (name, fqdn, protocol, is_primary, port,
deliveryservice, tenant) VALUES ($1, $2, $3, TRUE, $4, $5, $6) RETURNING id`
+ if err := tx.QueryRow(q, ds.XMLID, fqdn, protocol, port, ds.ID,
ds.TenantID).Scan(&originID); err != nil {
+ return fmt.Errorf("insert origin from '%s': %s",
*ds.OrgServerFQDN, err.Error())
+ }
+
+ api.CreateChangeLogRawTx(api.ApiChange, "Created primary origin id:
"+strconv.Itoa(originID)+" for delivery service: "+*ds.XMLID, user, tx)
+
+ return nil
+}
+
+func getDSType(tx *sql.Tx, xmlid string) (tc.DSType, bool, error) {
+ name := ""
+ if err := tx.QueryRow(`SELECT name FROM type WHERE id = (select type
from deliveryservice where xml_id = $1)`, xmlid).Scan(&name); err != nil {
+ if err == sql.ErrNoRows {
+ return "", false, nil
+ }
+ return "", false, fmt.Errorf("querying deliveryservice type
name: " + err.Error())
+ }
+ return tc.DSTypeFromString(name), true, nil
+}
+
func GetDeliveryServices(query string, queryValues map[string]interface{}, tx
*sqlx.Tx) ([]tc.DeliveryServiceNullable, []error, tc.ApiErrorType) {
rows, err := tx.NamedQuery(query, queryValues)
if err != nil {
@@ -620,7 +586,7 @@ func GetDeliveryServices(query string, queryValues
map[string]interface{}, tx *s
return dses, nil, tc.NoError
}
-func updateSSLKeys(ds *tc.DeliveryServiceNullable, hostName string, tx
*sql.Tx, cfg config.Config) error {
+func updateSSLKeys(ds *tc.DeliveryServiceNullable, hostName string, tx
*sql.Tx, cfg *config.Config) error {
if ds.XMLID == nil {
return errors.New("delivery services has no XMLID!")
}
@@ -938,6 +904,123 @@ func GetDSSelectQuery() string {
return selectQuery()
}
+// getTenantID returns the tenant Id of the given delivery service. Note it
may return a nil id and nil error, if the tenant ID in the database is nil.
+func getTenantID(tx *sql.Tx, ds *tc.DeliveryServiceNullable) (*int, error) {
+ if ds.ID == nil && ds.XMLID == nil {
+ return nil, errors.New("delivery service has no ID or XMLID")
+ }
+ if ds.ID != nil {
+ existingID, _, err := getDSTenantIDByID(tx, *ds.ID) // ignore
exists return - if the DS is new, we only need to check the user input tenant
+ return existingID, err
+ }
+ existingID, _, err := getDSTenantIDByName(tx, *ds.XMLID) // ignore
exists return - if the DS is new, we only need to check the user input tenant
+ return existingID, err
+}
+
+func isTenantAuthorized(inf *api.APIInfo, ds *tc.DeliveryServiceNullable)
(bool, error) {
+ tx := inf.Tx.Tx
+ user := inf.User
+
+ existingID, err := getTenantID(inf.Tx.Tx, ds)
+ if err != nil {
+ return false, errors.New("getting tenant ID: " + err.Error())
+ }
+ if ds.TenantID == nil {
+ ds.TenantID = existingID
+ }
+ if existingID != nil && existingID != ds.TenantID {
+ userAuthorizedForExistingDSTenant, err :=
tenant.IsResourceAuthorizedToUserTx(*existingID, user, tx)
+ if err != nil {
+ return false, errors.New("checking authorization for
existing DS ID: " + err.Error())
+ }
+ if !userAuthorizedForExistingDSTenant {
+ return false, nil
+ }
+ }
+ if ds.TenantID != nil {
+ userAuthorizedForNewDSTenant, err :=
tenant.IsResourceAuthorizedToUserTx(*ds.TenantID, user, tx)
+ if err != nil {
+ return false, errors.New("checking authorization for
new DS ID: " + err.Error())
+ }
+ if !userAuthorizedForNewDSTenant {
+ return false, nil
+ }
+ }
+ return true, nil
+}
+
+// getDSTenantIDByID returns the tenant ID, whether the delivery service
exists, and any error.
+func getDSTenantIDByID(tx *sql.Tx, id int) (*int, bool, error) {
+ tenantID := (*int)(nil)
+ if err := tx.QueryRow(`SELECT tenant_id FROM deliveryservice where id =
$1`, id).Scan(&tenantID); err != nil {
+ if err == sql.ErrNoRows {
+ return nil, false, nil
+ }
+ return nil, false, fmt.Errorf("querying tenant ID for delivery
service ID '%v': %v", id, err)
+ }
+ return tenantID, true, nil
+}
+
+// GetDSTenantIDByIDTx returns the tenant ID, whether the delivery service
exists, and any error.
+func GetDSTenantIDByIDTx(tx *sql.Tx, id int) (*int, bool, error) {
+ tenantID := (*int)(nil)
+ if err := tx.QueryRow(`SELECT tenant_id FROM deliveryservice where id =
$1`, id).Scan(&tenantID); err != nil {
+ if err == sql.ErrNoRows {
+ return nil, false, nil
+ }
+ return nil, false, fmt.Errorf("querying tenant ID for delivery
service ID '%v': %v", id, err)
+ }
+ return tenantID, true, nil
+}
+
+// getDSTenantIDByName returns the tenant ID, whether the delivery service
exists, and any error.
+func getDSTenantIDByName(tx *sql.Tx, name string) (*int, bool, error) {
+ tenantID := (*int)(nil)
+ if err := tx.QueryRow(`SELECT tenant_id FROM deliveryservice where
xml_id = $1`, name).Scan(&tenantID); err != nil {
+ if err == sql.ErrNoRows {
+ return nil, false, nil
+ }
+ return nil, false, fmt.Errorf("querying tenant ID for delivery
service name '%v': %v", name, err)
+ }
+ return tenantID, true, nil
+}
+
+// GetDSTenantIDByNameTx returns the tenant ID, whether the delivery service
exists, and any error.
+func GetDSTenantIDByNameTx(tx *sql.Tx, ds tc.DeliveryServiceName) (*int, bool,
error) {
+ tenantID := (*int)(nil)
+ if err := tx.QueryRow(`SELECT tenant_id FROM deliveryservice where
xml_id = $1`, ds).Scan(&tenantID); err != nil {
+ if err == sql.ErrNoRows {
+ return nil, false, nil
+ }
+ return nil, false, fmt.Errorf("querying tenant ID for delivery
service name '%v': %v", ds, err)
+ }
+ return tenantID, true, nil
+}
+
+// GetDeliveryServiceType returns the type of the deliveryservice.
+func GetDeliveryServiceType(dsID int, tx *sql.Tx) (tc.DSType, error) {
+ var dsType tc.DSType
+ if err := tx.QueryRow(`SELECT t.name FROM deliveryservice as ds JOIN
type t ON ds.type = t.id WHERE ds.id=$1`, dsID).Scan(&dsType); err != nil {
+ if err == sql.ErrNoRows {
+ return tc.DSTypeInvalid, errors.New("a deliveryservice
with id '" + strconv.Itoa(dsID) + "' was not found")
+ }
+ return tc.DSTypeInvalid, errors.New("querying type from
delivery service: " + err.Error())
+ }
+ return dsType, nil
+}
+
+// GetXMLID loads the DeliveryService's xml_id from the database, from the ID.
Returns whether the delivery service was found, and any error.
+func GetXMLID(tx *sql.Tx, id int) (string, bool, error) {
+ xmlID := ""
+ if err := tx.QueryRow(`SELECT xml_id FROM deliveryservice where id =
$1`, id).Scan(&xmlID); err != nil {
+ if err == sql.ErrNoRows {
+ return "", false, nil
+ }
+ return "", false, fmt.Errorf("querying xml_id for delivery
service ID '%v': %v", id, err)
+ }
+ return xmlID, true, nil
+}
+
func selectQuery() string {
return `
SELECT
diff --git
a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv12.go
b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv12.go
index b9d03fb..68d9c21 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv12.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv12.go
@@ -20,19 +20,15 @@ package deliveryservice
*/
import (
- "database/sql"
+ "encoding/json"
"errors"
- "fmt"
"net/http"
- "strconv"
"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/lib/go-util"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/auth"
- "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant"
- "github.com/jmoiron/sqlx"
"github.com/lib/pq"
)
@@ -41,6 +37,14 @@ type TODeliveryServiceV12 struct {
tc.DeliveryServiceNullableV12
}
+func (ds TODeliveryServiceV12) MarshalJSON() ([]byte, error) {
+ return json.Marshal(ds.DeliveryServiceNullableV12)
+}
+
+func (ds *TODeliveryServiceV12) UnmarshalJSON(data []byte) error {
+ return json.Unmarshal(data, ds.DeliveryServiceNullableV12)
+}
+
func (v *TODeliveryServiceV12) DeleteQuery() string {
return `DELETE FROM deliveryservice WHERE id = :id`
}
@@ -72,132 +76,16 @@ func (ds *TODeliveryServiceV12) GetType() string {
return "ds"
}
-// getDSTenantIDByID returns the tenant ID, whether the delivery service
exists, and any error.
-func getDSTenantIDByID(tx *sql.Tx, id int) (*int, bool, error) {
- tenantID := (*int)(nil)
- if err := tx.QueryRow(`SELECT tenant_id FROM deliveryservice where id =
$1`, id).Scan(&tenantID); err != nil {
- if err == sql.ErrNoRows {
- return nil, false, nil
- }
- return nil, false, fmt.Errorf("querying tenant ID for delivery
service ID '%v': %v", id, err)
- }
- return tenantID, true, nil
-}
-
-// GetDSTenantIDByIDTx returns the tenant ID, whether the delivery service
exists, and any error.
-func GetDSTenantIDByIDTx(tx *sql.Tx, id int) (*int, bool, error) {
- tenantID := (*int)(nil)
- if err := tx.QueryRow(`SELECT tenant_id FROM deliveryservice where id =
$1`, id).Scan(&tenantID); err != nil {
- if err == sql.ErrNoRows {
- return nil, false, nil
- }
- return nil, false, fmt.Errorf("querying tenant ID for delivery
service ID '%v': %v", id, err)
- }
- return tenantID, true, nil
-}
-
-// getDSTenantIDByName returns the tenant ID, whether the delivery service
exists, and any error.
-func getDSTenantIDByName(tx *sql.Tx, name string) (*int, bool, error) {
- tenantID := (*int)(nil)
- if err := tx.QueryRow(`SELECT tenant_id FROM deliveryservice where
xml_id = $1`, name).Scan(&tenantID); err != nil {
- if err == sql.ErrNoRows {
- return nil, false, nil
- }
- return nil, false, fmt.Errorf("querying tenant ID for delivery
service name '%v': %v", name, err)
- }
- return tenantID, true, nil
-}
-
-// GetDSTenantIDByNameTx returns the tenant ID, whether the delivery service
exists, and any error.
-func GetDSTenantIDByNameTx(tx *sql.Tx, ds tc.DeliveryServiceName) (*int, bool,
error) {
- tenantID := (*int)(nil)
- if err := tx.QueryRow(`SELECT tenant_id FROM deliveryservice where
xml_id = $1`, ds).Scan(&tenantID); err != nil {
- if err == sql.ErrNoRows {
- return nil, false, nil
- }
- return nil, false, fmt.Errorf("querying tenant ID for delivery
service name '%v': %v", ds, err)
- }
- return tenantID, true, nil
-}
-
-// GetXMLID loads the DeliveryService's xml_id from the database, from the ID.
Returns whether the delivery service was found, and any error.
-
-func (ds *TODeliveryServiceV12) GetXMLID(tx *sqlx.Tx) (string, bool, error) {
- if ds.ID == nil {
- return "", false, errors.New("missing ID")
- }
- return GetXMLID(tx.Tx, *ds.ID)
-}
-
-// GetXMLID loads the DeliveryService's xml_id from the database, from the ID.
Returns whether the delivery service was found, and any error.
-func GetXMLID(tx *sql.Tx, id int) (string, bool, error) {
- xmlID := ""
- if err := tx.QueryRow(`SELECT xml_id FROM deliveryservice where id =
$1`, id).Scan(&xmlID); err != nil {
- if err == sql.ErrNoRows {
- return "", false, nil
- }
- return "", false, fmt.Errorf("querying xml_id for delivery
service ID '%v': %v", id, err)
- }
- return xmlID, true, nil
-}
-
// IsTenantAuthorized checks that the user is authorized for both the delivery
service's existing tenant, and the new tenant they're changing it to (if
different).
-
func (ds *TODeliveryServiceV12) IsTenantAuthorized(user *auth.CurrentUser)
(bool, error) {
- return isTenantAuthorized(user, ds.ReqInfo.Tx.Tx,
&ds.DeliveryServiceNullableV12)
-}
-
-// getTenantID returns the tenant Id of the given delivery service. Note it
may return a nil id and nil error, if the tenant ID in the database is nil.
-func getTenantID(tx *sql.Tx, ds *tc.DeliveryServiceNullableV12) (*int, error) {
- if ds.ID == nil && ds.XMLID == nil {
- return nil, errors.New("delivery service has no ID or XMLID")
- }
- if ds.ID != nil {
- existingID, _, err := getDSTenantIDByID(tx, *ds.ID) // ignore
exists return - if the DS is new, we only need to check the user input tenant
- return existingID, err
- }
- existingID, _, err := getDSTenantIDByName(tx, *ds.XMLID) // ignore
exists return - if the DS is new, we only need to check the user input tenant
- return existingID, err
-}
-
-func isTenantAuthorized(user *auth.CurrentUser, tx *sql.Tx, ds
*tc.DeliveryServiceNullableV12) (bool, error) {
- existingID, err := getTenantID(tx, ds)
- if err != nil {
- return false, errors.New("getting tenant ID: " + err.Error())
- }
- if ds.TenantID == nil {
- ds.TenantID = existingID
- }
- if existingID != nil && existingID != ds.TenantID {
- userAuthorizedForExistingDSTenant, err :=
tenant.IsResourceAuthorizedToUserTx(*existingID, user, tx)
- if err != nil {
- return false, errors.New("checking authorization for
existing DS ID: " + err.Error())
- }
- if !userAuthorizedForExistingDSTenant {
- return false, nil
- }
- }
- if ds.TenantID != nil {
- userAuthorizedForNewDSTenant, err :=
tenant.IsResourceAuthorizedToUserTx(*ds.TenantID, user, tx)
- if err != nil {
- return false, errors.New("checking authorization for
new DS ID: " + err.Error())
- }
- if !userAuthorizedForNewDSTenant {
- return false, nil
- }
- }
- return true, nil
+ tcDS :=
tc.NewDeliveryServiceNullableFromV12(ds.DeliveryServiceNullableV12)
+ return isTenantAuthorized(ds.ReqInfo, &tcDS)
}
func (ds *TODeliveryServiceV12) Validate() error {
return ds.DeliveryServiceNullableV12.Validate(ds.ReqInfo.Tx.Tx)
}
-// Create is unimplemented, needed to satisfy CRUDer, since the framework
doesn't allow a create to return an array of one
-func (ds *TODeliveryServiceV12) Create() (error, error, int) {
- return nil, nil, http.StatusNotImplemented
-}
-
func CreateV12(w http.ResponseWriter, r *http.Request) {
inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
if userErr != nil || sysErr != nil {
@@ -210,13 +98,13 @@ func CreateV12(w http.ResponseWriter, r *http.Request) {
api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest,
errors.New("decoding: "+err.Error()), nil)
return
}
- dsv13 := tc.NewDeliveryServiceNullableFromV12(ds)
- dsv13, errCode, userErr, sysErr = create(inf.Tx.Tx, *inf.Config,
inf.User, dsv13)
+ tcDS := tc.NewDeliveryServiceNullableFromV12(ds)
+ tcDS, errCode, userErr, sysErr = create(inf, tcDS)
if userErr != nil || sysErr != nil {
api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
return
}
- api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice creation
was successful.",
[]tc.DeliveryServiceNullableV12{dsv13.DeliveryServiceNullableV12})
+ api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice creation
was successful.",
[]tc.DeliveryServiceNullableV12{tcDS.DeliveryServiceNullableV12})
}
func (ds *TODeliveryServiceV12) Read() ([]interface{}, error, error, int) {
@@ -237,13 +125,36 @@ func (ds *TODeliveryServiceV12) Read() ([]interface{},
error, error, int) {
return returnable, nil, nil, http.StatusOK
}
-//Delete is the DeliveryService implementation of the Deleter interface
-//all implementations of Deleter should use transactions and return the proper
errorType
+func UpdateV12(w http.ResponseWriter, r *http.Request) {
+ 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
+ }
+ defer inf.Close()
+
+ ds := tc.DeliveryServiceNullableV12{}
+ ds.ID = util.IntPtr(inf.IntParams["id"])
+ 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
+ }
+ tcDS := tc.NewDeliveryServiceNullableFromV12(ds)
+ tcDS, errCode, userErr, sysErr = update(inf, &tcDS)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+ return
+ }
+ api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice update
was successful.",
[]tc.DeliveryServiceNullableV12{tcDS.DeliveryServiceNullableV12})
+}
+
+//Delete is the DeliveryService implementation of the Deleter interface.
func (ds *TODeliveryServiceV12) Delete() (error, error, int) {
if ds.ID == nil {
return errors.New("missing id"), nil, http.StatusBadRequest
}
- xmlID, ok, err := ds.GetXMLID(ds.ReqInfo.Tx)
+
+ xmlID, ok, err := GetXMLID(ds.ReqInfo.Tx.Tx, *ds.ID)
if err != nil {
return nil, errors.New("dsv12 delete: getting xmlid: " +
err.Error()), http.StatusInternalServerError
} else if !ok {
@@ -278,43 +189,3 @@ func (ds *TODeliveryServiceV12) Delete() (error, error,
int) {
return nil, nil, http.StatusOK
}
-
-// Update is unimplemented, needed to satisfy CRUDer, since the framework
doesn't allow an update to return an array of one.
-func (ds *TODeliveryServiceV12) Update() (error, error, int) {
- return nil, nil, http.StatusNotImplemented
-}
-
-func UpdateV12(w http.ResponseWriter, r *http.Request) {
- 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
- }
- defer inf.Close()
-
- ds := tc.DeliveryServiceNullableV12{}
- ds.ID = util.IntPtr(inf.IntParams["id"])
- 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
- }
- dsv13 := tc.NewDeliveryServiceNullableFromV12(ds)
- dsv13, errCode, userErr, sysErr = update(inf.Tx.Tx, *inf.Config,
inf.User, &dsv13)
- if userErr != nil || sysErr != nil {
- api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
- return
- }
- api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice update
was successful.",
[]tc.DeliveryServiceNullableV12{dsv13.DeliveryServiceNullableV12})
-}
-
-// GetDeliveryServiceType returns the type of the deliveryservice.
-func GetDeliveryServiceType(dsID int, tx *sql.Tx) (tc.DSType, error) {
- var dsType tc.DSType
- if err := tx.QueryRow(`SELECT t.name FROM deliveryservice as ds JOIN
type t ON ds.type = t.id WHERE ds.id=$1`, dsID).Scan(&dsType); err != nil {
- if err == sql.ErrNoRows {
- return tc.DSTypeInvalid, errors.New("a deliveryservice
with id '" + strconv.Itoa(dsID) + "' was not found")
- }
- return tc.DSTypeInvalid, errors.New("querying type from
delivery service: " + err.Error())
- }
- return dsType, nil
-}
diff --git
a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv13.go
b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv13.go
index 9d5759b..6497b60 100644
--- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv13.go
+++ b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv13.go
@@ -20,26 +20,13 @@ package deliveryservice
*/
import (
- "database/sql"
"encoding/json"
"errors"
- "fmt"
"net/http"
- "strconv"
- "strings"
- "github.com/apache/trafficcontrol/lib/go-log"
"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/lib/go-util"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
- "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/auth"
- "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
-
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
-
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/riaksvc"
- "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant"
-
- "github.com/jmoiron/sqlx"
- "github.com/lib/pq"
)
//we need a type alias to define functions on
@@ -49,53 +36,25 @@ type TODeliveryServiceV13 struct {
tc.DeliveryServiceNullableV13
}
-func (ds *TODeliveryServiceV13) V12() *TODeliveryServiceV12 {
- v12 := &TODeliveryServiceV12{}
- v12.DeliveryServiceNullableV12 = ds.DeliveryServiceNullableV12
- v12.SetInfo(ds.ReqInfo)
- return v12
-}
-
func (ds TODeliveryServiceV13) MarshalJSON() ([]byte, error) {
return json.Marshal(ds.DeliveryServiceNullableV13)
}
+
func (ds *TODeliveryServiceV13) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, ds.DeliveryServiceNullableV13)
}
func (ds *TODeliveryServiceV13) APIInfo() *api.APIInfo { return ds.ReqInfo }
-func (ds TODeliveryServiceV13) GetKeyFieldsInfo() []api.KeyFieldInfo {
- return ds.V12().GetKeyFieldsInfo()
-}
-
-//Implementation of the Identifier, Validator interface functions
-func (ds TODeliveryServiceV13) GetKeys() (map[string]interface{}, bool) {
- return ds.V12().GetKeys()
-}
-
func (ds *TODeliveryServiceV13) SetKeys(keys map[string]interface{}) {
i, _ := keys["id"].(int) //this utilizes the non panicking type
assertion, if the thrown away ok variable is false i will be the zero of the
type, 0 here.
ds.ID = &i
}
-func (ds *TODeliveryServiceV13) GetAuditName() string {
- return ds.V12().GetAuditName()
-}
-
-func (ds *TODeliveryServiceV13) GetType() string {
- return ds.V12().GetType()
-}
-
func (ds *TODeliveryServiceV13) Validate() error {
return ds.DeliveryServiceNullableV13.Validate(ds.APIInfo().Tx.Tx)
}
-// Create is unimplemented, needed to satisfy CRUDer, since the framework
doesn't allow a create to return an array of one
-func (ds *TODeliveryServiceV13) Create() (error, error, int) {
- return nil, nil, http.StatusNotImplemented
-}
-
// TODO allow users to post names (type, cdn, etc) and get the IDs from
the names. This isn't trivial to do in a single query, without dynamically
building the entire insert query, and ideally inserting would be one query. But
it'd be much more convenient for users. Alternatively, remove IDs from the
database entirely and use real candidate keys.
func CreateV13(w http.ResponseWriter, r *http.Request) {
inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
@@ -118,104 +77,13 @@ func CreateV13(w http.ResponseWriter, r *http.Request) {
api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest,
errors.New("invalid request: "+err.Error()), nil)
return
}
- dsv14 := tc.NewDeliveryServiceNullableFromV13(ds)
- dsv14, errCode, userErr, sysErr = create(inf.Tx.Tx, *inf.Config,
inf.User, dsv14)
+ tcDS := tc.NewDeliveryServiceNullableFromV13(ds)
+ tcDS, errCode, userErr, sysErr = create(inf, tcDS)
if userErr != nil || sysErr != nil {
api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
return
}
- api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice creation
was successful.",
[]tc.DeliveryServiceNullableV13{dsv14.DeliveryServiceNullableV13})
-}
-
-// 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 create(tx *sql.Tx, cfg config.Config, user *auth.CurrentUser, ds
tc.DeliveryServiceNullable) (tc.DeliveryServiceNullable, int, error, error) {
- if authorized, err := isTenantAuthorized(user, tx,
&ds.DeliveryServiceNullableV12); err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("checking tenant: " +
err.Error())
- } else if !authorized {
- return tc.DeliveryServiceNullable{}, http.StatusForbidden,
errors.New("not authorized on this tenant"), nil
- }
-
- // TODO change DeepCachingType to implement sql.Valuer and sql.Scanner,
so sqlx struct scan can be used.
- deepCachingType := tc.DeepCachingType("").String()
- if ds.DeepCachingType != nil {
- deepCachingType = ds.DeepCachingType.String() // necessary,
because DeepCachingType's default needs to insert the string, not "", and Query
doesn't call .String().
- }
-
- resultRows, err := tx.Query(insertQuery(), &ds.Active,
&ds.AnonymousBlockingEnabled, &ds.CacheURL, &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,
&ds.GeoLimitCountries, &ds.GeoProvider, &ds.GlobalMaxMBPS, &ds.GlobalMaxTPS,
&ds.FQPacingRate, &ds.HTTPBypassFQDN, &ds.InfoURL, &ds.InitialDispers [...]
- if err != nil {
- usrErr, sysErr, code := api.ParseDBError(err)
- return tc.DeliveryServiceNullable{}, code, usrErr, sysErr
- }
- defer resultRows.Close()
-
- id := 0
- lastUpdated := tc.TimeNoMod{}
- if !resultRows.Next() {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("no deliveryservice request
inserted, no id was returned")
- }
- if err := resultRows.Scan(&id, &lastUpdated); err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("could not scan id from insert:
" + err.Error())
- }
- if resultRows.Next() {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("too many ids returned from
deliveryservice request insert")
- }
- ds.ID = &id
-
- if ds.ID == nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("missing id after insert")
- }
- if ds.XMLID == nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("missing xml_id after insert")
- }
- if ds.TypeID == nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("missing type after insert")
- }
- dsType, err := getTypeFromID(*ds.TypeID, tx)
- if err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("getting delivery service type:
" + err.Error())
- }
- ds.Type = &dsType
-
- if err := createDefaultRegex(tx, *ds.ID, *ds.XMLID); err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("creating default regex: " +
err.Error())
- }
-
- matchlists, err := GetDeliveryServicesMatchLists([]string{*ds.XMLID},
tx)
- if err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("creating DS: reading
matchlists: " + err.Error())
- }
- if matchlist, ok := matchlists[*ds.XMLID]; !ok {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("creating DS: reading
matchlists: not found")
- } else {
- ds.MatchList = &matchlist
- }
-
- cdnName, cdnDomain, dnssecEnabled, err :=
getCDNNameDomainDNSSecEnabled(*ds.ID, tx)
- if err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("creating DS: getting CDN info:
" + err.Error())
- }
-
- ds.ExampleURLs = MakeExampleURLs(ds.Protocol, *ds.Type,
*ds.RoutingName, *ds.MatchList, cdnDomain)
-
- if err := EnsureParams(tx, *ds.ID, *ds.XMLID, ds.EdgeHeaderRewrite,
ds.MidHeaderRewrite, ds.RegexRemap, ds.CacheURL, ds.SigningAlgorithm, dsType,
ds.MaxOriginConnections); err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("ensuring ds parameters:: " +
err.Error())
- }
-
- if dnssecEnabled {
- if err := PutDNSSecKeys(tx, &cfg, *ds.XMLID, cdnName,
ds.ExampleURLs); err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("creating DNSSEC keys: " +
err.Error())
- }
- }
-
- if err := createPrimaryOrigin(tx, user, ds); err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("creating delivery service: " +
err.Error())
- }
-
- ds.LastUpdated = &lastUpdated
- if err := api.CreateChangeLogRawErr(api.ApiChange, "Created ds:
"+*ds.XMLID+" id: "+strconv.Itoa(*ds.ID), user, tx); err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("error writing to audit log: "
+ err.Error())
- }
- return ds, http.StatusOK, nil, nil
+ api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice creation
was successful.",
[]tc.DeliveryServiceNullableV13{tcDS.DeliveryServiceNullableV13})
}
func (ds *TODeliveryServiceV13) Read() ([]interface{}, error, error, int) {
@@ -236,128 +104,6 @@ func (ds *TODeliveryServiceV13) Read() ([]interface{},
error, error, int) {
return returnable, nil, nil, http.StatusOK
}
-func createDefaultRegex(tx *sql.Tx, dsID int, xmlID string) error {
- regexStr := `.*\.` + xmlID + `\..*`
- regexID := 0
- if err := tx.QueryRow(`INSERT INTO regex (type, pattern) VALUES
((select id from type where name = 'HOST_REGEXP'), $1::text) RETURNING id`,
regexStr).Scan(®exID); err != nil {
- return errors.New("insert regex: " + err.Error())
- }
- if _, err := tx.Exec(`INSERT INTO deliveryservice_regex
(deliveryservice, regex, set_number) VALUES ($1::bigint, $2::bigint, 0)`, dsID,
regexID); err != nil {
- return errors.New("executing parameter query to insert
location: " + err.Error())
- }
- return nil
-}
-
-func createPrimaryOrigin(tx *sql.Tx, user *auth.CurrentUser, ds
tc.DeliveryServiceNullable) error {
- if ds.OrgServerFQDN == nil {
- return nil
- }
-
- protocol, fqdn, port, err := tc.ParseOrgServerFQDN(*ds.OrgServerFQDN)
- if err != nil {
- return fmt.Errorf("creating primary origin: %v", err)
- }
-
- originID := 0
- q := `INSERT INTO origin (name, fqdn, protocol, is_primary, port,
deliveryservice, tenant) VALUES ($1, $2, $3, TRUE, $4, $5, $6) RETURNING id`
- if err := tx.QueryRow(q, ds.XMLID, fqdn, protocol, port, ds.ID,
ds.TenantID).Scan(&originID); err != nil {
- return fmt.Errorf("insert origin from '%s': %s",
*ds.OrgServerFQDN, err.Error())
- }
-
- api.CreateChangeLogRawTx(api.ApiChange, "Created primary origin id:
"+strconv.Itoa(originID)+" for delivery service: "+*ds.XMLID, user, tx)
-
- return nil
-}
-
-func updatePrimaryOrigin(tx *sql.Tx, user *auth.CurrentUser, ds
tc.DeliveryServiceNullable) error {
- count := 0
- q := `SELECT count(*) FROM origin WHERE deliveryservice = $1 AND
is_primary`
- if err := tx.QueryRow(q, *ds.ID).Scan(&count); err != nil {
- return fmt.Errorf("querying existing primary origin for ds %s:
%s", *ds.XMLID, err.Error())
- }
-
- if ds.OrgServerFQDN == nil || *ds.OrgServerFQDN == "" {
- if count == 1 {
- // the update is removing the existing orgServerFQDN,
so the existing row needs to be deleted
- q = `DELETE FROM origin WHERE deliveryservice = $1 AND
is_primary`
- if _, err := tx.Exec(q, *ds.ID); err != nil {
- return fmt.Errorf("deleting primary origin for
ds %s: %s", *ds.XMLID, err.Error())
- }
- api.CreateChangeLogRawTx(api.ApiChange, "Deleted
primary origin for delivery service: "+*ds.XMLID, user, tx)
- }
- return nil
- }
-
- if count == 0 {
- // orgServerFQDN is going from null to not null, so the primary
origin needs to be created
- return createPrimaryOrigin(tx, user, ds)
- }
-
- protocol, fqdn, port, err := tc.ParseOrgServerFQDN(*ds.OrgServerFQDN)
- if err != nil {
- return fmt.Errorf("updating primary origin: %v", err)
- }
-
- name := ""
- q = `UPDATE origin SET protocol = $1, fqdn = $2, port = $3 WHERE
is_primary AND deliveryservice = $4 RETURNING name`
- if err := tx.QueryRow(q, protocol, fqdn, port, *ds.ID).Scan(&name); err
!= nil {
- return fmt.Errorf("update primary origin for ds %s from '%s':
%s", *ds.XMLID, *ds.OrgServerFQDN, err.Error())
- }
-
- api.CreateChangeLogRawTx(api.ApiChange, "Updated primary origin:
"+name+" for delivery service: "+*ds.XMLID, user, tx)
-
- return nil
-}
-
-func getOldHostName(id int, tx *sql.Tx) (string, error) {
- q := `
-SELECT ds.xml_id, ds.protocol, type.name, ds.routing_name, cdn.domain_name
-FROM deliveryservice as ds
-JOIN type ON ds.type = type.id
-JOIN cdn ON ds.cdn_id = cdn.id
-WHERE ds.id=$1
-`
- xmlID := ""
- protocol := (*int)(nil)
- dsTypeStr := ""
- routingName := ""
- cdnDomain := ""
- if err := tx.QueryRow(q, id).Scan(&xmlID, &protocol, &dsTypeStr,
&routingName, &cdnDomain); err != nil {
- return "", fmt.Errorf("querying delivery service %v host name:
"+err.Error()+"\n", id)
- }
- dsType := tc.DSTypeFromString(dsTypeStr)
- if dsType == tc.DSTypeInvalid {
- return "", errors.New("getting delivery services matchlist: got
invalid delivery service type '" + dsTypeStr + "'")
- }
- matchLists, err := GetDeliveryServicesMatchLists([]string{xmlID}, tx)
- if err != nil {
- return "", errors.New("getting delivery services matchlist: " +
err.Error())
- }
- matchList, ok := matchLists[xmlID]
- if !ok {
- return "", errors.New("delivery service has no match lists (is
your delivery service missing regexes?)")
- }
- host, err := getHostName(protocol, dsType, routingName, matchList,
cdnDomain) // protocol defaults to 0: doesn't need to check Valid()
- if err != nil {
- return "", errors.New("getting hostname: " + err.Error())
- }
- return host, nil
-}
-
-func getTypeFromID(id int, tx *sql.Tx) (tc.DSType, error) {
- // TODO combine with getOldHostName, to only make one query?
- name := ""
- if err := tx.QueryRow(`SELECT name FROM type WHERE id = $1`,
id).Scan(&name); err != nil {
- return "", fmt.Errorf("querying type ID %v: "+err.Error()+"\n",
id)
- }
- return tc.DSTypeFromString(name), nil
-}
-
-// Update is unimplemented, needed to satisfy CRUDer, since the framework
doesn't allow an update to return an array of one
-func (ds *TODeliveryServiceV13) Update() (error, error, int) {
- return nil, nil, http.StatusNotImplemented
-}
-
func UpdateV13(w http.ResponseWriter, r *http.Request) {
inf, userErr, sysErr, errCode := api.NewInfo(r, nil, []string{"id"})
if userErr != nil || sysErr != nil {
@@ -380,757 +126,10 @@ func UpdateV13(w http.ResponseWriter, r *http.Request) {
return
}
- ds, errCode, userErr, sysErr = update(inf.Tx.Tx, *inf.Config, inf.User,
&ds)
+ ds, errCode, userErr, sysErr = update(inf, &ds)
if userErr != nil || sysErr != nil {
api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
return
}
api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice update
was successful.", []tc.DeliveryServiceNullable{ds})
}
-
-func getDSType(tx *sql.Tx, xmlid string) (tc.DSType, bool, error) {
- name := ""
- if err := tx.QueryRow(`SELECT name FROM type WHERE id = (select type
from deliveryservice where xml_id = $1)`, xmlid).Scan(&name); err != nil {
- if err == sql.ErrNoRows {
- return "", false, nil
- }
- return "", false, fmt.Errorf("querying deliveryservice type
name: " + err.Error())
- }
- return tc.DSTypeFromString(name), true, nil
-}
-
-func update(tx *sql.Tx, cfg config.Config, user *auth.CurrentUser, ds
*tc.DeliveryServiceNullable) (tc.DeliveryServiceNullable, int, error, error) {
- if authorized, err := isTenantAuthorized(user, tx,
&ds.DeliveryServiceNullableV12); err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("checking tenant: " +
err.Error())
- } else if !authorized {
- return tc.DeliveryServiceNullable{}, http.StatusForbidden,
errors.New("not authorized on this tenant"), nil
- }
-
- if ds.XMLID == nil {
- return tc.DeliveryServiceNullable{}, http.StatusBadRequest,
errors.New("missing xml_id"), nil
- }
- if ds.ID == nil {
- return tc.DeliveryServiceNullable{}, http.StatusBadRequest,
errors.New("missing id"), nil
- }
-
- dsType, ok, err := getDSType(tx, *ds.XMLID)
- if !ok {
- return tc.DeliveryServiceNullable{}, http.StatusNotFound,
errors.New("delivery service '" + *ds.XMLID + "' not found"), nil
- }
- if err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("getting delivery service type
during update: " + err.Error())
- }
-
- // 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 := ""
- if dsType.HasSSLKeys() {
- oldHostName, err = getOldHostName(*ds.ID, tx)
- if err != nil {
- return tc.DeliveryServiceNullable{},
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 ds.DeepCachingType != nil {
- deepCachingType = ds.DeepCachingType.String() // necessary,
because DeepCachingType's default needs to insert the string, not "", and Query
doesn't call .String().
- }
-
- resultRows, err := tx.Query(updateDSQuery(), &ds.Active, &ds.CacheURL,
&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,
&ds.GeoLimitCountries, &ds.GeoProvider, &ds.GlobalMaxMBPS, &ds.GlobalMaxTPS,
&ds.FQPacingRate, &ds.HTTPBypassFQDN, &ds.InfoURL, &ds.InitialDispersion,
&ds.IPV6RoutingEnabled, &ds.LogsEnabled, &ds.Lon [...]
-
- if err != nil {
- usrErr, sysErr, code := api.ParseDBError(err)
- return tc.DeliveryServiceNullable{}, code, usrErr, sysErr
- }
- defer resultRows.Close()
- if !resultRows.Next() {
- return tc.DeliveryServiceNullable{}, http.StatusNotFound,
errors.New("no delivery service found with this id"), nil
- }
- lastUpdated := tc.TimeNoMod{}
- if err := resultRows.Scan(&lastUpdated); err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("scan updating delivery
service: " + err.Error())
- }
- if resultRows.Next() {
- xmlID := ""
- if ds.XMLID != nil {
- xmlID = *ds.XMLID
- }
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("updating delivery service " +
xmlID + ": " + "this update affected too many rows: > 1")
- }
-
- if ds.ID == nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("missing id after update")
- }
- if ds.XMLID == nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("missing xml_id after update")
- }
- if ds.TypeID == nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("missing type after update")
- }
- if ds.RoutingName == nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("missing routing name after
update")
- }
- newDSType, err := getTypeFromID(*ds.TypeID, tx)
- if err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("getting delivery service type
after update: " + err.Error())
- }
- ds.Type = &newDSType
-
- cdnDomain, err := getCDNDomain(*ds.ID, tx) // need to get the domain
again, in case it changed.
- if err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("getting CDN domain after
update: " + err.Error())
- }
-
- matchLists, err := GetDeliveryServicesMatchLists([]string{*ds.XMLID},
tx)
- if err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("getting matchlists after
update: " + err.Error())
- }
- if ml, ok := matchLists[*ds.XMLID]; !ok {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("no matchlists after update")
- } else {
- ds.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 := ""
- if dsType.HasSSLKeys() {
- newHostName, err = getHostName(ds.Protocol, *ds.Type,
*ds.RoutingName, *ds.MatchList, cdnDomain)
- if err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("getting hostname after update:
" + err.Error())
- }
- }
-
- if newDSType.HasSSLKeys() && oldHostName != newHostName {
- if err := updateSSLKeys(ds, newHostName, tx, cfg); err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("updating delivery service " +
*ds.XMLID + ": updating SSL keys: " + err.Error())
- }
- }
-
- if err := EnsureParams(tx, *ds.ID, *ds.XMLID, ds.EdgeHeaderRewrite,
ds.MidHeaderRewrite, ds.RegexRemap, ds.CacheURL, ds.SigningAlgorithm,
newDSType, ds.MaxOriginConnections); err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("ensuring ds parameters:: " +
err.Error())
- }
-
- if err := updatePrimaryOrigin(tx, user, *ds); err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("updating delivery service: " +
err.Error())
- }
-
- ds.LastUpdated = &lastUpdated
-
- if err := api.CreateChangeLogRawErr(api.ApiChange, "Updated ds:
"+*ds.XMLID+" id: "+strconv.Itoa(*ds.ID), user, tx); err != nil {
- return tc.DeliveryServiceNullable{},
http.StatusInternalServerError, nil, errors.New("writing change log entry: " +
err.Error())
- }
- return *ds, http.StatusOK, nil, nil
-}
-
-// Delete is the DeliveryService implementation of the Deleter interface
-//all implementations of Deleter should use transactions and return the proper
errorType
-func (ds *TODeliveryServiceV13) Delete() (error, error, int) {
- return ds.V12().Delete()
-}
-
-// IsTenantAuthorized implements the Tenantable interface to ensure the user
is authorized on the deliveryservice tenant
-func (ds *TODeliveryServiceV13) IsTenantAuthorized(user *auth.CurrentUser)
(bool, error) {
- return ds.V12().IsTenantAuthorized(user)
-}
-
-func readGetDeliveryServices(params map[string]string, tx *sqlx.Tx, user
*auth.CurrentUser) ([]tc.DeliveryServiceNullable, []error, tc.ApiErrorType) {
- if strings.HasSuffix(params["id"], ".json") {
- params["id"] = params["id"][:len(params["id"])-len(".json")]
- }
- if _, ok := params["orderby"]; !ok {
- params["orderby"] = "xml_id"
- }
-
- // Query Parameters to Database Query column mappings
- // see the fields mapped in the SQL query
- queryParamsToSQLCols := map[string]dbhelpers.WhereColumnInfo{
- "id": dbhelpers.WhereColumnInfo{"ds.id",
api.IsInt},
- "cdn": dbhelpers.WhereColumnInfo{"ds.cdn_id",
api.IsInt},
- "xml_id": dbhelpers.WhereColumnInfo{"ds.xml_id", nil},
- "xmlId": dbhelpers.WhereColumnInfo{"ds.xml_id", nil},
- "profile": dbhelpers.WhereColumnInfo{"ds.profile",
api.IsInt},
- "type": dbhelpers.WhereColumnInfo{"ds.type",
api.IsInt},
- "logsEnabled":
dbhelpers.WhereColumnInfo{"ds.logs_enabled", api.IsBool},
- "tenant": dbhelpers.WhereColumnInfo{"ds.tenant_id",
api.IsInt},
- "signingAlgorithm":
dbhelpers.WhereColumnInfo{"ds.signing_algorithm", nil},
- }
-
- where, orderBy, queryValues, errs :=
dbhelpers.BuildWhereAndOrderBy(params, queryParamsToSQLCols)
- if len(errs) > 0 {
- return nil, errs, tc.DataConflictError
- }
-
- tenantIDs, err := tenant.GetUserTenantIDListTx(tx.Tx, user.TenantID)
- if err != nil {
- log.Errorln("received error querying for user's tenants: " +
err.Error())
- return nil, []error{tc.DBError}, tc.SystemError
- }
- where, queryValues = dbhelpers.AddTenancyCheck(where, queryValues,
"ds.tenant_id", tenantIDs)
- query := selectQuery() + where + orderBy
-
- log.Debugln("generated deliveryServices query: " + query)
- log.Debugf("executing with values: %++v\n", queryValues)
-
- return GetDeliveryServices(query, queryValues, tx)
-}
-
-func GetDeliveryServices(query string, queryValues map[string]interface{}, tx
*sqlx.Tx) ([]tc.DeliveryServiceNullable, []error, tc.ApiErrorType) {
- rows, err := tx.NamedQuery(query, queryValues)
- if err != nil {
- return nil, []error{fmt.Errorf("querying: %v", err)},
tc.SystemError
- }
- defer rows.Close()
-
- dses := []tc.DeliveryServiceNullable{}
- dsCDNDomains := map[string]string{}
- for rows.Next() {
- ds := tc.DeliveryServiceNullable{}
- cdnDomain := ""
- err := rows.Scan(&ds.Active, &ds.AnonymousBlockingEnabled,
&ds.CacheURL, &ds.CCRDNSTTL, &ds.CDNID, &ds.CDNName, &ds.CheckPath,
&ds.ConsistentHashRegex, &ds.DeepCachingType, &ds.DisplayName,
&ds.DNSBypassCNAME, &ds.DNSBypassIP, &ds.DNSBypassIP6, &ds.DNSBypassTTL,
&ds.DSCP, &ds.EdgeHeaderRewrite, &ds.GeoLimitRedirectURL, &ds.GeoLimit,
&ds.GeoLimitCountries, &ds.GeoProvider, &ds.GlobalMaxMBPS, &ds.GlobalMaxTPS,
&ds.FQPacingRate, &ds.HTTPBypassFQDN, &ds.ID, &ds.InfoURL, &ds.InitialDispersi
[...]
- if err != nil {
- return nil, []error{fmt.Errorf("getting delivery
services: %v", err)}, tc.SystemError
- }
- dsCDNDomains[*ds.XMLID] = cdnDomain
- if ds.DeepCachingType != nil {
- *ds.DeepCachingType =
tc.DeepCachingTypeFromString(string(*ds.DeepCachingType))
- }
- ds.Signed = ds.SigningAlgorithm != nil && *ds.SigningAlgorithm
== tc.SigningAlgorithmURLSig
- dses = append(dses, ds)
- }
-
- dsNames := make([]string, len(dses), len(dses))
- for i, ds := range dses {
- dsNames[i] = *ds.XMLID
- }
-
- matchLists, err := GetDeliveryServicesMatchLists(dsNames, tx.Tx)
- if err != nil {
- return nil, []error{errors.New("getting delivery service
matchlists: " + err.Error())}, tc.SystemError
- }
- for i, ds := range dses {
- matchList, ok := matchLists[*ds.XMLID]
- if !ok {
- continue
- }
- ds.MatchList = &matchList
- ds.ExampleURLs = MakeExampleURLs(ds.Protocol, *ds.Type,
*ds.RoutingName, *ds.MatchList, dsCDNDomains[*ds.XMLID])
- dses[i] = ds
- }
-
- return dses, nil, tc.NoError
-}
-
-func updateSSLKeys(ds *tc.DeliveryServiceNullable, hostName string, tx
*sql.Tx, cfg config.Config) error {
- if ds.XMLID == nil {
- return errors.New("delivery services has no XMLID!")
- }
- key, ok, err := riaksvc.GetDeliveryServiceSSLKeysObj(*ds.XMLID,
riaksvc.DSSSLKeyVersionLatest, tx, cfg.RiakAuthOptions, cfg.RiakPort)
- if err != nil {
- return errors.New("getting SSL key: " + err.Error())
- }
- if !ok {
- return nil // no keys to update
- }
- key.DeliveryService = *ds.XMLID
- key.Hostname = hostName
- if err := riaksvc.PutDeliveryServiceSSLKeysObj(key, tx,
cfg.RiakAuthOptions, cfg.RiakPort); err != nil {
- return errors.New("putting updated SSL key: " + err.Error())
- }
- return nil
-}
-
-// getHostName gets the host name used for delivery service requests. The
dsProtocol may be nil, if the delivery service type doesn't have a protocol
(e.g. ANY_MAP).
-func getHostName(dsProtocol *int, dsType tc.DSType, dsRoutingName string,
dsMatchList []tc.DeliveryServiceMatch, cdnDomain string) (string, error) {
- exampleURLs := MakeExampleURLs(dsProtocol, dsType, dsRoutingName,
dsMatchList, cdnDomain)
-
- exampleURL := ""
- if dsProtocol != nil && *dsProtocol == 2 {
- if len(exampleURLs) < 2 {
- return "", errors.New("missing example URLs (does your
delivery service have matchsets?)")
- }
- exampleURL = exampleURLs[1]
- } else {
- if len(exampleURLs) < 1 {
- return "", errors.New("missing example URLs (does your
delivery service have matchsets?)")
- }
- exampleURL = exampleURLs[0]
- }
-
- host := strings.NewReplacer(`http://`, ``, `https://`,
``).Replace(exampleURL)
- if dsType.IsHTTP() {
- if firstDot := strings.Index(host, "."); firstDot == -1 {
- host = "*" // TODO warn? error?
- } else {
- host = "*" + host[firstDot:]
- }
- }
- return host, nil
-}
-
-func getCDNDomain(dsID int, tx *sql.Tx) (string, error) {
- q := `SELECT cdn.domain_name from cdn where cdn.id = (SELECT ds.cdn_id
from deliveryservice as ds where ds.id = $1)`
- cdnDomain := ""
- if err := tx.QueryRow(q, dsID).Scan(&cdnDomain); err != nil {
- return "", fmt.Errorf("getting CDN domain for delivery service
'%v': "+err.Error(), dsID)
- }
- return cdnDomain, nil
-}
-
-func getCDNNameDomainDNSSecEnabled(dsID int, tx *sql.Tx) (string, string,
bool, error) {
- q := `SELECT cdn.name, cdn.domain_name, cdn.dnssec_enabled from cdn
where cdn.id = (SELECT ds.cdn_id from deliveryservice as ds where ds.id = $1)`
- cdnName := ""
- cdnDomain := ""
- dnssecEnabled := false
- if err := tx.QueryRow(q, dsID).Scan(&cdnName, &cdnDomain,
&dnssecEnabled); err != nil {
- return "", "", false, fmt.Errorf("getting dnssec_enabled for
delivery service '%v': "+err.Error(), dsID)
- }
- return cdnName, cdnDomain, dnssecEnabled, nil
-}
-
-// makeExampleURLs creates the example URLs for a delivery service. The
dsProtocol may be nil, if the delivery service type doesn't have a protocol
(e.g. ANY_MAP).
-func MakeExampleURLs(protocol *int, dsType tc.DSType, routingName string,
matchList []tc.DeliveryServiceMatch, cdnDomain string) []string {
- examples := []string{}
- scheme := ""
- scheme2 := ""
- if protocol != nil {
- switch *protocol {
- case 0:
- scheme = "http"
- case 1:
- scheme = "https"
- case 2:
- fallthrough
- case 3:
- scheme = "http"
- scheme2 = "https"
- default:
- scheme = "http"
- }
- } else {
- scheme = "http"
- }
- dsIsDNS := dsType.IsDNS()
- regexReplacer := strings.NewReplacer(`\`, ``, `.*`, ``, `.`, ``)
- for _, match := range matchList {
- if dsIsDNS || match.Type == tc.DSMatchTypeHostRegex {
- host := regexReplacer.Replace(match.Pattern)
- if match.SetNumber == 0 {
- examples = append(examples,
scheme+`://`+routingName+`.`+host+`.`+cdnDomain)
- if scheme2 != "" {
- examples = append(examples,
scheme2+`://`+routingName+`.`+host+`.`+cdnDomain)
- }
- continue
- }
- examples = append(examples, scheme+`://`+match.Pattern)
- if scheme2 != "" {
- examples = append(examples,
scheme2+`://`+match.Pattern)
- }
- } else if match.Type == tc.DSMatchTypePathRegex {
- examples = append(examples, match.Pattern)
- }
- }
- return examples
-}
-
-func GetDeliveryServicesMatchLists(dses []string, tx *sql.Tx)
(map[string][]tc.DeliveryServiceMatch, error) {
- // TODO move somewhere generic
- q := `
-SELECT ds.xml_id as ds_name, t.name as type, r.pattern,
COALESCE(dsr.set_number, 0)
-FROM regex as r
-JOIN deliveryservice_regex as dsr ON dsr.regex = r.id
-JOIN deliveryservice as ds on ds.id = dsr.deliveryservice
-JOIN type as t ON r.type = t.id
-WHERE ds.xml_id = ANY($1)
-ORDER BY dsr.set_number
-`
- rows, err := tx.Query(q, pq.Array(dses))
- if err != nil {
- return nil, errors.New("getting delivery service regexes: " +
err.Error())
- }
- defer rows.Close()
-
- matches := map[string][]tc.DeliveryServiceMatch{}
- for rows.Next() {
- m := tc.DeliveryServiceMatch{}
- dsName := ""
- matchTypeStr := ""
- if err := rows.Scan(&dsName, &matchTypeStr, &m.Pattern,
&m.SetNumber); err != nil {
- return nil, errors.New("scanning delivery service
regexes: " + err.Error())
- }
- matchType := tc.DSMatchTypeFromString(matchTypeStr)
- if matchType == tc.DSMatchTypeInvalid {
- return nil, errors.New("getting delivery service
regexes: got invalid delivery service match type '" + matchTypeStr + "'")
- }
- m.Type = matchType
- matches[dsName] = append(matches[dsName], m)
- }
- return matches, nil
-}
-
-type tierType int
-
-const (
- midTier tierType = iota
- edgeTier
-)
-
-// EnsureParams ensures the given delivery service's necessary parameters
exist on profiles of servers assigned to the delivery service.
-// Note the edgeHeaderRewrite, midHeaderRewrite, regexRemap, and cacheURL may
be nil, if the delivery service does not have those values.
-func EnsureParams(tx *sql.Tx, dsID int, xmlID string, edgeHeaderRewrite
*string, midHeaderRewrite *string, regexRemap *string, cacheURL *string,
signingAlgorithm *string, dsType tc.DSType, maxOriginConns *int) error {
- if err := ensureHeaderRewriteParams(tx, dsID, xmlID, edgeHeaderRewrite,
edgeTier, dsType, maxOriginConns); err != nil {
- return errors.New("creating edge header rewrite parameters: " +
err.Error())
- }
- if err := ensureHeaderRewriteParams(tx, dsID, xmlID, midHeaderRewrite,
midTier, dsType, maxOriginConns); err != nil {
- return errors.New("creating mid header rewrite parameters: " +
err.Error())
- }
- if err := ensureRegexRemapParams(tx, dsID, xmlID, regexRemap); err !=
nil {
- return errors.New("creating mid regex remap parameters: " +
err.Error())
- }
- if err := ensureCacheURLParams(tx, dsID, xmlID, cacheURL); err != nil {
- return errors.New("creating mid cacheurl parameters: " +
err.Error())
- }
- if err := ensureURLSigParams(tx, dsID, xmlID, signingAlgorithm); err !=
nil {
- return errors.New("creating urlsig parameters: " + err.Error())
- }
- return nil
-}
-
-func ensureHeaderRewriteParams(tx *sql.Tx, dsID int, xmlID string, hdrRW
*string, tier tierType, dsType tc.DSType, maxOriginConns *int) error {
- configFile := "hdr_rw_" + xmlID + ".config"
- if tier == midTier {
- configFile = "hdr_rw_mid_" + xmlID + ".config"
- }
-
- if tier == midTier && dsType.IsLive() && !dsType.IsNational() {
- // live local DSes don't get header rewrite rules on the mid so
cleanup any location params related to mids
- return deleteLocationParam(tx, configFile)
- }
-
- hasMaxOriginConns := *maxOriginConns > 0 && ((tier == midTier) ==
dsType.UsesMidCache())
- if (hdrRW == nil || *hdrRW == "") && !hasMaxOriginConns {
- return deleteLocationParam(tx, configFile)
- }
- locationParamID, err := ensureLocation(tx, configFile)
- if err != nil {
- return err
- }
- if tier != midTier {
- return createDSLocationProfileParams(tx, locationParamID, dsID)
- }
- profileParameterQuery := `
-INSERT INTO profile_parameter (profile, parameter)
-SELECT DISTINCT(profile), $1::bigint FROM server
-WHERE server.type IN (SELECT id from type where type.name like 'MID%' and
type.use_in_table = 'server')
-AND server.cdn_id = (select cdn_id from deliveryservice where id = $2)
-ON CONFLICT DO NOTHING
-`
- if _, err := tx.Exec(profileParameterQuery, locationParamID, dsID); err
!= nil {
- return fmt.Errorf("parameter query to insert profile_parameters
query '"+profileParameterQuery+"' location parameter ID '%v' delivery service
ID '%v': %v", locationParamID, dsID, err)
- }
- return nil
-}
-
-func ensureURLSigParams(tx *sql.Tx, dsID int, xmlID string, signingAlgorithm
*string) error {
- configFile := "url_sig_" + xmlID + ".config"
- if signingAlgorithm == nil || *signingAlgorithm !=
tc.SigningAlgorithmURLSig {
- return deleteLocationParam(tx, configFile)
- }
- locationParamID, err := ensureLocation(tx, configFile)
- if err != nil {
- return err
- }
- return createDSLocationProfileParams(tx, locationParamID, dsID)
-}
-
-func ensureRegexRemapParams(tx *sql.Tx, dsID int, xmlID string, regexRemap
*string) error {
- configFile := "regex_remap_" + xmlID + ".config"
- if regexRemap == nil || *regexRemap == "" {
- return deleteLocationParam(tx, configFile)
- }
- locationParamID, err := ensureLocation(tx, configFile)
- if err != nil {
- return err
- }
- return createDSLocationProfileParams(tx, locationParamID, dsID)
-}
-
-func ensureCacheURLParams(tx *sql.Tx, dsID int, xmlID string, cacheURL
*string) error {
- configFile := "cacheurl_" + xmlID + ".config"
- if cacheURL == nil || *cacheURL == "" {
- return deleteLocationParam(tx, configFile)
- }
- locationParamID, err := ensureLocation(tx, configFile)
- if err != nil {
- return err
- }
- return createDSLocationProfileParams(tx, locationParamID, dsID)
-}
-
-// createDSLocationProfileParams adds the given parameter to all profiles
assigned to servers which are assigned to the given delivery service.
-func createDSLocationProfileParams(tx *sql.Tx, locationParamID int,
deliveryServiceID int) error {
- profileParameterQuery := `
-INSERT INTO profile_parameter (profile, parameter)
-SELECT DISTINCT(profile), $1::bigint FROM server
-WHERE server.id IN (SELECT server from deliveryservice_server where
deliveryservice = $2)
-ON CONFLICT DO NOTHING
-`
- if _, err := tx.Exec(profileParameterQuery, locationParamID,
deliveryServiceID); err != nil {
- return errors.New("inserting profile_parameters: " +
err.Error())
- }
- return nil
-}
-
-// ensureLocation ensures a location parameter exists for the given config
file. If not, it creates one, with the same value as the 'remap.config' file
parameter. Returns the ID of the location parameter.
-func ensureLocation(tx *sql.Tx, configFile string) (int, error) {
- atsConfigLocation := ""
- if err := tx.QueryRow(`SELECT value FROM parameter WHERE name =
'location' AND config_file = 'remap.config'`).Scan(&atsConfigLocation); err !=
nil {
- if err == sql.ErrNoRows {
- return 0, errors.New("executing parameter query for ATS
config location: parameter missing (do you have a name=location
config_file=remap.config parameter?")
- }
- return 0, errors.New("executing parameter query for ATS config
location: " + err.Error())
- }
- atsConfigLocation = strings.TrimRight(atsConfigLocation, `/`)
-
- locationParamID := 0
- existingLocationErr := tx.QueryRow(`SELECT id FROM parameter WHERE name
= 'location' AND config_file = $1`, configFile).Scan(&locationParamID)
- if existingLocationErr != nil && existingLocationErr != sql.ErrNoRows {
- return 0, errors.New("executing parameter query for existing
location: " + existingLocationErr.Error())
- }
-
- if existingLocationErr == sql.ErrNoRows {
- resultRows, err := tx.Query(`INSERT INTO parameter
(config_file, name, value) VALUES ($1, 'location', $2) RETURNING id`,
configFile, atsConfigLocation)
- if err != nil {
- return 0, errors.New("executing parameter query to
insert location: " + err.Error())
- }
- defer resultRows.Close()
- if !resultRows.Next() {
- return 0, errors.New("parameter query to insert
location didn't return id")
- }
- if err := resultRows.Scan(&locationParamID); err != nil {
- return 0, errors.New("parameter query to insert
location returned id scan: " + err.Error())
- }
- if resultRows.Next() {
- return 0, errors.New("parameter query to insert
location returned too many rows (>1)")
- }
- }
- return locationParamID, nil
-}
-
-func deleteLocationParam(tx *sql.Tx, configFile string) error {
- id := 0
- err := tx.QueryRow(`DELETE FROM parameter WHERE name = 'location' AND
config_file = $1 RETURNING id`, configFile).Scan(&id)
- if err == sql.ErrNoRows {
- return nil
- }
- if err != nil {
- log.Errorln("deleting name=location config_file=" + configFile
+ " parameter: " + err.Error())
- return errors.New("executing parameter delete: " + err.Error())
- }
- if _, err := tx.Exec(`DELETE FROM profile_parameter WHERE parameter =
$1`, id); err != nil {
- log.Errorf("deleting parameter name=location config_file=%v
id=%v profile_parameter: %v", configFile, id, err)
- return errors.New("executing parameter profile_parameter
delete: " + err.Error())
- }
- return nil
-}
-
-// export the selectQuery for the 'deliveryservice' package.
-func GetDSSelectQuery() string {
- return selectQuery()
-}
-
-func selectQuery() string {
- return `
-SELECT
-ds.active,
-ds.anonymous_blocking_enabled,
-ds.cacheurl,
-ds.ccr_dns_ttl,
-ds.cdn_id,
-cdn.name as cdnName,
-ds.check_path,
-ds.consistent_hash_regex,
-CAST(ds.deep_caching_type AS text) as deep_caching_type,
-ds.display_name,
-ds.dns_bypass_cname,
-ds.dns_bypass_ip,
-ds.dns_bypass_ip6,
-ds.dns_bypass_ttl,
-ds.dscp,
-ds.edge_header_rewrite,
-ds.geolimit_redirect_url,
-ds.geo_limit,
-ds.geo_limit_countries,
-ds.geo_provider,
-ds.global_max_mbps,
-ds.global_max_tps,
-ds.fq_pacing_rate,
-ds.http_bypass_fqdn,
-ds.id,
-ds.info_url,
-ds.initial_dispersion,
-ds.ipv6_routing_enabled,
-ds.last_updated,
-ds.logs_enabled,
-ds.long_desc,
-ds.long_desc_1,
-ds.long_desc_2,
-ds.max_dns_answers,
-ds.max_origin_connections,
-ds.mid_header_rewrite,
-COALESCE(ds.miss_lat, 0.0),
-COALESCE(ds.miss_long, 0.0),
-ds.multi_site_origin,
-(SELECT o.protocol::::text || ':://' || o.fqdn || rtrim(concat('::',
o.port::::text), '::')
- FROM origin o
- WHERE o.deliveryservice = ds.id
- AND o.is_primary) as org_server_fqdn,
-ds.origin_shield,
-ds.profile as profileID,
-profile.name as profile_name,
-profile.description as profile_description,
-ds.protocol,
-ds.qstring_ignore,
-ds.range_request_handling,
-ds.regex_remap,
-ds.regional_geo_blocking,
-ds.remap_text,
-ds.routing_name,
-ds.signing_algorithm,
-ds.ssl_key_version,
-ds.tenant_id,
-tenant.name,
-ds.tr_request_headers,
-ds.tr_response_headers,
-type.name,
-ds.type as type_id,
-ds.xml_id,
-cdn.domain_name as cdn_domain
-from deliveryservice as ds
-JOIN type ON ds.type = type.id
-JOIN cdn ON ds.cdn_id = cdn.id
-LEFT JOIN profile ON ds.profile = profile.id
-LEFT JOIN tenant ON ds.tenant_id = tenant.id
-`
-}
-
-func updateDSQuery() string {
- return `
-UPDATE
-deliveryservice SET
-active=$1,
-cacheurl=$2,
-ccr_dns_ttl=$3,
-cdn_id=$4,
-check_path=$5,
-deep_caching_type=$6,
-display_name=$7,
-dns_bypass_cname=$8,
-dns_bypass_ip=$9,
-dns_bypass_ip6=$10,
-dns_bypass_ttl=$11,
-dscp=$12,
-edge_header_rewrite=$13,
-geolimit_redirect_url=$14,
-geo_limit=$15,
-geo_limit_countries=$16,
-geo_provider=$17,
-global_max_mbps=$18,
-global_max_tps=$19,
-fq_pacing_rate=$20,
-http_bypass_fqdn=$21,
-info_url=$22,
-initial_dispersion=$23,
-ipv6_routing_enabled=$24,
-logs_enabled=$25,
-long_desc=$26,
-long_desc_1=$27,
-long_desc_2=$28,
-max_dns_answers=$29,
-mid_header_rewrite=$30,
-miss_lat=$31,
-miss_long=$32,
-multi_site_origin=$33,
-origin_shield=$34,
-profile=$35,
-protocol=$36,
-qstring_ignore=$37,
-range_request_handling=$38,
-regex_remap=$39,
-regional_geo_blocking=$40,
-remap_text=$41,
-routing_name=$42,
-signing_algorithm=$43,
-ssl_key_version=$44,
-tenant_id=$45,
-tr_request_headers=$46,
-tr_response_headers=$47,
-type=$48,
-xml_id=$49,
-anonymous_blocking_enabled=$50,
-consistent_hash_regex=$51,
-max_origin_connections=$52
-WHERE id=$53
-RETURNING last_updated
-`
-}
-
-func insertQuery() string {
- return `
-INSERT INTO deliveryservice (
-active,
-anonymous_blocking_enabled,
-cacheurl,
-ccr_dns_ttl,
-cdn_id,
-check_path,
-consistent_hash_regex,
-deep_caching_type,
-display_name,
-dns_bypass_cname,
-dns_bypass_ip,
-dns_bypass_ip6,
-dns_bypass_ttl,
-dscp,
-edge_header_rewrite,
-geolimit_redirect_url,
-geo_limit,
-geo_limit_countries,
-geo_provider,
-global_max_mbps,
-global_max_tps,
-fq_pacing_rate,
-http_bypass_fqdn,
-info_url,
-initial_dispersion,
-ipv6_routing_enabled,
-logs_enabled,
-long_desc,
-long_desc_1,
-long_desc_2,
-max_dns_answers,
-max_origin_connections,
-mid_header_rewrite,
-miss_lat,
-miss_long,
-multi_site_origin,
-origin_shield,
-profile,
-protocol,
-qstring_ignore,
-range_request_handling,
-regex_remap,
-regional_geo_blocking,
-remap_text,
-routing_name,
-signing_algorithm,
-ssl_key_version,
-tenant_id,
-tr_request_headers,
-tr_response_headers,
-type,
-xml_id
-)
-VALUES
($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21,$22,$23,$24,$25,$26,$27,$28,$29,$30,$31,$32,$33,$34,$35,$36,$37,$38,$39,$40,$41,$42,$43,$44,$45,$46,$47,$48,$49,$50,$51,$52)
-RETURNING id, last_updated
-`
-}
diff --git
a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv14.go
b/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv14.go
deleted file mode 100644
index 98f3337..0000000
--- a/traffic_ops/traffic_ops_golang/deliveryservice/deliveryservicesv14.go
+++ /dev/null
@@ -1,170 +0,0 @@
-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 (
- "encoding/json"
- "errors"
- "net/http"
-
- "github.com/apache/trafficcontrol/lib/go-tc"
- "github.com/apache/trafficcontrol/lib/go-util"
- "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
- "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/auth"
-)
-
-//we need a type alias to define functions on
-
-type TODeliveryServiceV14 struct {
- api.APIInfoImpl
- tc.DeliveryServiceNullable
-}
-
-func (ds *TODeliveryServiceV14) V13() *TODeliveryServiceV13 {
- v13 := &TODeliveryServiceV13{}
- v13.DeliveryServiceNullableV13 = ds.DeliveryServiceNullableV13
- v13.SetInfo(ds.ReqInfo)
- return v13
-}
-
-func (ds TODeliveryServiceV14) MarshalJSON() ([]byte, error) {
- return json.Marshal(ds.DeliveryServiceNullable)
-}
-
-func (ds *TODeliveryServiceV14) UnmarshalJSON(data []byte) error {
- return json.Unmarshal(data, ds.DeliveryServiceNullable)
-}
-
-func (ds *TODeliveryServiceV14) APIInfo() *api.APIInfo { return ds.ReqInfo }
-
-func (ds TODeliveryServiceV14) GetKeyFieldsInfo() []api.KeyFieldInfo {
- return ds.V13().GetKeyFieldsInfo()
-}
-
-//Implementation of the Identifier, Validator interface functions
-func (ds TODeliveryServiceV14) GetKeys() (map[string]interface{}, bool) {
- return ds.V13().GetKeys()
-}
-
-func (ds *TODeliveryServiceV14) SetKeys(keys map[string]interface{}) {
- i, _ := keys["id"].(int) //this utilizes the non panicking type
assertion, if the thrown away ok variable is false i will be the zero of the
type, 0 here.
- ds.ID = &i
-}
-
-func (ds *TODeliveryServiceV14) GetAuditName() string {
- return ds.V13().GetAuditName()
-}
-
-func (ds *TODeliveryServiceV14) GetType() string {
- return ds.V13().GetType()
-}
-
-func (ds *TODeliveryServiceV14) Validate() error {
- return ds.DeliveryServiceNullable.Validate(ds.APIInfo().Tx.Tx)
-}
-
-// TODO allow users to post names (type, cdn, etc) and get the IDs from
the names. This isn't trivial to do in a single query, without dynamically
building the entire insert query, and ideally inserting would be one query. But
it'd be much more convenient for users. Alternatively, remove IDs from the
database entirely and use real candidate keys.
-func CreateV14(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()
-
- ds := tc.DeliveryServiceNullable{}
- 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
- }
-
- if ds.RoutingName == nil || *ds.RoutingName == "" {
- ds.RoutingName = util.StrPtr("cdn")
- }
- if err := ds.Validate(inf.Tx.Tx); err != nil {
- api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest,
errors.New("invalid request: "+err.Error()), nil)
- return
- }
- ds, errCode, userErr, sysErr = create(inf.Tx.Tx, *inf.Config, inf.User,
ds)
- if userErr != nil || sysErr != nil {
- api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
- return
- }
- api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice creation
was successful.", []tc.DeliveryServiceNullable{ds})
-}
-
-func (ds *TODeliveryServiceV14) Read() ([]interface{}, error, error, int) {
- returnable := []interface{}{}
- dses, errs, _ := readGetDeliveryServices(ds.APIInfo().Params,
ds.APIInfo().Tx, ds.APIInfo().User)
- if len(errs) > 0 {
- for _, err := range errs {
- if err.Error() == `id cannot parse to integer` { //
TODO create const for string
- return nil, errors.New("Resource not found."),
nil, http.StatusNotFound //matches perl response
- }
- }
- return nil, nil, errors.New("reading dses: " +
util.JoinErrsStr(errs)), http.StatusInternalServerError
- }
-
- for _, ds := range dses {
- returnable = append(returnable, ds)
- }
- return returnable, nil, nil, http.StatusOK
-}
-
-func UpdateV14(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"]
-
- ds := tc.DeliveryServiceNullable{}
- if err := json.NewDecoder(r.Body).Decode(&ds); err != nil {
- api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest,
errors.New("malformed JSON: "+err.Error()), nil)
- return
- }
- ds.ID = &id
-
- if err := ds.Validate(inf.Tx.Tx); err != nil {
- api.HandleErr(w, r, inf.Tx.Tx, http.StatusBadRequest,
errors.New("invalid request: "+err.Error()), nil)
- return
- }
-
- ds, errCode, userErr, sysErr = update(inf.Tx.Tx, *inf.Config, inf.User,
&ds)
- if userErr != nil || sysErr != nil {
- api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
- return
- }
- api.WriteRespAlertObj(w, r, tc.SuccessLevel, "Deliveryservice update
was successful.", []tc.DeliveryServiceNullable{ds})
-}
-
-// Delete is the DeliveryService implementation of the Deleter interface
-//all implementations of Deleter should use transactions and return the proper
errorType
-func (ds *TODeliveryServiceV14) Delete() (error, error, int) {
- return ds.V13().Delete()
-}
-
-// IsTenantAuthorized implements the Tenantable interface to ensure the user
is authorized on the deliveryservice tenant
-func (ds *TODeliveryServiceV14) IsTenantAuthorized(user *auth.CurrentUser)
(bool, error) {
- return ds.V13().IsTenantAuthorized(user)
-}
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go
b/traffic_ops/traffic_ops_golang/routing/routes.go
index aeb8594..532d73c 100644
--- a/traffic_ops/traffic_ops_golang/routing/routes.go
+++ b/traffic_ops/traffic_ops_golang/routing/routes.go
@@ -389,24 +389,22 @@ func Routes(d ServerData) ([]Route, []RawRoute,
http.Handler, error) {
{1.1, http.MethodPost,
`federations/{id}/deliveryservices?(\.json)?$`, federations.PostDSes,
auth.PrivLevelAdmin, Authenticated, nil},
////DeliveryServices
- {1.4, http.MethodGet, `deliveryservices/?(\.json)?$`,
api.ReadHandler(&deliveryservice.TODeliveryServiceV14{}),
auth.PrivLevelReadOnly, Authenticated, nil},
+ {1.4, http.MethodGet, `deliveryservices/?(\.json)?$`,
api.ReadHandler(&deliveryservice.TODeliveryService{}), auth.PrivLevelReadOnly,
Authenticated, nil},
{1.3, http.MethodGet, `deliveryservices/?(\.json)?$`,
api.ReadHandler(&deliveryservice.TODeliveryServiceV13{}),
auth.PrivLevelReadOnly, Authenticated, nil},
{1.1, http.MethodGet, `deliveryservices/?(\.json)?$`,
api.ReadHandler(&deliveryservice.TODeliveryServiceV12{}),
auth.PrivLevelReadOnly, Authenticated, nil},
- {1.4, http.MethodGet, `deliveryservices/{id}/?(\.json)?$`,
api.ReadHandler(&deliveryservice.TODeliveryServiceV14{}),
auth.PrivLevelReadOnly, Authenticated, nil},
+ {1.4, http.MethodGet, `deliveryservices/{id}/?(\.json)?$`,
api.ReadHandler(&deliveryservice.TODeliveryService{}), auth.PrivLevelReadOnly,
Authenticated, nil},
{1.3, http.MethodGet, `deliveryservices/{id}/?(\.json)?$`,
api.ReadHandler(&deliveryservice.TODeliveryServiceV13{}),
auth.PrivLevelReadOnly, Authenticated, nil},
{1.1, http.MethodGet, `deliveryservices/{id}/?(\.json)?$`,
api.ReadHandler(&deliveryservice.TODeliveryServiceV12{}),
auth.PrivLevelReadOnly, Authenticated, nil},
- {1.4, http.MethodPost, `deliveryservices/?(\.json)?$`,
deliveryservice.CreateV14, auth.PrivLevelOperations, Authenticated, nil},
+ {1.4, http.MethodPost, `deliveryservices/?(\.json)?$`,
deliveryservice.Create, auth.PrivLevelOperations, Authenticated, nil},
{1.3, http.MethodPost, `deliveryservices/?(\.json)?$`,
deliveryservice.CreateV13, auth.PrivLevelOperations, Authenticated, nil},
{1.1, http.MethodPost, `deliveryservices/?(\.json)?$`,
deliveryservice.CreateV12, auth.PrivLevelOperations, Authenticated, nil},
- {1.4, http.MethodPut, `deliveryservices/{id}/?(\.json)?$`,
deliveryservice.UpdateV14, auth.PrivLevelOperations, Authenticated, nil},
+ {1.4, http.MethodPut, `deliveryservices/{id}/?(\.json)?$`,
deliveryservice.Update, auth.PrivLevelOperations, Authenticated, nil},
{1.3, http.MethodPut, `deliveryservices/{id}/?(\.json)?$`,
deliveryservice.UpdateV13, auth.PrivLevelOperations, Authenticated, nil},
{1.1, http.MethodPut, `deliveryservices/{id}/?(\.json)?$`,
deliveryservice.UpdateV12, auth.PrivLevelOperations, Authenticated, nil},
- {1.4, http.MethodDelete, `deliveryservices/{id}/?(\.json)?$`,
api.DeleteHandler(&deliveryservice.TODeliveryServiceV14{}),
auth.PrivLevelOperations, Authenticated, nil},
- {1.3, http.MethodDelete, `deliveryservices/{id}/?(\.json)?$`,
api.DeleteHandler(&deliveryservice.TODeliveryServiceV13{}),
auth.PrivLevelOperations, Authenticated, nil},
{1.1, http.MethodDelete, `deliveryservices/{id}/?(\.json)?$`,
api.DeleteHandler(&deliveryservice.TODeliveryServiceV12{}),
auth.PrivLevelOperations, Authenticated, nil},
{1.1, http.MethodGet,
`deliveryservices/{id}/servers/eligible/?(\.json)?$`,
deliveryservice.GetServersEligible, auth.PrivLevelReadOnly, Authenticated, nil},