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 073e431438 Added a new route and logic for assigning multiple server
capabilities to a server (#6996)
073e431438 is described below
commit 073e431438716a5b7c4624762abe9d77999dcbde
Author: Rima Shah <[email protected]>
AuthorDate: Tue Aug 9 10:46:30 2022 -0600
Added a new route and logic for assigning multiple server capabilities to a
server (#6996)
* Added a new route and logic for assigning multiple server capabilities to
a server.
* Added tests for assigning multiple server capabilities to a server.
* Updated CHANGELOG.md
* Updated test based on latest table driven model
* Added documentation for new route.
* Fixed spacing.
* Fixed spacing.
* Updated based on review comments.
* Updated based on review comments-1.
* Added both API versions to allow client flexibility.
* Updated order of API versions.
* Removed `id` parameter.
* Updated based on another set of review comments.
* Updated test based on route change
* updated format.
* Revert "updated format."
This reverts commit 8181b518391ea1d31c9e6b2b33c6e48a12c92b0c.
* removed format and updated docs
---
CHANGELOG.md | 3 +
.../source/api/v4/multiple_server_capabilities.rst | 83 +++++++++++++++++++++
lib/go-tc/server_server_capability.go | 6 ++
.../api/v4/server_server_capabilities_test.go | 25 ++++++-
traffic_ops/traffic_ops_golang/routing/routes.go | 3 +
.../server/servers_server_capability.go | 86 ++++++++++++++++++++++
traffic_ops/v4-client/endpoints.go | 1 +
.../v4-client/server_server_capabilities.go | 10 +++
8 files changed, 216 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9a1acf2b8c..78c1b611a7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,9 @@ All notable changes to this project will be documented in this
file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
## [unreleased]
+### Added
+- [#6033](https://github.com/apache/trafficcontrol/issues/6033) Added ability
to assign multiple server capabilities to a server.
+
### Changed
- Traffic Portal now obscures sensitive text in Delivery Service "Raw Remap"
fields, private SSL keys, "Header Rewrite" rules, and ILO interface passwords
by default.
diff --git a/docs/source/api/v4/multiple_server_capabilities.rst
b/docs/source/api/v4/multiple_server_capabilities.rst
new file mode 100644
index 0000000000..37f56b1549
--- /dev/null
+++ b/docs/source/api/v4/multiple_server_capabilities.rst
@@ -0,0 +1,83 @@
+..
+..
+.. Licensed under the Apache License, Version 2.0 (the "License");
+.. you may not use this file except in compliance with the License.
+.. You may obtain a copy of the License at
+..
+.. http://www.apache.org/licenses/LICENSE-2.0
+..
+.. Unless required by applicable law or agreed to in writing, software
+.. distributed under the License is distributed on an "AS IS" BASIS,
+.. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+.. See the License for the specific language governing permissions and
+.. limitations under the License.
+..
+
+.. _to-api-multiple_server_capabilities:
+
+********************************
+``multiple_server_capabilities``
+********************************
+
+.. versionadded:: 4.1
+
+``PUT``
+========
+Associates a list of :term:`Server Capability` to a server. The API call
replaces all the server capabilities assigned to a server with the ones
specified in the serverCapabilities field.
+
+:Auth. Required: Yes
+:Roles Required: "admin" or "operations"
+:Permissions Required: SERVER:UPDATE, SERVER:READ, SERVER-CAPABILITY:READ
+:Response Type: Object
+
+Request Structure
+-----------------
+:serverId: The integral, unique identifier of a server to be
associated with a :term:`Server Capability`
+:serverCapabilities: List of :term:`Server Capability`'s name to associate
+
+.. code-block:: http
+ :caption: Request Example
+
+ PUT /api/4.1/multiple_server_capabilities/ HTTP/1.1
+ Host: trafficops.infra.ciab.test
+ User-Agent: curl/7.47.0
+ Accept: */*
+ Cookie: mojolicious=...
+ Content-Length: 84
+ Content-Type: application/json
+
+ {
+ "serverId": 1,
+ "serverCapabilities": ["test", "disk"]
+ }
+
+Response Structure
+------------------
+:serverId: The integral, unique identifier of the newly associated
server
+:serverCapabilities: List of :term:`Server Capability`'s name
+
+.. code-block:: http
+ :caption: Response Example
+
+ HTTP/1.1 200 OK
+ Access-Control-Allow-Credentials: true
+ Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type,
Accept, Set-Cookie, Cookie
+ Access-Control-Allow-Methods: POST,GET,OPTIONS,PUT,DELETE
+ Access-Control-Allow-Origin: *
+ Content-Type: application/json
+ Set-Cookie: mojolicious=...; Path=/; Expires=Mon, 8 Aug 2022 22:40:54
GMT; Max-Age=3600; HttpOnly
+ Whole-Content-Sha512:
eQrl48zWids0kDpfCYmmtYMpegjnFxfOVvlBYxxLSfp7P7p6oWX4uiC+/Cfh2X9i3G+MQ36eH95gukJqOBOGbQ==
+ X-Server-Name: traffic_ops_golang/
+ Date: Mon, 08 Aug 2022 16:15:11 GMT
+ Content-Length: 157
+
+ {
+ "alerts": [{
+ "text": "Multiple Server Capabilities assigned to a
server",
+ "level": "success"
+ }],
+ "response": {
+ "serverId": 1,
+ "serverCapabilities": ["test", "disk"]
+ }
+ }
diff --git a/lib/go-tc/server_server_capability.go
b/lib/go-tc/server_server_capability.go
index 6c406bfa2f..535a95da7f 100644
--- a/lib/go-tc/server_server_capability.go
+++ b/lib/go-tc/server_server_capability.go
@@ -27,6 +27,12 @@ type ServerServerCapability struct {
ServerCapability *string `json:"serverCapability"
db:"server_capability"`
}
+// MultipleServerCapabilities represents an association between a server and
list of server capabilities.
+type MultipleServerCapabilities struct {
+ ServerID int `json:"serverId" db:"server"`
+ ServerCapabilities []string `json:"serverCapabilities"
db:"server_capability"`
+}
+
// ServerServerCapabilitiesResponse is the type of a response from Traffic
// Ops to a request made to its /server_server_capabilities.
type ServerServerCapabilitiesResponse struct {
diff --git a/traffic_ops/testing/api/v4/server_server_capabilities_test.go
b/traffic_ops/testing/api/v4/server_server_capabilities_test.go
index 71ca9dc9be..b7519355c4 100644
--- a/traffic_ops/testing/api/v4/server_server_capabilities_test.go
+++ b/traffic_ops/testing/api/v4/server_server_capabilities_test.go
@@ -37,6 +37,7 @@ func TestServerServerCapabilities(t *testing.T) {
currentTime := time.Now().UTC().Add(-15 * time.Second)
tomorrow := currentTime.AddDate(0, 0, 1).Format(time.RFC1123)
+ var multipleSCs []string
methodTests := utils.V4TestCase{
"GET": {
@@ -147,6 +148,16 @@ func TestServerServerCapabilities(t *testing.T) {
Expectations:
utils.CkRequest(utils.HasError(), utils.HasStatus(http.StatusBadRequest)),
},
},
+ "PUT": {
+ "OK When Assigned Multiple Server
Capabilities": {
+ ClientSession: TOSession,
+ RequestBody: map[string]interface{}{
+ "serverId":
GetServerID(t, "dtrc-mid-04")(),
+ "serverCapabilities":
append(multipleSCs, "disk", "blah"),
+ },
+ Expectations:
utils.CkRequest(utils.NoError(), utils.HasStatus(http.StatusOK)),
+ },
+ },
"DELETE": {
"OK when NOT the LAST SERVER of CACHE GROUP of
TOPOLOGY DS which has REQUIRED CAPABILITIES": {
ClientSession: TOSession,
@@ -180,13 +191,18 @@ func TestServerServerCapabilities(t *testing.T) {
t.Run(method, func(t *testing.T) {
for name, testCase := range testCases {
ssc := tc.ServerServerCapability{}
+ msc := tc.MultipleServerCapabilities{}
var serverId int
var serverCapability string
if testCase.RequestBody != nil {
dat, err :=
json.Marshal(testCase.RequestBody)
assert.NoError(t, err, "Error
occurred when marshalling request body: %v", err)
- err = json.Unmarshal(dat, &ssc)
+ if method == "PUT" {
+ err =
json.Unmarshal(dat, &msc)
+ } else {
+ err =
json.Unmarshal(dat, &ssc)
+ }
assert.NoError(t, err, "Error
occurred when unmarshalling request body: %v", err)
}
@@ -205,6 +221,13 @@ func TestServerServerCapabilities(t *testing.T) {
check(t,
reqInf, nil, alerts, err)
}
})
+ case "PUT":
+ t.Run(name, func(t *testing.T) {
+ alerts, reqInf, err :=
testCase.ClientSession.AssignMultipleServerCapability(msc,
testCase.RequestOpts, serverId)
+ for _, check := range
testCase.Expectations {
+ check(t,
reqInf, nil, alerts, err)
+ }
+ })
case "DELETE":
t.Run(name, func(t *testing.T) {
if val, ok :=
testCase.RequestOpts.QueryParameters["serverId"]; ok {
diff --git a/traffic_ops/traffic_ops_golang/routing/routes.go
b/traffic_ops/traffic_ops_golang/routing/routes.go
index 2c02779b60..7089c120bf 100644
--- a/traffic_ops/traffic_ops_golang/routing/routes.go
+++ b/traffic_ops/traffic_ops_golang/routing/routes.go
@@ -131,6 +131,9 @@ func Routes(d ServerData) ([]Route, http.Handler, error) {
* 4.x API
*/
+ // Assign Multiple Server Capabilities
+ {Version: api.Version{Major: 4, Minor: 1}, Method:
http.MethodPut, Path: `multiple_server_capabilities/?$`, Handler:
server.AssignMultipleServerCapabilities, RequiredPrivLevel:
auth.PrivLevelOperations, RequiredPermissions: []string{"SERVER:UPDATE",
"SERVER:READ", "SERVER-CAPABILITY:READ"}, Authenticated: Authenticated,
Middlewares: nil, ID: 40792419258},
+
// CDNI integration
{Version: api.Version{Major: 4, Minor: 0}, Method:
http.MethodGet, Path: `OC/FCI/advertisement/?$`, Handler: cdni.GetCapabilities,
RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions:
[]string{"CDNI-CAPACITY:READ"}, Authenticated: Authenticated, Middlewares: nil,
ID: 541357729077},
{Version: api.Version{Major: 4, Minor: 0}, Method:
http.MethodPut, Path: `OC/CI/configuration/?$`, Handler: cdni.PutConfiguration,
RequiredPrivLevel: auth.PrivLevelReadOnly, RequiredPermissions:
[]string{"CDNI-CAPACITY:UPDATE"}, Authenticated: Authenticated, Middlewares:
nil, ID: 541357729078},
diff --git a/traffic_ops/traffic_ops_golang/server/servers_server_capability.go
b/traffic_ops/traffic_ops_golang/server/servers_server_capability.go
index 64291f21cd..21ae31db2a 100644
--- a/traffic_ops/traffic_ops_golang/server/servers_server_capability.go
+++ b/traffic_ops/traffic_ops_golang/server/servers_server_capability.go
@@ -20,6 +20,7 @@ package server
*/
import (
+ "encoding/json"
"errors"
"fmt"
"net/http"
@@ -441,3 +442,88 @@ func getDSTenantIDsByIDs(tx *sqlx.Tx, dsIDs []int64)
([]DSTenant, error) {
return dsTenantIDs, nil
}
+
+// AssignMultipleServerCapabilities helps assign multiple server capabilities
to a given server.
+func AssignMultipleServerCapabilities(w http.ResponseWriter, r *http.Request) {
+ inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+ tx := inf.Tx.Tx
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, errCode, userErr, sysErr)
+ return
+ }
+ defer inf.Close()
+
+ var msc tc.MultipleServerCapabilities
+ if err := json.NewDecoder(r.Body).Decode(&msc); err != nil {
+ api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+ return
+ }
+
+ // Check existence prior to checking type
+ _, exists, err := dbhelpers.GetServerNameFromID(tx, int64(msc.ServerID))
+ if err != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError, nil,
err)
+ }
+ if !exists {
+ userErr := fmt.Errorf("server %d does not exist", msc.ServerID)
+ api.HandleErr(w, r, tx, http.StatusNotFound, userErr, nil)
+ return
+ }
+
+ // Ensure type is correct
+ correctType := true
+ if err := tx.QueryRow(scCheckServerTypeQuery(),
msc.ServerID).Scan(&correctType); err != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError, nil,
fmt.Errorf("checking server type: %w", err))
+ return
+ }
+ if !correctType {
+ userErr := fmt.Errorf("server %d has an incorrect server type.
Server capabilities can only be assigned to EDGE or MID servers", msc.ServerID)
+ api.HandleErr(w, r, tx, http.StatusBadRequest, userErr, nil)
+ return
+ }
+
+ cdnName, err := dbhelpers.GetCDNNameFromServerID(tx,
int64(msc.ServerID))
+ if err != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError, nil,
err)
+ 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
+ }
+
+ //Delete existing rows from server_server_capability for a given server
+ _, err = tx.Exec("DELETE FROM server_server_capability ssc WHERE
ssc.server=$1", msc.ServerID)
+ if err != nil {
+ useErr, sysErr, statusCode := api.ParseDBError(err)
+ api.HandleErr(w, r, tx, statusCode, useErr, sysErr)
+ return
+ }
+
+ multipleServerCapabilities := make([]string, 0,
len(msc.ServerCapabilities))
+
+ mscQuery := `WITH inserted AS (
+ INSERT INTO server_server_capability
+ SELECT "server_capability", $2
+ FROM UNNEST($1::text[]) AS tmp("server_capability")
+ RETURNING server_capability
+ )
+ SELECT ARRAY_AGG(server_capability)
+ FROM (
+ SELECT server_capability
+ FROM inserted
+ ) AS returned(server_capability)`
+
+ err = tx.QueryRow(mscQuery, pq.Array(msc.ServerCapabilities),
msc.ServerID).Scan(pq.Array(&multipleServerCapabilities))
+ if err != nil {
+ useErr, sysErr, statusCode := api.ParseDBError(err)
+ api.HandleErr(w, r, tx, statusCode, useErr, sysErr)
+ return
+ }
+ msc.ServerCapabilities = multipleServerCapabilities
+ alerts := tc.CreateAlerts(tc.SuccessLevel, "Multiple Server
Capabilities assigned to a server")
+ api.WriteAlertsObj(w, r, http.StatusOK, alerts, msc)
+ return
+}
diff --git a/traffic_ops/v4-client/endpoints.go
b/traffic_ops/v4-client/endpoints.go
index fd9478cd1c..470afe26da 100644
--- a/traffic_ops/v4-client/endpoints.go
+++ b/traffic_ops/v4-client/endpoints.go
@@ -22,6 +22,7 @@ package client
// Versions are ordered latest-first.
func apiVersions() []string {
return []string{
+ "4.1",
"4.0",
}
}
diff --git a/traffic_ops/v4-client/server_server_capabilities.go
b/traffic_ops/v4-client/server_server_capabilities.go
index 9cd03733a7..dd17238517 100644
--- a/traffic_ops/v4-client/server_server_capabilities.go
+++ b/traffic_ops/v4-client/server_server_capabilities.go
@@ -27,6 +27,9 @@ import (
// /server_server_capabilities API endpoint.
const apiServerServerCapabilities = "/server_server_capabilities"
+// apiMultipleServerCapabilities is the API version-relative path to the
/multiple_server_capabilities API endpoint.
+const apiMultipleServerCapabilities = "/multiple_server_capabilities"
+
// CreateServerServerCapability assigns a Server Capability to a Server.
func (to *Session) CreateServerServerCapability(ssc tc.ServerServerCapability,
opts RequestOptions) (tc.Alerts, toclientlib.ReqInf, error) {
var alerts tc.Alerts
@@ -53,3 +56,10 @@ func (to *Session) GetServerServerCapabilities(opts
RequestOptions) (tc.ServerSe
reqInf, err := to.get(apiServerServerCapabilities, opts, &resp)
return resp, reqInf, err
}
+
+// AssignMultipleServerCapability assigns multiple server capabilities to a
server.
+func (to *Session) AssignMultipleServerCapability(msc
tc.MultipleServerCapabilities, opts RequestOptions, id int) (tc.Alerts,
toclientlib.ReqInf, error) {
+ var alerts tc.Alerts
+ reqInf, err := to.put(apiMultipleServerCapabilities, opts, msc, &alerts)
+ return alerts, reqInf, err
+}