This is an automated email from the ASF dual-hosted git repository.
ocket8888 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 8ce828a531 To origin rfc3339 (#7733)
8ce828a531 is described below
commit 8ce828a531c3f544481bce91b7a7d3ca82b70777
Author: Kannan.G.B <[email protected]>
AuthorDate: Tue Sep 5 22:56:58 2023 +0530
To origin rfc3339 (#7733)
* origin initial function completion
* origin test fixes
* origin doc updated
* origin change log
* v50 correction
* typo fix
* type correction
* format caps removed
* changed refrence types
* comments addresed
* test updated
* origin delete
* review comments addressed
* test case and comment fixes
* Merge branch 'master' into to-origin-rfc3339
* var fix
* changelog
* test case issues
* for delete fnc
* test pass
* fixes test case
* test case fixes
* comment fixs
* comment addresed
* comment fixed
* comment addressed
* comment
* comment addressed
* formatting
---
CHANGELOG.md | 3 +-
docs/source/api/v5/origins.rst | 12 +-
lib/go-tc/origins.go | 74 +++++
traffic_ops/testing/api/v5/origins_test.go | 322 +++++++++++----------
traffic_ops/testing/api/v5/traffic_control_test.go | 2 +-
traffic_ops/traffic_ops_golang/origin/origins.go | 294 ++++++++++++++++++-
traffic_ops/traffic_ops_golang/routing/routes.go | 8 +-
traffic_ops/v5-client/origin.go | 34 +--
8 files changed, 558 insertions(+), 191 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6e87c4aac1..c422fcb923 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -106,8 +106,9 @@ The format is based on [Keep a
Changelog](http://keepachangelog.com/en/1.0.0/).
- [#7691](https://github.com/apache/trafficcontrol/pull/7691) *Traffic
Ops* Fixed `/topologies` v5 APIs to respond with `RFC3339` timestamps.
- [#7413](https://github.com/apache/trafficcontrol/issues/7413) *Traffic
Ops* Fixed `/service_category` v5 APIs to respond with `RFC3339` timestamps.
- [#7413](https://github.com/apache/trafficcontrol/issues/7706) *Traffic
Ops* Fixed `/statuses` v5 APIs to respond with `RFC3339` timestamps.
+ - [#7733](https://github.com/apache/trafficcontrol/pull/7733) *Traffic
Ops* Fixes `origins` v5 apis to respond with `RFC3339` date/time Format.
+ - [#7743](https://github.com/apache/trafficcontrol/issues/7743) *Traffic
Ops* Fixed `/server_server_capabilities` v5 APIs to respond with `RFC3339`
timestamps.
- [#7762](https://github.com/apache/trafficcontrol/pull/7762) *Traffic Ops*
Fixed `/phys_locations` update API to remove error related to mismatching
region name and ID.
-- [#7743](https://github.com/apache/trafficcontrol/issues/7743) *Traffic Ops*
Fixed `/server_server_capabilities` v5 APIs to respond with `RFC3339`
timestamps.
- [#7730](https://github.com/apache/trafficcontrol/pull/7730) *Traffic
Monitor* Fixed the panic seen in TM when `plugin.system_stats.timestamp_ms`
appears as float and not string.
- [#4393](https://github.com/apache/trafficcontrol/issues/4393) *Traffic Ops*
Fixed the error code and alert structure when TO is queried for a delivery
service with no ssl keys.
- [#7623](https://github.com/apache/trafficcontrol/pull/7623) *Traffic Ops*
Removed TryIfModifiedSinceQuery from servicecategories.go and reused from ims.go
diff --git a/docs/source/api/v5/origins.rst b/docs/source/api/v5/origins.rst
index c503ae8258..89c891acf8 100644
--- a/docs/source/api/v5/origins.rst
+++ b/docs/source/api/v5/origins.rst
@@ -89,7 +89,7 @@ Response Structure
:ip6Address: The IPv6 address of the :term:`Origin`
:ipAddress: The IPv4 address of the :term:`Origin`
:isPrimary: A boolean value which, when ``true`` specifies this
:term:`Origin` as the 'primary' :term:`Origin` served by ``deliveryService``
-:lastUpdated: The date and time at which this :term:`Origin` was last
modified
+:lastUpdated: The date and time at which this :term:`Origin` was last
modified in :rfc:`3339` format
:name: The name of the :term:`Origin`
:port: The TCP port on which the :term:`Origin` listens
:profile: The :ref:`profile-name` of the :term:`Profile` used by
this :term:`Origin`
@@ -126,7 +126,7 @@ Response Structure
"ip6Address": null,
"ipAddress": null,
"isPrimary": true,
- "lastUpdated": "2018-12-10 19:11:32+00",
+ "lastUpdated": "2018-12-10T15:59:33.7096-06:00",
"name": "demo1",
"port": null,
"profile": null,
@@ -201,7 +201,7 @@ Response Structure
:ip6Address: The IPv6 address of the :term:`Origin`
:ipAddress: The IPv4 address of the :term:`Origin`
:isPrimary: A boolean value which, when ``true`` specifies this
:term:`Origin` as the 'primary' :term:`Origin` served by ``deliveryService``
-:lastUpdated: The date and time at which this :term:`Origin` was last
modified
+:lastUpdated: The date and time at which this :term:`Origin` was last
modified in :rfc:`3339` format
:name: The name of the :term:`Origin`
:port: The TCP port on which the :term:`Origin` listens
:profile: The :ref:`profile-name` of the :term:`Profile` used by
this :term:`Origin`
@@ -243,7 +243,7 @@ Response Structure
"ip6Address": null,
"ipAddress": null,
"isPrimary": null,
- "lastUpdated": "2018-12-11 15:14:27+00",
+ "lastUpdated": "2018-12-11T15:59:33.7096-06:00",
"name": "example",
"port": 80,
"profile": null,
@@ -320,7 +320,7 @@ Response Structure
:ip6Address: The IPv6 address of the :term:`Origin`
:ipAddress: The IPv4 address of the :term:`Origin`
:isPrimary: A boolean value which, when ``true`` specifies this
:term:`Origin` as the 'primary' :term:`Origin` served by ``deliveryService``
-:lastUpdated: The date and time at which this :term:`Origin` was last
modified
+:lastUpdated: The date and time at which this :term:`Origin` was last
modified in :rfc:`3339` format
:name: The name of the :term:`Origin`
:port: The TCP port on which the :term:`Origin` listens
:profile: The :ref:`profile-name` of the :term:`Profile` used by
this :term:`Origin`
@@ -362,7 +362,7 @@ Response Structure
"ip6Address": null,
"ipAddress": null,
"isPrimary": true,
- "lastUpdated": "2018-12-11 15:40:53+00",
+ "lastUpdated": "2018-12-11T17:59:33.7096-06:00",
"name": "example",
"port": 443,
"profile": null,
diff --git a/lib/go-tc/origins.go b/lib/go-tc/origins.go
index e0b4b0a739..520664cf54 100644
--- a/lib/go-tc/origins.go
+++ b/lib/go-tc/origins.go
@@ -19,6 +19,8 @@ package tc
* under the License.
*/
+import "time"
+
// OriginsResponse is a list of Origins as a response.
type OriginsResponse struct {
Response []Origin `json:"response"`
@@ -54,3 +56,75 @@ type Origin struct {
Tenant *string `json:"tenant" db:"tenant"`
TenantID *int `json:"tenantId" db:"tenant_id"`
}
+
+// OriginsResponseV5 is an alias for the latest minor version of the major
version 5.
+type OriginsResponseV5 = OriginsResponseV50
+
+// OriginsResponseV50 is a list of Origins as a response for APIv5.
+type OriginsResponseV50 struct {
+ Response []OriginV5 `json:"response"`
+ Alerts
+}
+
+// OriginDetailResponseV5 is an alias for the latest minor version of the
major version 5.
+type OriginDetailResponseV5 = OriginDetailResponseV50
+
+// OriginDetailResponseV50 is the JSON object returned for a single origin in
APIv5.
+type OriginDetailResponseV50 struct {
+ Response OriginV5 `json:"response"`
+ Alerts
+}
+
+// OriginV5 is an alias for the latest minor version of the major version 5.
+type OriginV5 = OriginV50
+
+// OriginV50 contains information relating to an Origin, in the latest minor
version APIv50.
+type OriginV50 struct {
+ Cachegroup *string `json:"cachegroup" db:"cachegroup"`
+ CachegroupID *int `json:"cachegroupId" db:"cachegroup_id"`
+ Coordinate *string `json:"coordinate" db:"coordinate"`
+ CoordinateID *int `json:"coordinateId" db:"coordinate_id"`
+ DeliveryService string `json:"deliveryService"
db:"deliveryservice"`
+ DeliveryServiceID int `json:"deliveryServiceId"
db:"deliveryservice_id"`
+ FQDN string `json:"fqdn" db:"fqdn"`
+ ID int `json:"id" db:"id"`
+ IP6Address *string `json:"ip6Address" db:"ip6_address"`
+ IPAddress *string `json:"ipAddress" db:"ip_address"`
+ IsPrimary bool `json:"isPrimary" db:"is_primary"`
+ LastUpdated time.Time `json:"lastUpdated" db:"last_updated"`
+ Name string `json:"name" db:"name"`
+ Port *int `json:"port" db:"port"`
+ Profile *string `json:"profile" db:"profile"`
+ ProfileID *int `json:"profileId" db:"profile_id"`
+ Protocol string `json:"protocol" db:"protocol"`
+ Tenant string `json:"tenant" db:"tenant"`
+ TenantID int `json:"tenantId" db:"tenant_id"`
+}
+
+// ToOriginV5 upgrades from Origin to APIv5.
+func (old Origin) ToOriginV5() OriginV5 {
+ r := time.Unix(old.LastUpdated.Unix(), 0)
+
+ var originV5 OriginV5
+ originV5.Cachegroup = old.Cachegroup
+ originV5.CachegroupID = old.CachegroupID
+ originV5.Coordinate = old.Coordinate
+ originV5.CoordinateID = old.CoordinateID
+ originV5.DeliveryService = *old.DeliveryService
+ originV5.DeliveryServiceID = *old.DeliveryServiceID
+ originV5.FQDN = *old.FQDN
+ originV5.ID = *old.ID
+ originV5.IP6Address = old.IP6Address
+ originV5.IPAddress = old.IPAddress
+ originV5.IsPrimary = *old.IsPrimary
+ originV5.LastUpdated = r
+ originV5.Name = *old.Name
+ originV5.Port = old.Port
+ originV5.Profile = old.Profile
+ originV5.ProfileID = old.ProfileID
+ originV5.Protocol = *old.Protocol
+ originV5.Tenant = *old.Tenant
+ originV5.TenantID = *old.TenantID
+
+ return originV5
+}
diff --git a/traffic_ops/testing/api/v5/origins_test.go
b/traffic_ops/testing/api/v5/origins_test.go
index b9bbab7087..af679f9974 100644
--- a/traffic_ops/testing/api/v5/origins_test.go
+++ b/traffic_ops/testing/api/v5/origins_test.go
@@ -40,7 +40,7 @@ func TestOrigins(t *testing.T) {
tenant4UserSession := utils.CreateV5Session(t,
Config.TrafficOps.URL, "tenant4user", "pa$$word",
Config.Default.Session.TimeoutInSecs)
- methodTests := utils.TestCase[client.Session,
client.RequestOptions, tc.Origin]{
+ methodTests := utils.TestCase[client.Session,
client.RequestOptions, tc.OriginV5]{
"GET": {
"OK when VALID request": {
ClientSession: TOSession,
@@ -162,113 +162,113 @@ func TestOrigins(t *testing.T) {
"POST": {
"BAD REQUEST when ALREADY EXISTS": {
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("origin1"),
+ RequestBody: tc.OriginV5{
+ Name: "origin1",
Cachegroup:
util.Ptr("originCachegroup"),
Coordinate:
util.Ptr("coordinate1"),
- DeliveryService:
util.Ptr("ds1"),
- FQDN:
util.Ptr("origin1.example.com"),
+ DeliveryService: "ds1",
+ FQDN:
"origin1.example.com",
IPAddress:
util.Ptr("1.2.3.4"),
IP6Address:
util.Ptr("dead:beef:cafe::42"),
Port: util.Ptr(1234),
Profile:
util.Ptr("ATS_EDGE_TIER_CACHE"),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant1")()),
- IsPrimary: util.Ptr(true),
+ Protocol: "http",
+ TenantID: GetTenantID(t,
"tenant1")(),
+ IsPrimary: true,
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
},
"FORBIDDEN when CHILD TENANT CREATES ORIGIN
OUTSIDE TENANCY": {
ClientSession: tenant4UserSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("originTenancyTest"),
+ RequestBody: tc.OriginV5{
+ Name:
"originTenancyTest",
Cachegroup:
util.Ptr("originCachegroup"),
- DeliveryServiceID:
util.Ptr(GetDeliveryServiceId(t, "ds1")()),
- FQDN:
util.Ptr("origintenancy.example.com"),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant3")()),
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds1")(),
+ FQDN:
"origintenancy.example.com",
+ Protocol: "http",
+ TenantID:
GetTenantID(t, "tenant3")(),
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusForbidden)),
},
"NOT FOUND when CACHEGROUP DOESNT EXIST": {
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("testcg"),
+ RequestBody: tc.OriginV5{
+ Name: "testcg",
CachegroupID:
util.Ptr(10000000),
- DeliveryServiceID:
util.Ptr(GetDeliveryServiceId(t, "ds1")()),
- FQDN:
util.Ptr("test.cachegroupId.com"),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant1")()),
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds1")(),
+ FQDN:
"test.cachegroupId.com",
+ Protocol: "http",
+ TenantID:
GetTenantID(t, "tenant1")(),
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusNotFound)),
},
"NOT FOUND when PROFILEID DOESNT EXIST": {
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("testprofile"),
- DeliveryServiceID:
util.Ptr(GetDeliveryServiceId(t, "ds1")()),
- FQDN:
util.Ptr("test.profileId.com"),
+ RequestBody: tc.OriginV5{
+ Name:
"testprofile",
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds1")(),
+ FQDN:
"test.profileId.com",
ProfileID:
util.Ptr(1000000),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant1")()),
+ Protocol: "http",
+ TenantID:
GetTenantID(t, "tenant1")(),
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusNotFound)),
},
"NOT FOUND when COORDINATE DOESNT EXIST": {
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("testcoordinate"),
+ RequestBody: tc.OriginV5{
+ Name:
"testcoordinate",
CoordinateID:
util.Ptr(10000000),
- DeliveryServiceID:
util.Ptr(GetDeliveryServiceId(t, "ds1")()),
- FQDN:
util.Ptr("test.coordinate.com"),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant1")()),
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds1")(),
+ FQDN:
"test.coordinate.com",
+ Protocol: "http",
+ TenantID:
GetTenantID(t, "tenant1")(),
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusNotFound)),
},
"FORBIDDEN when INVALID TENANT": {
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("testtenant"),
- DeliveryServiceID:
util.Ptr(GetDeliveryServiceId(t, "ds1")()),
- FQDN:
util.Ptr("test.tenant.com"),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(11111111),
+ RequestBody: tc.OriginV5{
+ Name: "testtenant",
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds1")(),
+ FQDN:
"test.tenant.com",
+ Protocol: "http",
+ TenantID: 11111111,
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusForbidden)),
},
"BAD REQUEST when INVALID PROTOCOL": {
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("testprotocol"),
- DeliveryServiceID:
util.Ptr(GetDeliveryServiceId(t, "ds1")()),
- FQDN:
util.Ptr("test.protocol.com"),
- Protocol:
util.Ptr("httttpppss"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant1")()),
+ RequestBody: tc.OriginV5{
+ Name:
"testprotocol",
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds1")(),
+ FQDN:
"test.protocol.com",
+ Protocol: "httttpppss",
+ TenantID:
GetTenantID(t, "tenant1")(),
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
},
"BAD REQUEST when INVALID IPV4 ADDRESS": {
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("testip"),
- DeliveryServiceID:
util.Ptr(GetDeliveryServiceId(t, "ds1")()),
- FQDN:
util.Ptr("test.ip.com"),
+ RequestBody: tc.OriginV5{
+ Name: "testip",
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds1")(),
+ FQDN:
"test.ip.com",
IPAddress:
util.Ptr("311.255.323.412"),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant1")()),
+ Protocol: "http",
+ TenantID:
GetTenantID(t, "tenant1")(),
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
},
"BAD REQUEST when INVALID IPV6 ADDRESS": {
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("testipv6"),
- DeliveryServiceID:
util.Ptr(GetDeliveryServiceId(t, "ds1")()),
- FQDN:
util.Ptr("origin1.example.com"),
+ RequestBody: tc.OriginV5{
+ Name: "testipv6",
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds1")(),
+ FQDN:
"origin1.example.com",
IP6Address:
util.Ptr("badipv6::addresss"),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant1")()),
+ Protocol: "http",
+ TenantID:
GetTenantID(t, "tenant1")(),
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
},
@@ -277,17 +277,17 @@ func TestOrigins(t *testing.T) {
"OK when VALID request": {
EndpointID: GetOriginID(t,
"origin2"),
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("origin2"),
+ RequestBody: tc.OriginV5{
+ Name: "origin2",
Cachegroup:
util.Ptr("multiOriginCachegroup"),
Coordinate:
util.Ptr("coordinate2"),
- DeliveryService:
util.Ptr("ds3"),
- FQDN:
util.Ptr("originupdated.example.com"),
+ DeliveryService: "ds3",
+ FQDN:
"originupdated.example.com",
IPAddress:
util.Ptr("1.2.3.4"),
IP6Address:
util.Ptr("0000::1111"),
Port: util.Ptr(1234),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant2")()),
+ Protocol: "http",
+ TenantID: GetTenantID(t,
"tenant2")(),
},
Expectations:
utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK),
validateOriginsUpdateCreateFields("origin2",
map[string]interface{}{"Cachegroup": "multiOriginCachegroup", "Coordinate":
"coordinate2", "DeliveryService": "ds3",
@@ -296,145 +296,145 @@ func TestOrigins(t *testing.T) {
"FORBIDDEN when CHILD TENANT updates PARENT
TENANT ORIGIN": {
EndpointID: GetOriginID(t,
"origin2"),
ClientSession: tenant4UserSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("testtenancy"),
- DeliveryServiceID:
util.Ptr(GetDeliveryServiceId(t, "ds1")()),
- FQDN:
util.Ptr("testtenancy.example.com"),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant1")()),
+ RequestBody: tc.OriginV5{
+ Name:
"testtenancy",
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds1")(),
+ FQDN:
"testtenancy.example.com",
+ Protocol: "http",
+ TenantID:
GetTenantID(t, "tenant1")(),
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusForbidden)),
},
"NOT FOUND when ORIGIN DOESNT EXIST": {
EndpointID: func() int { return
1111111 },
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("testid"),
- DeliveryServiceID:
util.Ptr(GetDeliveryServiceId(t, "ds1")()),
- FQDN:
util.Ptr("testid.example.com"),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant1")()),
+ RequestBody: tc.OriginV5{
+ Name: "testid",
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds1")(),
+ FQDN:
"testid.example.com",
+ Protocol: "http",
+ TenantID:
GetTenantID(t, "tenant1")(),
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusNotFound)),
},
"BAD REQUEST when DELIVERY SERVICE DOESNT
EXIST": {
EndpointID: GetOriginID(t,
"origin2"),
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("origin2"),
- DeliveryServiceID:
util.Ptr(11111111),
- FQDN:
util.Ptr("origin2.example.com"),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant1")()),
+ RequestBody: tc.OriginV5{
+ Name: "origin2",
+ DeliveryServiceID: 11111111,
+ FQDN:
"origin2.example.com",
+ Protocol: "http",
+ TenantID:
GetTenantID(t, "tenant1")(),
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
},
"NOT FOUND when CACHEGROUP DOESNT EXIST": {
EndpointID: GetOriginID(t,
"origin2"),
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("origin2"),
+ RequestBody: tc.OriginV5{
+ Name: "origin2",
CachegroupID:
util.Ptr(1111111),
- DeliveryServiceID:
util.Ptr(GetDeliveryServiceId(t, "ds1")()),
- FQDN:
util.Ptr("origin2.example.com"),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant1")()),
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds1")(),
+ FQDN:
"origin2.example.com",
+ Protocol: "http",
+ TenantID:
GetTenantID(t, "tenant1")(),
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusNotFound)),
},
"NOT FOUND when PROFILEID DOESNT EXIST": {
EndpointID: GetOriginID(t,
"origin2"),
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("origin2"),
+ RequestBody: tc.OriginV5{
+ Name:
*util.Ptr("origin2"),
Cachegroup:
util.Ptr("originCachegroup"),
- DeliveryServiceID:
util.Ptr(GetDeliveryServiceId(t, "ds1")()),
- FQDN:
util.Ptr("origin2.example.com"),
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds1")(),
+ FQDN:
"origin2.example.com",
ProfileID:
util.Ptr(11111111),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant1")()),
+ Protocol: "http",
+ TenantID:
GetTenantID(t, "tenant1")(),
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusNotFound)),
},
"NOT FOUND when COORDINATE DOESNT EXIST": {
EndpointID: GetOriginID(t,
"origin2"),
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("origin2"),
+ RequestBody: tc.OriginV5{
+ Name: "origin2",
Cachegroup:
util.Ptr("originCachegroup"),
CoordinateID:
util.Ptr(1111111),
- DeliveryServiceID:
util.Ptr(GetDeliveryServiceId(t, "ds1")()),
- FQDN:
util.Ptr("origin2.example.com"),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant1")()),
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds1")(),
+ FQDN:
"origin2.example.com",
+ Protocol: "http",
+ TenantID:
GetTenantID(t, "tenant1")(),
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusNotFound)),
},
"FORBIDDEN when INVALID TENANT": {
EndpointID: GetOriginID(t,
"origin2"),
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("origin1"),
+ RequestBody: tc.OriginV5{
+ Name: "origin1",
Cachegroup:
util.Ptr("originCachegroup"),
- DeliveryServiceID:
util.Ptr(GetDeliveryServiceId(t, "ds1")()),
- FQDN:
util.Ptr("origin1.example.com"),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(1111111),
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds1")(),
+ FQDN:
"origin1.example.com",
+ Protocol: "http",
+ TenantID: 1111111,
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusForbidden)),
},
"BAD REQUEST when INVALID PROTOCOL": {
EndpointID: GetOriginID(t,
"origin2"),
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("origin2"),
+ RequestBody: tc.OriginV5{
+ Name: "origin2",
Cachegroup:
util.Ptr("originCachegroup"),
- DeliveryServiceID:
util.Ptr(GetDeliveryServiceId(t, "ds1")()),
- FQDN:
util.Ptr("origin2.example.com"),
- Protocol:
util.Ptr("htttttpssss"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant1")()),
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds1")(),
+ FQDN:
"origin2.example.com",
+ Protocol:
"htttttpssss",
+ TenantID:
GetTenantID(t, "tenant1")(),
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
},
"BAD REQUEST when INVALID IPV4 ADDRESS": {
EndpointID: GetOriginID(t,
"origin2"),
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("origin2"),
+ RequestBody: tc.OriginV5{
+ Name: "origin2",
Cachegroup:
util.Ptr("originCachegroup"),
- DeliveryServiceID:
util.Ptr(GetDeliveryServiceId(t, "ds2")()),
- FQDN:
util.Ptr("origin2.example.com"),
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds2")(),
+ FQDN:
"origin2.example.com",
IPAddress:
util.Ptr("300.254.123.1"),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant1")()),
+ Protocol: "http",
+ TenantID:
GetTenantID(t, "tenant1")(),
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
},
"BAD REQUEST when INVALID IPV6 ADDRESS": {
EndpointID: GetOriginID(t,
"origin2"),
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("origin2"),
+ RequestBody: tc.OriginV5{
+ Name: "origin2",
Cachegroup:
util.Ptr("originCachegroup"),
- DeliveryServiceID:
util.Ptr(GetDeliveryServiceId(t, "ds2")()),
- FQDN:
util.Ptr("origin2.example.com"),
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds2")(),
+ FQDN:
"origin2.example.com",
IP6Address:
util.Ptr("test::42"),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant1")()),
+ Protocol: "http",
+ TenantID:
GetTenantID(t, "tenant1")(),
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
},
"BAD REQUEST when INVALID PORT": {
EndpointID: GetOriginID(t,
"origin2"),
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("origin2"),
+ RequestBody: tc.OriginV5{
+ Name: "origin2",
Cachegroup:
util.Ptr("originCachegroup"),
- DeliveryServiceID:
util.Ptr(GetDeliveryServiceId(t, "ds2")()),
- FQDN:
util.Ptr("origin2.example.com"),
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds2")(),
+ FQDN:
"origin2.example.com",
Port:
util.Ptr(80000),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant1")()),
+ Protocol: "http",
+ TenantID:
GetTenantID(t, "tenant1")(),
},
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
},
@@ -442,26 +442,28 @@ func TestOrigins(t *testing.T) {
EndpointID: GetOriginID(t,
"origin2"),
ClientSession: TOSession,
RequestOpts:
client.RequestOptions{Header: http.Header{rfc.IfUnmodifiedSince:
{currentTimeRFC}}},
- RequestBody: tc.Origin{
- Name:
util.Ptr("origin2"),
- Cachegroup:
util.Ptr("originCachegroup"),
- DeliveryService:
util.Ptr("ds2"),
- FQDN:
util.Ptr("origin2.example.com"),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant1")()),
+ RequestBody: tc.OriginV5{
+ Name: "origin2",
+ Cachegroup:
util.Ptr("originCachegroup"),
+ DeliveryService: "ds2",
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds2")(),
+ FQDN:
"origin2.example.com",
+ Protocol: "http",
+ TenantID:
GetTenantID(t, "tenant1")(),
},
Expectations:
utils.CkRequest(utils.HasError(),
utils.HasStatus(http.StatusPreconditionFailed)),
},
"PRECONDITION FAILED when updating with IFMATCH
ETAG Header": {
EndpointID: GetOriginID(t,
"origin2"),
ClientSession: TOSession,
- RequestBody: tc.Origin{
- Name:
util.Ptr("origin2"),
- Cachegroup:
util.Ptr("originCachegroup"),
- DeliveryService:
util.Ptr("ds2"),
- FQDN:
util.Ptr("origin2.example.com"),
- Protocol:
util.Ptr("http"),
- TenantID:
util.Ptr(GetTenantID(t, "tenant1")()),
+ RequestBody: tc.OriginV5{
+ Name: "origin2",
+ Cachegroup:
util.Ptr("originCachegroup"),
+ DeliveryService: "ds2",
+ DeliveryServiceID:
GetDeliveryServiceId(t, "ds2")(),
+ FQDN:
"origin2.example.com",
+ Protocol: "http",
+ TenantID:
GetTenantID(t, "tenant1")(),
},
RequestOpts:
client.RequestOptions{Header: http.Header{rfc.IfMatch:
{rfc.ETag(currentTime)}}},
Expectations:
utils.CkRequest(utils.HasError(),
utils.HasStatus(http.StatusPreconditionFailed)),
@@ -523,7 +525,7 @@ func TestOrigins(t *testing.T) {
func validateOriginsFields(expectedResp map[string]interface{})
utils.CkReqFunc {
return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _
tc.Alerts, _ error) {
assert.RequireNotNil(t, resp, "Expected Origin response to not
be nil.")
- originResp := resp.([]tc.Origin)
+ originResp := resp.([]tc.OriginV5)
for field, expected := range expectedResp {
for _, origin := range originResp {
switch field {
@@ -541,16 +543,16 @@ func validateOriginsFields(expectedResp
map[string]interface{}) utils.CkReqFunc
assert.Equal(t, expected,
*origin.CoordinateID, "Expected CoordinateID to be %v, but got %d", expected,
*origin.CoordinateID)
case "DeliveryService":
assert.RequireNotNil(t,
origin.DeliveryService, "Expected DeliveryService to not be nil.")
- assert.Equal(t, expected,
*origin.DeliveryService, "Expected DeliveryService to be %v, but got %s",
expected, *origin.DeliveryService)
+ assert.Equal(t, expected,
origin.DeliveryService, "Expected DeliveryService to be %v, but got %s",
expected, origin.DeliveryService)
case "DeliveryServiceID":
assert.RequireNotNil(t,
origin.DeliveryServiceID, "Expected DeliveryServiceID to not be nil.")
- assert.Equal(t, expected,
*origin.DeliveryServiceID, "Expected DeliveryServiceID to be %v, but got %d",
expected, *origin.DeliveryServiceID)
+ assert.Equal(t, expected,
origin.DeliveryServiceID, "Expected DeliveryServiceID to be %v, but got %d",
expected, origin.DeliveryServiceID)
case "FQDN":
assert.RequireNotNil(t, origin.FQDN,
"Expected FQDN to not be nil.")
- assert.Equal(t, expected, *origin.FQDN,
"Expected FQDN to be %v, but got %s", expected, *origin.FQDN)
+ assert.Equal(t, expected, origin.FQDN,
"Expected FQDN to be %v, but got %s", expected, origin.FQDN)
case "ID":
assert.RequireNotNil(t, origin.ID,
"Expected ID to not be nil.")
- assert.Equal(t, expected, *origin.ID,
"Expected ID to be %v, but got %d", expected, *origin.ID)
+ assert.Equal(t, expected, origin.ID,
"Expected ID to be %v, but got %d", expected, origin.ID)
case "IPAddress":
assert.RequireNotNil(t,
origin.IPAddress, "Expected IPAddress to not be nil.")
assert.Equal(t, expected,
*origin.IPAddress, "Expected IPAddress to be %v, but got %s", expected,
*origin.IPAddress)
@@ -559,10 +561,10 @@ func validateOriginsFields(expectedResp
map[string]interface{}) utils.CkReqFunc
assert.Equal(t, expected,
*origin.IP6Address, "Expected IP6Address to be %v, but got %s", expected,
*origin.IP6Address)
case "IsPrimary":
assert.RequireNotNil(t,
origin.IsPrimary, "Expected IsPrimary to not be nil.")
- assert.Equal(t, expected,
*origin.IsPrimary, "Expected IsPrimary to be %v, but got %v", expected,
*origin.IsPrimary)
+ assert.Equal(t, expected,
origin.IsPrimary, "Expected IsPrimary to be %v, but got %v", expected,
origin.IsPrimary)
case "Name":
assert.RequireNotNil(t, origin.Name,
"Expected Name to not be nil.")
- assert.Equal(t, expected, *origin.Name,
"Expected Name to be %v, but got %s", expected, *origin.Name)
+ assert.Equal(t, expected, origin.Name,
"Expected Name to be %v, but got %s", expected, origin.Name)
case "Port":
assert.RequireNotNil(t, origin.Port,
"Expected Port to not be nil.")
assert.Equal(t, expected, *origin.Port,
"Expected Port to be %v, but got %d", expected, *origin.Port)
@@ -574,13 +576,13 @@ func validateOriginsFields(expectedResp
map[string]interface{}) utils.CkReqFunc
assert.Equal(t, expected,
*origin.ProfileID, "Expected ProfileID to be %v, but got %d", expected,
*origin.ProfileID)
case "Protocol":
assert.RequireNotNil(t,
origin.Protocol, "Expected Protocol to not be nil.")
- assert.Equal(t, expected,
*origin.Protocol, "Expected Tenant to be %v, but got %s", expected,
*origin.Protocol)
+ assert.Equal(t, expected,
origin.Protocol, "Expected Tenant to be %v, but got %s", expected,
origin.Protocol)
case "Tenant":
assert.RequireNotNil(t, origin.Tenant,
"Expected Tenant to not be nil.")
- assert.Equal(t, expected,
*origin.Tenant, "Expected Tenant to be %v, but got %s", expected,
*origin.Tenant)
+ assert.Equal(t, expected,
origin.Tenant, "Expected Tenant to be %v, but got %s", expected, origin.Tenant)
case "TenantID":
assert.RequireNotNil(t,
origin.TenantID, "Expected TenantID to not be nil.")
- assert.Equal(t, expected,
*origin.TenantID, "Expected TenantID to be %v, but got %d", expected,
*origin.TenantID)
+ assert.Equal(t, expected,
origin.TenantID, "Expected TenantID to be %v, but got %d", expected,
origin.TenantID)
default:
t.Errorf("Expected field: %v, does not
exist in response", field)
}
@@ -602,7 +604,7 @@ func validateOriginsUpdateCreateFields(name string,
expectedResp map[string]inte
func validateOriginsPagination(paginationParam string) utils.CkReqFunc {
return func(t *testing.T, _ toclientlib.ReqInf, resp interface{}, _
tc.Alerts, _ error) {
- paginationResp := resp.([]tc.Origin)
+ paginationResp := resp.([]tc.OriginV5)
opts := client.NewRequestOptions()
opts.QueryParameters.Set("orderby", "id")
@@ -630,7 +632,7 @@ func GetOriginID(t *testing.T, name string) func() int {
assert.RequireNoError(t, err, "Get Origins Request failed with
error:", err)
assert.RequireEqual(t, 1, len(origins.Response), "Expected
response object length 1, but got %d", len(origins.Response))
assert.RequireNotNil(t, origins.Response[0].ID, "Expected ID to
not be nil.")
- return *origins.Response[0].ID
+ return origins.Response[0].ID
}
}
@@ -649,15 +651,15 @@ func DeleteTestOrigins(t *testing.T) {
assert.RequireNotNil(t, origin.ID, "Expected origin ID to not
be nil.")
assert.RequireNotNil(t, origin.Name, "Expected origin ID to not
be nil.")
assert.RequireNotNil(t, origin.IsPrimary, "Expected origin ID
to not be nil.")
- if !*origin.IsPrimary {
- alerts, _, err := TOSession.DeleteOrigin(*origin.ID,
client.RequestOptions{})
- assert.NoError(t, err, "Unexpected error deleting
Origin '%s' (#%d): %v - alerts: %+v", *origin.Name, *origin.ID, err,
alerts.Alerts)
+ if !origin.IsPrimary {
+ alerts, _, err := TOSession.DeleteOrigin(origin.ID,
client.RequestOptions{})
+ assert.NoError(t, err, "Unexpected error deleting
Origin '%s' (#%d): %v - alerts: %+v", origin.Name, origin.ID, err,
alerts.Alerts)
// Retrieve the Origin to see if it got deleted
opts := client.NewRequestOptions()
- opts.QueryParameters.Set("id", strconv.Itoa(*origin.ID))
+ opts.QueryParameters.Set("id", strconv.Itoa(origin.ID))
getOrigin, _, err := TOSession.GetOrigins(opts)
- assert.NoError(t, err, "Error getting Origin '%s' after
deletion: %v - alerts: %+v", *origin.Name, err, getOrigin.Alerts)
- assert.Equal(t, 0, len(getOrigin.Response), "Expected
Origin '%s' to be deleted, but it was found in Traffic Ops", *origin.Name)
+ assert.NoError(t, err, "Error getting Origin '%s' after
deletion: %v - alerts: %+v", origin.Name, err, getOrigin.Alerts)
+ assert.Equal(t, 0, len(getOrigin.Response), "Expected
Origin '%s' to be deleted, but it was found in Traffic Ops", origin.Name)
}
}
}
diff --git a/traffic_ops/testing/api/v5/traffic_control_test.go
b/traffic_ops/testing/api/v5/traffic_control_test.go
index 7d9547fa38..ae2a4452ba 100644
--- a/traffic_ops/testing/api/v5/traffic_control_test.go
+++ b/traffic_ops/testing/api/v5/traffic_control_test.go
@@ -38,7 +38,7 @@ type TrafficControl struct {
Federations []tc.CDNFederation
`json:"federations"`
FederationResolvers
[]tc.FederationResolverV5 `json:"federation_resolvers"`
Jobs
[]tc.InvalidationJobCreateV4 `json:"jobs"`
- Origins []tc.Origin
`json:"origins"`
+ Origins []tc.OriginV5
`json:"origins"`
Profiles []tc.ProfileV5
`json:"profiles"`
Parameters []tc.ParameterV5
`json:"parameters"`
ProfileParameters
[]tc.ProfileParameterV5 `json:"profileParameters"`
diff --git a/traffic_ops/traffic_ops_golang/origin/origins.go
b/traffic_ops/traffic_ops_golang/origin/origins.go
index ff34ca8705..f5a807ea91 100644
--- a/traffic_ops/traffic_ops_golang/origin/origins.go
+++ b/traffic_ops/traffic_ops_golang/origin/origins.go
@@ -21,15 +21,15 @@ package origin
import (
"database/sql"
+ "encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"time"
-
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/util/ims"
-
"github.com/apache/trafficcontrol/lib/go-log"
+ "github.com/apache/trafficcontrol/lib/go-rfc"
"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/lib/go-tc/tovalidate"
"github.com/apache/trafficcontrol/lib/go-util"
@@ -37,6 +37,7 @@ import (
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/auth"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/dbhelpers"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tenant"
+
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/util/ims"
validation "github.com/go-ozzo/ozzo-validation"
"github.com/go-ozzo/ozzo-validation/is"
@@ -487,3 +488,292 @@ func deleteQuery() string {
WHERE id=:id`
return query
}
+
+// Get is the handler for GET requests to Origins of APIv5.
+func Get(w http.ResponseWriter, r *http.Request) {
+
+ var useIMS bool
+
+ inf, sysErr, userErr, errCode := api.NewInfo(r, nil, nil)
+ if sysErr != nil || userErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+ return
+ }
+ defer inf.Close()
+
+ origins, userErr, sysErr, errCode, _ := getOrigins(w.Header(),
inf.Params, inf.Tx, inf.User, useIMS)
+
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+ return
+ }
+
+ returnable := make([]tc.OriginV5, len(origins))
+ for i, origin := range origins {
+ returnable[i] = origin.ToOriginV5()
+ }
+
+ api.WriteResp(w, r, returnable)
+ return
+}
+
+// Create Origin with the passed data for APIv5.
+func Create(w http.ResponseWriter, r *http.Request) {
+ inf, sysError, userError, errorCode := api.NewInfo(r, nil, nil)
+ tx := inf.Tx
+ if sysError != nil || userError != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, errorCode, userError, sysError)
+ return
+ }
+ defer inf.Close()
+
+ org, errorCode, readValErr := readAndValidateJsonStruct(r, tx)
+ if readValErr != nil {
+ api.HandleErr(w, r, tx.Tx, errorCode, readValErr, nil)
+ return
+ }
+
+ userErr, sysErr, errCode := checkTenancy(&org.TenantID,
&org.DeliveryServiceID, tx, inf.User)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx.Tx, errCode, userErr, sysErr)
+ return
+ }
+
+ _, cdnName, _, err := dbhelpers.GetDSNameAndCDNFromID(inf.Tx.Tx,
org.DeliveryServiceID)
+ if err != nil {
+ api.HandleErr(w, r, tx.Tx, http.StatusInternalServerError, nil,
fmt.Errorf("database error: unable to retrieve delivery service name and cdn:
%w", err))
+ return
+ }
+ userErr, sysErr, errCode =
dbhelpers.CheckIfCurrentUserCanModifyCDN(tx.Tx, string(cdnName),
inf.User.UserName)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+ return
+ }
+
+ resultRows, err := tx.NamedQuery(insertQuery(), org)
+ if err != nil {
+ usrErr, sysErr, code := api.ParseDBError(err)
+ api.HandleErr(w, r, tx.Tx, code, usrErr, sysErr)
+ return
+ }
+ defer resultRows.Close()
+
+ rowsAffected := 0
+ for resultRows.Next() {
+ rowsAffected++
+ if err := resultRows.Scan(&org.ID, &org.LastUpdated); err !=
nil {
+ api.HandleErr(w, r, tx.Tx,
http.StatusInternalServerError, fmt.Errorf("origin create: scanning: %w", err),
nil)
+ return
+ }
+ }
+
+ if rowsAffected == 0 {
+ api.HandleErr(w, r, tx.Tx, http.StatusInternalServerError,
fmt.Errorf("origin create: no rows inserted"), nil)
+ return
+ } else if rowsAffected > 1 {
+ api.HandleErr(w, r, tx.Tx, http.StatusInternalServerError,
fmt.Errorf("origin create: multiple rows returned"), nil)
+ return
+ }
+
+ alerts := tc.CreateAlerts(tc.SuccessLevel, "origin was created.")
+ w.Header().Set(rfc.Location, fmt.Sprintf("/api/%s/origins?id=%d",
inf.Version, org.ID))
+ api.WriteAlertsObj(w, r, http.StatusCreated, alerts, org)
+}
+
+// Update a Origin for APIv5.
+func Update(w http.ResponseWriter, r *http.Request) {
+ inf, sysError, userError, errorCode := api.NewInfo(r, []string{"id"},
[]string{"id"})
+ tx := inf.Tx
+ if sysError != nil || userError != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, errorCode, userError, sysError)
+ return
+ }
+ defer inf.Close()
+
+ requestedOriginId := inf.IntParams["id"]
+
+ origin, errorCode, readValErr := readAndValidateJsonStruct(r, tx)
+ if readValErr != nil {
+ api.HandleErr(w, r, tx.Tx, errorCode, readValErr, nil)
+ return
+ }
+
+ userErr, sysErr, errCode := checkTenancy(&origin.TenantID,
&origin.DeliveryServiceID, tx, inf.User)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx.Tx, errCode, userErr, sysErr)
+ return
+ }
+
+ isPrimary := false
+ ds := 0
+ var existingLastUpdated time.Time
+
+ q := `SELECT is_primary, deliveryservice, last_updated FROM origin
WHERE id = $1`
+ errLookup := tx.QueryRow(q, requestedOriginId).Scan(&isPrimary, &ds,
&existingLastUpdated)
+ if errLookup != nil {
+ if errors.Is(errLookup, sql.ErrNoRows) {
+ api.HandleErr(w, r, tx.Tx, http.StatusNotFound,
fmt.Errorf("no origin exists by id: %d", requestedOriginId), nil)
+ return
+ }
+ api.HandleErr(w, r, tx.Tx, http.StatusInternalServerError, nil,
fmt.Errorf("database error: %w, when checking if origin with id %d exists",
errLookup, requestedOriginId))
+ return
+ }
+ // check if the entity was already updated
+ userErr, sysErr, errCode = api.CheckIfUnModified(r.Header, inf.Tx,
requestedOriginId, "origin")
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx.Tx, errCode, userErr, sysErr)
+ return
+ }
+
+ if isPrimary && origin.DeliveryServiceID != ds {
+ api.HandleErr(w, r, tx.Tx, http.StatusBadRequest,
fmt.Errorf("cannot update the delivery service of a primary origin"), nil)
+ return
+ }
+
+ _, cdnName, _, err := dbhelpers.GetDSNameAndCDNFromID(tx.Tx,
origin.DeliveryServiceID)
+ if err != nil {
+ api.HandleErr(w, r, tx.Tx, http.StatusInternalServerError, err,
nil)
+ return
+ }
+ userErr, sysErr, errCode =
dbhelpers.CheckIfCurrentUserCanModifyCDN(tx.Tx, string(cdnName),
inf.User.UserName)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx.Tx, errCode, userErr, sysErr)
+ return
+ }
+
+ query := `UPDATE origin SET
+ cachegroup=$1,
+ coordinate=$2,
+ deliveryservice=$3,
+ fqdn=$4,
+ ip6_address=$5,
+ ip_address=$6,
+ name=$7,
+ port=$8,
+ profile=$9,
+ protocol=$10,
+ tenant=$11
+ WHERE id=$12 RETURNING id,last_updated`
+ errUpdate := tx.QueryRow(query, origin.CachegroupID,
origin.CoordinateID, origin.DeliveryServiceID,
+ origin.FQDN, origin.IP6Address, origin.IPAddress, origin.Name,
+ origin.Port, origin.ProfileID, origin.Protocol,
origin.TenantID, requestedOriginId).Scan(&origin.ID, &origin.LastUpdated)
+ if errUpdate != nil {
+ if errors.Is(errUpdate, sql.ErrNoRows) {
+ api.HandleErr(w, r, tx.Tx, http.StatusNotFound,
fmt.Errorf("origin: %d not found", requestedOriginId), nil)
+ return
+ }
+ usrErr, sysErr, code := api.ParseDBError(errUpdate)
+ api.HandleErr(w, r, tx.Tx, code, usrErr, sysErr)
+ return
+ }
+
+ origin.ID = requestedOriginId
+ alerts := tc.CreateAlerts(tc.SuccessLevel, "origin was updated.")
+ api.WriteAlertsObj(w, r, http.StatusOK, alerts, origin)
+ return
+}
+
+// Delete an Origin for APIv5.
+func Delete(w http.ResponseWriter, r *http.Request) {
+ inf, userErr, sysErr, errCode := api.NewInfo(r, []string{"id"},
[]string{"id"})
+ tx := inf.Tx.Tx
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+ return
+ }
+ defer inf.Close()
+
+ id := inf.IntParams["id"]
+
+ var origin tc.OriginV5
+ if err := tx.QueryRow(`SELECT is_primary, deliveryservice, tenant FROM
origin WHERE id = $1`, id).Scan(&origin.IsPrimary, &origin.DeliveryServiceID,
&origin.TenantID); err != nil {
+ if errors.Is(err, sql.ErrNoRows) {
+ api.HandleErr(w, r, tx, http.StatusNotFound,
fmt.Errorf("no origin exists by id: %d", id), nil)
+ return
+ }
+ api.HandleErr(w, r, tx, http.StatusInternalServerError,
fmt.Errorf("origin delete: is_primary scanning: %w", err), nil)
+ return
+ }
+
+ if origin.IsPrimary {
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
fmt.Errorf("cannot delete a primary origin"), nil)
+ return
+ }
+
+ if &origin.DeliveryServiceID != nil {
+ _, cdnName, _, err := dbhelpers.GetDSNameAndCDNFromID(tx,
origin.DeliveryServiceID)
+ if err != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError,
err, nil)
+ return
+ }
+
+ userErr, sysErr, errCode :=
dbhelpers.CheckIfCurrentUserCanModifyCDN(tx, string(cdnName), inf.User.UserName)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+ return
+ }
+ }
+
+ userErr, sysErr, errCode = checkTenancy(&origin.TenantID,
&origin.DeliveryServiceID, inf.Tx, inf.User)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+ return
+ }
+
+ res, err := tx.Exec("DELETE FROM origin WHERE id=$1", id)
+ if err != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError, nil,
err)
+ return
+ }
+ rowsAffected, err := res.RowsAffected()
+ if err != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError, nil,
fmt.Errorf("origin delete: getting rows affected: %w", err))
+ return
+ }
+ if rowsAffected == 0 {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError,
fmt.Errorf("no rows deleted for origin"), nil)
+ return
+ }
+ if rowsAffected != 1 {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError,
fmt.Errorf("origin delete: multiple rows affected"), nil)
+ return
+ }
+
+ alerts := tc.CreateAlerts(tc.SuccessLevel, "origin was deleted.")
+ api.WriteAlerts(w, r, http.StatusOK, alerts)
+ return
+}
+
+// readAndValidateJsonStruct reads json body and validates json fields.
+func readAndValidateJsonStruct(r *http.Request, tx *sqlx.Tx) (tc.OriginV5,
int, error) {
+ var origin tc.OriginV5
+ if err := json.NewDecoder(r.Body).Decode(&origin); err != nil {
+ userErr := fmt.Errorf("error decoding POST request body into
OriginV5 struct %w", err)
+ return origin, http.StatusBadRequest, userErr
+ }
+
+ noSpaces := validation.NewStringRule(tovalidate.NoSpaces, "cannot
contain spaces")
+ validProtocol :=
validation.NewStringRule(tovalidate.IsOneOfStringICase("http", "https"), "must
be http or https")
+ portErr := "must be a valid integer between 1 and 65535"
+
+ // validate JSON body
+ errs := tovalidate.ToErrors(validation.Errors{
+ "cachegroupId": validation.Validate(origin.CachegroupID,
validation.Min(1)),
+ "coordinateId": validation.Validate(origin.CoordinateID,
validation.Min(1)),
+ "deliveryServiceId":
validation.Validate(origin.DeliveryServiceID, validation.Required),
+ "fqdn": validation.Validate(origin.FQDN,
validation.Required, is.DNSName),
+ "ip6Address": validation.Validate(origin.IP6Address,
validation.NilOrNotEmpty, is.IPv6),
+ "ipAddress": validation.Validate(origin.IPAddress,
validation.NilOrNotEmpty, is.IPv4),
+ "name": validation.Validate(origin.Name,
validation.Required, noSpaces),
+ "port": validation.Validate(origin.Port,
validation.NilOrNotEmpty.Error(portErr), validation.Min(1).Error(portErr),
validation.Max(65535).Error(portErr)),
+ "profileId": validation.Validate(origin.ProfileID,
validation.Min(1)),
+ "protocol": validation.Validate(origin.Protocol,
validation.Required, validProtocol),
+ "tenantId": validation.Validate(origin.TenantID,
validation.Required, validation.Min(1)),
+ })
+ if len(errs) > 0 {
+ userErr := util.JoinErrs(errs)
+ return origin, http.StatusBadRequest, userErr
+ }
+
+ return origin, http.StatusBadRequest, nil
+}
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go
b/traffic_ops/traffic_ops_golang/routing/routes.go
index eec686a680..2aa1b328e1 100644
--- a/traffic_ops/traffic_ops_golang/routing/routes.go
+++ b/traffic_ops/traffic_ops_golang/routing/routes.go
@@ -403,10 +403,10 @@ func Routes(d ServerData) ([]Route, http.Handler, error) {
{Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodPost, Path: `cdns/{name}/dnsseckeys/ksk/generate$`, Handler:
cdn.GenerateKSK, RequiredPrivLevel: auth.PrivLevelAdmin, RequiredPermissions:
[]string{"DNS-SEC:CREATE", "CDN:UPDATE", "CDN:READ"}, Authenticated:
Authenticated, Middlewares: nil, ID: 47292428131},
//Origins
- {Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodGet, Path: `origins/?$`, Handler:
api.ReadHandler(&origin.TOOrigin{}), RequiredPrivLevel: auth.PrivLevelReadOnly,
RequiredPermissions: []string{"ORIGIN:READ", "DELIVERY-SERVICE:READ"},
Authenticated: Authenticated, Middlewares: nil, ID: 44464925631},
- {Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodPut, Path: `origins/?$`, Handler:
api.UpdateHandler(&origin.TOOrigin{}), RequiredPrivLevel:
auth.PrivLevelOperations, RequiredPermissions: []string{"ORIGIN:UPDATE",
"ORIGIN:READ", "DELIVERY-SERVICE:READ", "DELIVERY-SERVICE:UPDATE"},
Authenticated: Authenticated, Middlewares: nil, ID: 4156774631},
- {Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodPost, Path: `origins/?$`, Handler:
api.CreateHandler(&origin.TOOrigin{}), RequiredPrivLevel:
auth.PrivLevelOperations, RequiredPermissions: []string{"ORIGIN:CREATE",
"ORIGIN:READ", "DELIVERY-SERVICE:READ", "DELIVERY-SERVICE:UPDATE"},
Authenticated: Authenticated, Middlewares: nil, ID: 409956164331},
- {Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodDelete, Path: `origins/?$`, Handler:
api.DeleteHandler(&origin.TOOrigin{}), RequiredPrivLevel:
auth.PrivLevelOperations, RequiredPermissions: []string{"ORIGIN:DELETE",
"DELIVERY-SERVICE:UPDATE"}, Authenticated: Authenticated, Middlewares: nil, ID:
46027326331},
+ {Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodGet, Path: `origins/?$`, Handler: origin.Get, RequiredPrivLevel:
auth.PrivLevelReadOnly, RequiredPermissions: []string{"ORIGIN:READ",
"DELIVERY-SERVICE:READ"}, Authenticated: Authenticated, Middlewares: nil, ID:
44464925631},
+ {Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodPut, Path: `origins/?$`, Handler: origin.Update, RequiredPrivLevel:
auth.PrivLevelOperations, RequiredPermissions: []string{"ORIGIN:UPDATE",
"ORIGIN:READ", "DELIVERY-SERVICE:READ", "DELIVERY-SERVICE:UPDATE"},
Authenticated: Authenticated, Middlewares: nil, ID: 4156774631},
+ {Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodPost, Path: `origins/?$`, Handler: origin.Create, RequiredPrivLevel:
auth.PrivLevelOperations, RequiredPermissions: []string{"ORIGIN:CREATE",
"ORIGIN:READ", "DELIVERY-SERVICE:READ", "DELIVERY-SERVICE:UPDATE"},
Authenticated: Authenticated, Middlewares: nil, ID: 409956164331},
+ {Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodDelete, Path: `origins/?$`, Handler: origin.Delete,
RequiredPrivLevel: auth.PrivLevelOperations, RequiredPermissions:
[]string{"ORIGIN:DELETE", "DELIVERY-SERVICE:UPDATE"}, Authenticated:
Authenticated, Middlewares: nil, ID: 46027326331},
//Roles
{Version: api.Version{Major: 5, Minor: 0}, Method:
http.MethodGet, Path: `roles/?$`, Handler: role.Get, RequiredPrivLevel:
auth.PrivLevelReadOnly, RequiredPermissions: []string{"ROLE:READ"},
Authenticated: Authenticated, Middlewares: nil, ID: 48708858331},
diff --git a/traffic_ops/v5-client/origin.go b/traffic_ops/v5-client/origin.go
index 0964b43c15..48d2be2d50 100644
--- a/traffic_ops/v5-client/origin.go
+++ b/traffic_ops/v5-client/origin.go
@@ -29,7 +29,7 @@ import (
// apiOrigins is the full path to the /origins API route.
const apiOrigins = "/origins"
-func (to *Session) originIDs(origin *tc.Origin) error {
+func (to *Session) originIDs(origin *tc.OriginV5) error {
if origin == nil {
return errors.New("invalid call to originIDs; nil origin")
}
@@ -48,17 +48,17 @@ func (to *Session) originIDs(origin *tc.Origin) error {
origin.CachegroupID = p.Response[0].ID
}
- if origin.DeliveryServiceID == nil && origin.DeliveryService != nil {
- opts.QueryParameters.Set("xmlId", *origin.DeliveryService)
+ if origin.DeliveryServiceID == 0 && origin.DeliveryService != "" {
+ opts.QueryParameters.Set("xmlId", origin.DeliveryService)
dses, _, err := to.GetDeliveryServices(opts)
if err != nil {
- return fmt.Errorf("resolving Delivery Service XMLID
'%s' to an ID: %w - alerts: %+v", *origin.DeliveryService, err, dses.Alerts)
+ return fmt.Errorf("resolving Delivery Service XMLID
'%s' to an ID: %w - alerts: %+v", origin.DeliveryService, err, dses.Alerts)
}
if len(dses.Response) == 0 {
- return fmt.Errorf("no Delivery Service with XMLID
'%s'", *origin.DeliveryService)
+ return fmt.Errorf("no Delivery Service with XMLID
'%s'", origin.DeliveryService)
}
opts.QueryParameters.Del("xmlId")
- origin.DeliveryServiceID = dses.Response[0].ID
+ origin.DeliveryServiceID = *dses.Response[0].ID
}
if origin.ProfileID == nil && origin.Profile != nil {
@@ -85,24 +85,24 @@ func (to *Session) originIDs(origin *tc.Origin) error {
origin.CoordinateID = coordinates.Response[0].ID
}
- if origin.TenantID == nil && origin.Tenant != nil {
- opts.QueryParameters.Set("name", *origin.Tenant)
+ if origin.TenantID == 0 && origin.Tenant != "" {
+ opts.QueryParameters.Set("name", origin.Tenant)
tenant, _, err := to.GetTenants(opts)
if err != nil {
- return fmt.Errorf("resolving Tenant name '%s' to an ID:
%w - alerts: %+v", *origin.Tenant, err, tenant.Alerts)
+ return fmt.Errorf("resolving Tenant name '%s' to an ID:
%w - alerts: %+v", origin.Tenant, err, tenant.Alerts)
}
if len(tenant.Response) == 0 {
- return fmt.Errorf("no Tenant with name '%s'",
*origin.Tenant)
+ return fmt.Errorf("no Tenant with name '%s'",
origin.Tenant)
}
- origin.TenantID = tenant.Response[0].ID
+ origin.TenantID = *tenant.Response[0].ID
}
return nil
}
// CreateOrigin creates the given Origin.
-func (to *Session) CreateOrigin(origin tc.Origin, opts RequestOptions)
(tc.OriginDetailResponse, toclientlib.ReqInf, error) {
- var originResp tc.OriginDetailResponse
+func (to *Session) CreateOrigin(origin tc.OriginV5, opts RequestOptions)
(tc.OriginDetailResponseV5, toclientlib.ReqInf, error) {
+ var originResp tc.OriginDetailResponseV5
var remoteAddr net.Addr
reqInf := toclientlib.ReqInf{CacheHitStatus:
toclientlib.CacheHitStatusMiss, RemoteAddr: remoteAddr}
@@ -115,8 +115,8 @@ func (to *Session) CreateOrigin(origin tc.Origin, opts
RequestOptions) (tc.Origi
}
// UpdateOrigin replaces the Origin identified by 'id' with the passed Origin.
-func (to *Session) UpdateOrigin(id int, origin tc.Origin, opts RequestOptions)
(tc.OriginDetailResponse, toclientlib.ReqInf, error) {
- var originResp tc.OriginDetailResponse
+func (to *Session) UpdateOrigin(id int, origin tc.OriginV5, opts
RequestOptions) (tc.OriginDetailResponseV5, toclientlib.ReqInf, error) {
+ var originResp tc.OriginDetailResponseV5
var remoteAddr net.Addr
reqInf := toclientlib.ReqInf{CacheHitStatus:
toclientlib.CacheHitStatusMiss, RemoteAddr: remoteAddr}
@@ -133,8 +133,8 @@ func (to *Session) UpdateOrigin(id int, origin tc.Origin,
opts RequestOptions) (
}
// GetOrigins retrieves Origins from Traffic Ops.
-func (to *Session) GetOrigins(opts RequestOptions) (tc.OriginsResponse,
toclientlib.ReqInf, error) {
- var data tc.OriginsResponse
+func (to *Session) GetOrigins(opts RequestOptions) (tc.OriginsResponseV5,
toclientlib.ReqInf, error) {
+ var data tc.OriginsResponseV5
reqInf, err := to.get(apiOrigins, opts, &data)
return data, reqInf, err
}