Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added the ability to change a server capability name #5605

Merged
merged 17 commits into from
Mar 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).

## [unreleased]
### Added
- Traffic Ops/Traffic Portal: [#5479](https://github.com/apache/trafficcontrol/issues/5479) - Added the ability to change a server capability name
- Traffic Ops: [#3577](https://github.com/apache/trafficcontrol/issues/3577) - Added a query param (server host_name or ID) for servercheck API
- Traffic Portal: [#5318](https://github.com/apache/trafficcontrol/issues/5318) - Rename server columns for IPv4 address fields.
- Traffic Portal: [#5361](https://github.com/apache/trafficcontrol/issues/5361) - Added the ability to change the name of a topology.
Expand Down
60 changes: 60 additions & 0 deletions docs/source/api/v4/server_capabilities.rst
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,66 @@ Response Structure
}
}

``PUT``
========
Update an existing :term:`Server Capability`.

:Auth. Required: Yes
:Roles Required: "admin" or "operations"
:Response Type: Object

Request Structure
-----------------
:name: The name of the :term:`Server Capability`

.. code-block:: http
:caption: Request Example

PUT /api/4.0/server_capabilities/edit?name=RAM HTTP/1.1
Host: trafficops.infra.ciab.test
User-Agent: curl/7.47.0
Accept: */*
Cookie: mojolicious=...
Content-Length: 15
Content-Type: application/json

{
"name": "HDD"
}

Response Structure
------------------
:name: The name of this :term:`Server Capability`
:lastUpdated: The date and time at which this :term:`Server Capability` was last updated, in ISO-like format

.. 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, 18 Nov 2019 17:40:54 GMT; Max-Age=3600; HttpOnly
Whole-Content-Sha512: ysdopC//JQI79BRUa61s6M2HzHxYHpo5RdcuauOoqCYxiVOoUhNZfOVydVkv8zDN2qA374XKnym4kWj3VzQIXg==
X-Server-Name: traffic_ops_golang/
Date: Wed, 03 March 2021 21:22:08 GMT
Content-Length: 137

{
"alerts": [
{
"text": "server capability was updated.",
"level": "success"
}
],
"response": {
"name": "HDD",
"lastUpdated": "2021-03-03 21:22:08+00"
}
}

``DELETE``
==========
Deletes a specific :term:`Server Capability`.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
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.
*/

-- +goose Up
-- SQL in section 'Up' is executed when this migration is applied
ALTER TABLE public.server_server_capability DROP CONSTRAINT fk_server_capability;
rimashah25 marked this conversation as resolved.
Show resolved Hide resolved
ALTER TABLE public.deliveryservices_required_capability DROP CONSTRAINT fk_required_capability;
ALTER TABLE public.server_server_capability ADD CONSTRAINT fk_server_capability FOREIGN KEY (server_capability) REFERENCES server_capability(name) ON UPDATE CASCADE ON DELETE RESTRICT;
ALTER TABLE public.deliveryservices_required_capability ADD CONSTRAINT fk_required_capability FOREIGN KEY (required_capability) REFERENCES server_capability(name) ON UPDATE CASCADE ON DELETE RESTRICT;

-- +goose Down
-- SQL section 'Down' is executed when this migration is rolled back
ALTER TABLE public.server_server_capability DROP CONSTRAINT fk_server_capability;
ALTER TABLE public.deliveryservices_required_capability DROP CONSTRAINT fk_required_capability;
ALTER TABLE public.server_server_capability ADD CONSTRAINT fk_server_capability FOREIGN KEY (server_capability) REFERENCES server_capability(name) ON DELETE RESTRICT;
ALTER TABLE public.deliveryservices_required_capability ADD CONSTRAINT fk_required_capability FOREIGN KEY (required_capability) REFERENCES server_capability(name) ON DELETE RESTRICT;
42 changes: 42 additions & 0 deletions traffic_ops/testing/api/v4/servercapabilities_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func TestServerCapabilities(t *testing.T) {
SortTestServerCapabilities(t)
GetTestServerCapabilities(t)
ValidationTestServerCapabilities(t)
UpdateTestServerCapabilities(t)
})
}

Expand Down Expand Up @@ -89,6 +90,47 @@ func ValidationTestServerCapabilities(t *testing.T) {
}
}

func UpdateTestServerCapabilities(t *testing.T) {
var header http.Header

// Get server capability name and edit it to a new name
resp, _, err := TOSession.GetServerCapabilitiesWithHdr(header)
if err != nil {
rimashah25 marked this conversation as resolved.
Show resolved Hide resolved
t.Fatalf("Expected no error, but got %v", err.Error())
}
if len(resp) == 0 {
t.Fatalf("no server capability in response, quitting")
}
origName := resp[0].Name
newSCName := "sc-test"
resp[0].Name = newSCName

// Update server capability with new name
updateResponse, _, err := TOSession.UpdateServerCapabilityByName(origName, &resp[0])
if err != nil {
t.Errorf("cannot PUT server capability: %v - %v", err, updateResponse)
}

// Get updated name
getResp, _, err := TOSession.GetServerCapabilityWithHdr(newSCName, header)
if err != nil {
rimashah25 marked this conversation as resolved.
Show resolved Hide resolved
t.Fatalf("Expected no error, but %v", err.Error())
}
if getResp == nil {
t.Fatalf("no server capability in response, quitting")
}
if getResp.Name != newSCName {
t.Errorf("failed to update server capability name, expected: %v but got: %v", newSCName, updateResponse.Name)
}

// Set everything back as it was for further testing.
resp[0].Name = origName
r, _, err := TOSession.UpdateServerCapabilityByName(newSCName, &resp[0])
if err != nil {
t.Errorf("cannot PUT seerver capability: %v - %v", err, r)
}
}

func DeleteTestServerCapabilities(t *testing.T) {

for _, sc := range testData.ServerCapabilities {
Expand Down
64 changes: 64 additions & 0 deletions traffic_ops/testing/api/v4/serverservercapability_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func TestServerServerCapabilities(t *testing.T) {
GetTestServerServerCapabilitiesIMS(t)
GetTestServerServerCapabilities(t)
GetDeliveryServiceServersWithCapabilities(t)
UpdateTestServerServerCapabilities(t)
})
}

Expand Down Expand Up @@ -222,6 +223,69 @@ func GetTestServerServerCapabilities(t *testing.T) {
}
}

func UpdateTestServerServerCapabilities(t *testing.T) {
var header http.Header

// Get server capability name and edit it to a new name
resp, _, err := TOSession.GetServerCapabilitiesWithHdr(header)
if err != nil {
rimashah25 marked this conversation as resolved.
Show resolved Hide resolved
t.Fatalf("Expected no error, but got %v", err.Error())
}
if len(resp) == 0 {
t.Fatal("no server capability in response, quitting")
}
origName := resp[0].Name
newSCName := "sc-test"
resp[0].Name = newSCName

// Get all servers related to original sever capability name
servOrigResp, _, err := TOSession.GetServerServerCapabilitiesWithHdr(nil, nil, &origName, nil)
if err != nil {
t.Fatalf("cannot GET server capabilities assigned to servers by server capability name %v: %v", origName, err)
}
if len(servOrigResp) == 0 {
t.Fatalf("no servers associated with server capability name: %v", origName)
}
mapOrigServ := make(map[string]string)
for _, s := range servOrigResp {
mapOrigServ[*s.Server] = *s.ServerCapability
}

// Update server capability with new name
updateResponse, _, err := TOSession.UpdateServerCapabilityByName(origName, &resp[0])
if err != nil {
t.Errorf("cannot PUT server capability: %v - %v", err, updateResponse)
}

//To check whether the primary key change trickled down to server table
servUpdatedResp, _, err := TOSession.GetServerServerCapabilitiesWithHdr(nil, nil, &newSCName, nil)
if err != nil {
t.Fatalf("cannot GET server capabilities assigned to servers by server capability name %v: %v", newSCName, err)
}
if len(servUpdatedResp) == 0 {
t.Fatalf("no server associated with server capability name:%v", newSCName)
}
if len(servOrigResp) != len(servUpdatedResp) {
t.Fatalf("length of servers for a given server capability name is different, expected: %v-%v, got: %v-%v", origName, len(servOrigResp), newSCName, len(servUpdatedResp))
}
for _, s := range servUpdatedResp {
if newSCName != *s.ServerCapability {
t.Errorf("GET server server capabilities by server capability returned non-matching server capability: %v", *s.ServerCapability)
}
_, ok := mapOrigServ[*s.Server]
if !ok {
t.Fatalf("server capability name change didn't trickle to server: %v", *s.Server)
}
}

// Set everything back as it was for further testing.
resp[0].Name = origName
r, _, err := TOSession.UpdateServerCapabilityByName(newSCName, &resp[0])
if err != nil {
t.Errorf("cannot PUT seerver capability: %v - %v", err, r)
}
}

func DeleteTestServerServerCapabilities(t *testing.T) {
// Get Server Capabilities to delete them
sscs, _, err := TOSession.GetServerServerCapabilitiesWithHdr(nil, nil, nil, nil)
Expand Down
1 change: 1 addition & 0 deletions traffic_ops/traffic_ops_golang/routing/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,7 @@ func Routes(d ServerData) ([]Route, []RawRoute, http.Handler, error) {
//Server Capability
{api.Version{4, 0}, http.MethodGet, `server_capabilities$`, api.ReadHandler(&servercapability.TOServerCapability{}), auth.PrivLevelReadOnly, Authenticated, nil, 4104073913},
{api.Version{4, 0}, http.MethodPost, `server_capabilities$`, api.CreateHandler(&servercapability.TOServerCapability{}), auth.PrivLevelOperations, Authenticated, nil, 40744707083},
{api.Version{4, 0}, http.MethodPut, `server_capabilities$`, api.UpdateHandler(&servercapability.TOServerCapability{}), auth.PrivLevelOperations, Authenticated, nil, 42543770109},
{api.Version{4, 0}, http.MethodDelete, `server_capabilities$`, api.DeleteHandler(&servercapability.TOServerCapability{}), auth.PrivLevelOperations, Authenticated, nil, 4364150383},

//Server Server Capabilities: CRUD
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ package servercapability
*/

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

"github.com/apache/trafficcontrol/lib/go-log"
"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/lib/go-tc/tovalidate"
"github.com/apache/trafficcontrol/lib/go-util"
Expand All @@ -33,6 +38,7 @@ import (

type TOServerCapability struct {
api.APIInfoImpl `json:"-"`
RequestedName string
tc.ServerCapability
}

Expand Down Expand Up @@ -60,6 +66,15 @@ FROM
`
}

func (v *TOServerCapability) updateQuery() string {
return `
UPDATE server_capability sc SET
name = $1
WHERE sc.name = $2
RETURNING sc.name, sc.last_updated
`
}

func (v *TOServerCapability) DeleteQuery() string {
return `
DELETE FROM server_capability WHERE name=:name
Expand All @@ -82,6 +97,7 @@ func (v TOServerCapability) GetKeys() (map[string]interface{}, bool) {
}

func (v *TOServerCapability) SetKeys(keys map[string]interface{}) {
v.RequestedName = v.Name
v.Name, _ = keys["name"].(string)
}

Expand All @@ -105,6 +121,45 @@ func (v *TOServerCapability) Read(h http.Header, useIMS bool) ([]interface{}, er
api.DefaultSort(v.APIInfo(), "name")
return api.GenericRead(h, v, useIMS)
}

func (v *TOServerCapability) Update(h http.Header) (error, error, int) {
sc, userErr, sysErr, errCode, _ := v.Read(h, false)
if userErr != nil || sysErr != nil {
return userErr, sysErr, errCode
}
if len(sc) != 1 {
return fmt.Errorf("cannot find exactly one server capability with the query string provided"), nil, http.StatusBadRequest
}

// check if the name was being updated by someone else
var existingLastUpdated *tc.TimeNoMod
q := `SELECT last_updated FROM server_capability WHERE name = $1`
if err := v.ReqInfo.Tx.QueryRow(q, v.Name).Scan(&existingLastUpdated); err != nil {
if err == sql.ErrNoRows {
return errors.New("server capability was not found"), nil, http.StatusNotFound
}
return nil, errors.New("server capability update: querying: " + err.Error()), http.StatusInternalServerError
}
if !api.IsUnmodified(h, existingLastUpdated.Time) {
return errors.New("the resource has been modified since the time specified by the request headers"), nil, http.StatusPreconditionFailed
}

// udpate server capability name
rows, err := v.ReqInfo.Tx.Query(v.updateQuery(), v.RequestedName, v.Name)
if err != nil {
return nil, fmt.Errorf("server capability update: error setting the name for server capability %v: %v", v.Name, err.Error()), http.StatusInternalServerError
}
defer log.Close(rows, "unable to close DB connection")
rimashah25 marked this conversation as resolved.
Show resolved Hide resolved

for rows.Next() {
err = rows.Scan(&v.Name, &v.LastUpdated)
if err != nil {
return api.ParseDBError(err)
}
}
return nil, nil, http.StatusOK
}

func (v *TOServerCapability) SelectMaxLastUpdatedQuery(where, orderBy, pagination, tableName string) string {
return `SELECT max(t) from (
SELECT max(sc.last_updated) as t from server_capability sc ` + where + orderBy + pagination +
Expand Down
11 changes: 11 additions & 0 deletions traffic_ops/v4-client/servercapability.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,17 @@ func (to *Session) GetServerCapability(name string) (*tc.ServerCapability, tocli
return to.GetServerCapabilityWithHdr(name, nil)
}

// UpdateServerCapabilityByName updates a Server Capability by name.
func (to *Session) UpdateServerCapabilityByName(name string, sc *tc.ServerCapability) (*tc.ServerCapability, toclientlib.ReqInf, error) {
route := fmt.Sprintf("%s?name=%s", APIServerCapabilities, url.QueryEscape(name))
var data tc.ServerCapability
reqInf, err := to.put(route, sc, nil, &data)
if err != nil {
return nil, reqInf, err
}
return &data, reqInf, nil
}

// DeleteServerCapability deletes the given server capability by name.
func (to *Session) DeleteServerCapability(name string) (tc.Alerts, toclientlib.ReqInf, error) {
reqUrl := fmt.Sprintf("%s?name=%s", APIServerCapabilities, url.QueryEscape(name))
Expand Down
4 changes: 2 additions & 2 deletions traffic_portal/app/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ var trafficPortal = angular.module('trafficPortal', [
require('./modules/private/serverCapabilities/list').name,
require('./modules/private/serverCapabilities/new').name,
require('./modules/private/serverCapabilities/servers').name,
require('./modules/private/serverCapabilities/view').name,
require('./modules/private/serverCapabilities/edit').name,
require('./modules/private/servers').name,
require('./modules/private/servers/capabilities').name,
require('./modules/private/servers/deliveryServices').name,
Expand Down Expand Up @@ -325,7 +325,7 @@ var trafficPortal = angular.module('trafficPortal', [
require('./common/modules/form/role/new').name,
require('./common/modules/form/serverCapability').name,
require('./common/modules/form/serverCapability/new').name,
require('./common/modules/form/serverCapability/view').name,
require('./common/modules/form/serverCapability/edit').name,
require('./common/modules/form/server').name,
require('./common/modules/form/server/edit').name,
require('./common/modules/form/server/new').name,
Expand Down
Loading