shamrickus commented on code in PR #7691:
URL: https://github.com/apache/trafficcontrol/pull/7691#discussion_r1283312667
##########
traffic_ops/traffic_ops_golang/topology/topologies.go:
##########
@@ -396,18 +502,84 @@ func (topology *TOTopology) GetAuditName() string {
return topology.Name
}
+// Create is the handler for creating a new topology entity in api version 5.0.
+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)
+ return
+ }
+ defer inf.Close()
+ tx := inf.Tx.Tx
+ var topology tc.TopologyV5
+ if err := json.NewDecoder(r.Body).Decode(&topology); err != nil {
+ api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+ return
+ }
+
+ alerts, userErr, sysErr := ValidateTopology(topology, inf)
+ if userErr != nil || sysErr != nil {
+ code := http.StatusBadRequest
+ if sysErr != nil {
+ code = http.StatusInternalServerError
+ }
+ api.HandleErr(w, r, inf.Tx.Tx, code, userErr, sysErr)
+ return
+ }
+
+ legacyNodes := DowngradeTopologyNodes(topology.Nodes)
+ userErr, sysErr, statusCode :=
checkIfTopologyCanBeAlteredByCurrentUser(inf, legacyNodes)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+ return
+ }
+ err := tx.QueryRow(insertQuery(), topology.Name,
topology.Description).Scan(&topology.Name, &topology.Description,
&topology.LastUpdated)
+ if err != nil {
+ userErr, sysErr, statusCode := api.ParseDBError(err)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr,
sysErr)
+ return
+ }
+ }
+ topology.LastUpdated, err =
util.ConvertTimeFormat(*topology.LastUpdated, time.RFC3339)
+ if err != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, fmt.Errorf("could not convert last updated time into rfc3339 format: %s",
err.Error()))
+ return
+ }
+
+ if userErr, sysErr, statusCode := addNodes(tx, topology.Name,
topology.Nodes); userErr != nil || sysErr != nil {
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr,
sysErr)
+ return
+ }
+ }
+
+ if userErr, sysErr, statusCode := addParents(tx, topology.Nodes);
userErr != nil || sysErr != nil {
Review Comment:
These variables are already declared and don't need to be redeclared.
##########
traffic_ops/traffic_ops_golang/topology/topologies.go:
##########
@@ -88,6 +90,111 @@ func (topology *TOTopology) GetType() string {
return "topology"
}
+// DowngradeTopologyNodes downgrades v5 topology nodes into legacy topology
node structures.
+func DowngradeTopologyNodes(nodes []tc.TopologyNodeV5) []tc.TopologyNode {
+ var legacyNodes []tc.TopologyNode
Review Comment:
We know the length of `legacyNodes` at this point, so we could `make` them
here to do the memory allocs at once.
##########
traffic_ops/traffic_ops_golang/topology/validation.go:
##########
@@ -54,6 +56,45 @@ func checkForSelfParents(nodes []tc.TopologyNode, index int)
error {
return nil
}
+// checkForEdgeParents returns an error if an index given in the parents
array, adds a warning + returns a nil error if
Review Comment:
The first part of this comment `returns an error if an index given in the
parents array, ...` doesn't make sense.
##########
traffic_ops/traffic_ops_golang/topology/topologies.go:
##########
@@ -419,6 +591,286 @@ func (topology *TOTopology) Create() (error, error, int) {
return nil, nil, 0
}
+func readTopologies(r *http.Request, useIMS bool) ([]interface{}, error,
error, int, *time.Time) {
+ var maxTime time.Time
+ var runSecond bool
+ inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+ if userErr != nil || sysErr != nil {
+ //api.HandleErr(w, r, tx, errCode, userErr, sysErr)
Review Comment:
Commented line should be removed.
##########
traffic_ops/testing/api/v5/topologies_test.go:
##########
@@ -20,17 +20,16 @@ package v5
*/
import (
- "net/http"
- "net/url"
- "testing"
- "time"
-
"github.com/apache/trafficcontrol/lib/go-rfc"
"github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/lib/go-util/assert"
"github.com/apache/trafficcontrol/traffic_ops/testing/api/utils"
"github.com/apache/trafficcontrol/traffic_ops/toclientlib"
client "github.com/apache/trafficcontrol/traffic_ops/v5-client"
+ "net/http"
Review Comment:
These imports didn't need to be moved.
##########
traffic_ops/traffic_ops_golang/topology/topologies.go:
##########
@@ -88,6 +90,111 @@ func (topology *TOTopology) GetType() string {
return "topology"
}
+// DowngradeTopologyNodes downgrades v5 topology nodes into legacy topology
node structures.
+func DowngradeTopologyNodes(nodes []tc.TopologyNodeV5) []tc.TopologyNode {
+ var legacyNodes []tc.TopologyNode
+ for _, n := range nodes {
+ var legacyNode tc.TopologyNode
+ legacyNode.Id = n.Id
+ legacyNode.Cachegroup = n.Cachegroup
+ legacyNode.Parents = make([]int, 0)
Review Comment:
We know the length of `Parents` at this point.
##########
traffic_ops/testing/api/v5/topologies_test.go:
##########
@@ -470,42 +469,42 @@ func TestTopologies(t *testing.T) {
topology := testCase.RequestBody
switch method {
- case "GET", "GET AFTER CHANGES":
- t.Run(name, func(t *testing.T) {
- resp, reqInf, err :=
testCase.ClientSession.GetTopologies(testCase.RequestOpts)
- for _, check := range
testCase.Expectations {
- check(t,
reqInf, resp.Response, resp.Alerts, err)
- }
- })
+ //case "GET", "GET AFTER CHANGES":
Review Comment:
This test should either not be commented out or the the commented out line
should be removed.
##########
traffic_ops/traffic_ops_golang/topology/topologies.go:
##########
@@ -21,8 +21,10 @@ package topology
import (
"database/sql"
+ "encoding/json"
"errors"
"fmt"
+ "github.com/apache/trafficcontrol/lib/go-rfc"
Review Comment:
Local imports should go below system imports
##########
traffic_ops/traffic_ops_golang/topology/topologies.go:
##########
@@ -419,6 +591,286 @@ func (topology *TOTopology) Create() (error, error, int) {
return nil, nil, 0
}
+func readTopologies(r *http.Request, useIMS bool) ([]interface{}, error,
error, int, *time.Time) {
+ var maxTime time.Time
+ var runSecond bool
+ inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+ if userErr != nil || sysErr != nil {
+ //api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+ return nil, userErr, sysErr, errCode, &maxTime
+ }
+ defer inf.Close()
+
+ interfaces := make([]interface{}, 0)
+
+ queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{
+ "name": {Column: "t.name"},
+ "description": {Column: "t.description"},
+ "lastUpdated": {Column: "t.last_updated"},
+ }
+
+ where, orderBy, pagination, queryValues, errs :=
dbhelpers.BuildWhereAndOrderByAndPagination(inf.Params, queryParamsToQueryCols)
+ if len(errs) > 0 {
+ return nil, util.JoinErrs(errs), nil, http.StatusBadRequest,
&maxTime
+ }
+
+ if useIMS {
+ runSecond, maxTime = ims.TryIfModifiedSinceQuery(inf.Tx,
r.Header, queryValues, selectMaxLastUpdatedQuery(where))
+ if !runSecond {
+ log.Debugln("IMS HIT")
+ return []interface{}{}, nil, nil,
http.StatusNotModified, &maxTime
+ }
+ log.Debugln("IMS MISS")
+ } else {
+ log.Debugln("Non IMS request")
+ }
+
+ // Case where we need to run the second query
+ query := selectQuery() + where + orderBy + pagination
+ rows, err := inf.Tx.NamedQuery(query, queryValues)
+ if err != nil {
+ //api.HandleErr(w, r, tx, http.StatusInternalServerError, nil,
errors.New("topology read: querying: "+err.Error()))
+ return nil, nil, errors.New("topology read: querying: " +
err.Error()), http.StatusInternalServerError, &maxTime
+ }
+ defer log.Close(rows, "unable to close DB connection")
+
+ topologies := map[string]*tc.TopologyV5{}
+ indices := map[int]int{}
+ for index := 0; rows.Next(); index++ {
+ var (
+ name, description string
+ lastUpdated time.Time
+ )
+ topologyNode := tc.TopologyNodeV5{}
+ topologyNode.Parents = []int{}
+ var parents pq.Int64Array
+ if err = rows.Scan(
+ &name,
+ &description,
+ &lastUpdated,
+ &topologyNode.Id,
+ &topologyNode.Cachegroup,
+ &parents,
+ ); err != nil {
+ //api.HandleErr(w, r, tx,
http.StatusInternalServerError, nil, errors.New("topology read: scanning:
"+err.Error()))
Review Comment:
Commented line should be removed.
##########
traffic_ops/traffic_ops_golang/topology/topologies.go:
##########
@@ -396,18 +502,84 @@ func (topology *TOTopology) GetAuditName() string {
return topology.Name
}
+// Create is the handler for creating a new topology entity in api version 5.0.
+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)
+ return
+ }
+ defer inf.Close()
+ tx := inf.Tx.Tx
+ var topology tc.TopologyV5
+ if err := json.NewDecoder(r.Body).Decode(&topology); err != nil {
+ api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+ return
+ }
+
+ alerts, userErr, sysErr := ValidateTopology(topology, inf)
+ if userErr != nil || sysErr != nil {
+ code := http.StatusBadRequest
+ if sysErr != nil {
+ code = http.StatusInternalServerError
+ }
+ api.HandleErr(w, r, inf.Tx.Tx, code, userErr, sysErr)
+ return
+ }
+
+ legacyNodes := DowngradeTopologyNodes(topology.Nodes)
+ userErr, sysErr, statusCode :=
checkIfTopologyCanBeAlteredByCurrentUser(inf, legacyNodes)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+ return
+ }
+ err := tx.QueryRow(insertQuery(), topology.Name,
topology.Description).Scan(&topology.Name, &topology.Description,
&topology.LastUpdated)
+ if err != nil {
+ userErr, sysErr, statusCode := api.ParseDBError(err)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr,
sysErr)
+ return
+ }
+ }
+ topology.LastUpdated, err =
util.ConvertTimeFormat(*topology.LastUpdated, time.RFC3339)
+ if err != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, fmt.Errorf("could not convert last updated time into rfc3339 format: %s",
err.Error()))
+ return
+ }
+
+ if userErr, sysErr, statusCode := addNodes(tx, topology.Name,
topology.Nodes); userErr != nil || sysErr != nil {
Review Comment:
These variables are already declared and don't need to be redeclared.
##########
traffic_ops/traffic_ops_golang/topology/topologies.go:
##########
@@ -396,18 +502,84 @@ func (topology *TOTopology) GetAuditName() string {
return topology.Name
}
+// Create is the handler for creating a new topology entity in api version 5.0.
+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)
+ return
+ }
+ defer inf.Close()
+ tx := inf.Tx.Tx
+ var topology tc.TopologyV5
+ if err := json.NewDecoder(r.Body).Decode(&topology); err != nil {
+ api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+ return
+ }
+
+ alerts, userErr, sysErr := ValidateTopology(topology, inf)
+ if userErr != nil || sysErr != nil {
+ code := http.StatusBadRequest
+ if sysErr != nil {
+ code = http.StatusInternalServerError
+ }
+ api.HandleErr(w, r, inf.Tx.Tx, code, userErr, sysErr)
+ return
+ }
+
+ legacyNodes := DowngradeTopologyNodes(topology.Nodes)
+ userErr, sysErr, statusCode :=
checkIfTopologyCanBeAlteredByCurrentUser(inf, legacyNodes)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+ return
+ }
+ err := tx.QueryRow(insertQuery(), topology.Name,
topology.Description).Scan(&topology.Name, &topology.Description,
&topology.LastUpdated)
+ if err != nil {
+ userErr, sysErr, statusCode := api.ParseDBError(err)
Review Comment:
These variables are already declared and don't need to be redeclared.
##########
traffic_ops/traffic_ops_golang/topology/topologies.go:
##########
@@ -419,6 +591,286 @@ func (topology *TOTopology) Create() (error, error, int) {
return nil, nil, 0
}
+func readTopologies(r *http.Request, useIMS bool) ([]interface{}, error,
error, int, *time.Time) {
+ var maxTime time.Time
+ var runSecond bool
+ inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+ if userErr != nil || sysErr != nil {
+ //api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+ return nil, userErr, sysErr, errCode, &maxTime
+ }
+ defer inf.Close()
+
+ interfaces := make([]interface{}, 0)
+
+ queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{
+ "name": {Column: "t.name"},
+ "description": {Column: "t.description"},
+ "lastUpdated": {Column: "t.last_updated"},
+ }
+
+ where, orderBy, pagination, queryValues, errs :=
dbhelpers.BuildWhereAndOrderByAndPagination(inf.Params, queryParamsToQueryCols)
+ if len(errs) > 0 {
+ return nil, util.JoinErrs(errs), nil, http.StatusBadRequest,
&maxTime
+ }
+
+ if useIMS {
+ runSecond, maxTime = ims.TryIfModifiedSinceQuery(inf.Tx,
r.Header, queryValues, selectMaxLastUpdatedQuery(where))
+ if !runSecond {
+ log.Debugln("IMS HIT")
+ return []interface{}{}, nil, nil,
http.StatusNotModified, &maxTime
+ }
+ log.Debugln("IMS MISS")
+ } else {
+ log.Debugln("Non IMS request")
+ }
+
+ // Case where we need to run the second query
+ query := selectQuery() + where + orderBy + pagination
+ rows, err := inf.Tx.NamedQuery(query, queryValues)
+ if err != nil {
+ //api.HandleErr(w, r, tx, http.StatusInternalServerError, nil,
errors.New("topology read: querying: "+err.Error()))
+ return nil, nil, errors.New("topology read: querying: " +
err.Error()), http.StatusInternalServerError, &maxTime
+ }
+ defer log.Close(rows, "unable to close DB connection")
+
+ topologies := map[string]*tc.TopologyV5{}
+ indices := map[int]int{}
+ for index := 0; rows.Next(); index++ {
+ var (
+ name, description string
+ lastUpdated time.Time
+ )
+ topologyNode := tc.TopologyNodeV5{}
+ topologyNode.Parents = []int{}
+ var parents pq.Int64Array
+ if err = rows.Scan(
+ &name,
+ &description,
+ &lastUpdated,
+ &topologyNode.Id,
+ &topologyNode.Cachegroup,
+ &parents,
+ ); err != nil {
+ //api.HandleErr(w, r, tx,
http.StatusInternalServerError, nil, errors.New("topology read: scanning:
"+err.Error()))
+ return nil, nil, errors.New("topology read: scanning: "
+ err.Error()), http.StatusInternalServerError, &maxTime
+ }
+ for _, id := range parents {
+ topologyNode.Parents = append(topologyNode.Parents,
int(id))
+ }
+ indices[topologyNode.Id] = index
+ if _, exists := topologies[name]; !exists {
+ topology := tc.TopologyV5{Nodes: []tc.TopologyNodeV5{}}
+ topologies[name] = &topology
+ topology.Name = name
+ topology.Description = description
+ topology.LastUpdated, err =
util.ConvertTimeFormat(lastUpdated, time.RFC3339)
+ if err != nil {
+ //api.HandleErr(w, r, tx,
http.StatusInternalServerError, nil, errors.New("couldn't convert last updated
time to rfc3339 format: "+err.Error()))
+ return nil, nil, errors.New("couldn't convert
last updated time to rfc3339 format: " + err.Error()),
http.StatusInternalServerError, &maxTime
+ }
+ }
+ topologies[name].Nodes = append(topologies[name].Nodes,
topologyNode)
+ }
+
+ for _, topology := range topologies {
+ nodeMap := map[int]int{}
+ for index, node := range topology.Nodes {
+ nodeMap[node.Id] = index
+ }
+ for _, node := range topology.Nodes {
+ for parentIndex := 0; parentIndex < len(node.Parents);
parentIndex++ {
+ node.Parents[parentIndex] =
nodeMap[node.Parents[parentIndex]]
+ }
+ }
+ interfaces = append(interfaces, *topology)
+ }
+ return interfaces, nil, nil, http.StatusOK, &maxTime
+}
+
+// Read is the handler for reading topologies in api version 5.0.
+func Read(w http.ResponseWriter, r *http.Request) {
+ inf, userErr, sysErr, statusCode := api.NewInfo(r, nil, nil)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, nil, statusCode, userErr, sysErr)
+ return
+ }
+ defer inf.Close()
+ interfaces, userErr, sysErr, statusCode, maxTime := readTopologies(r,
inf.Config.UseIMS)
+ if statusCode == http.StatusNotModified {
+ api.AddLastModifiedHdr(w, *maxTime)
+ w.WriteHeader(http.StatusNotModified)
+ return
+ }
+ if api.SetLastModifiedHeader(r, inf.Config.UseIMS) {
+ date := maxTime.Format(rfc.LastModifiedFormat)
+ w.Header().Add(rfc.LastModified, date)
+ }
+ api.WriteResp(w, r, interfaces)
+ return
+}
+
+// Delete is the handler for removing a topology entity in api version 5.0.
+func Delete(w http.ResponseWriter, r *http.Request) {
+ inf, userErr, sysErr, statusCode := api.NewInfo(r, []string{"name"},
nil)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+ return
+ }
+ defer inf.Close()
+ tx := inf.Tx.Tx
+
+ topologies, userErr, sysErr, statusCode, _ := readTopologies(r, false)
+ if len(topologies) != 1 {
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
errors.New("cannot find exactly 1 topology with the query string provided"),
nil)
+ return
+ }
+
+ topology := topologies[0].(tc.TopologyV5)
+ nodes := DowngradeTopologyNodes(topology.Nodes)
+ userErr, sysErr, statusCode =
checkIfTopologyCanBeAlteredByCurrentUser(inf, nodes)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, statusCode, userErr, sysErr)
+ return
+ }
+
+ where, _, _, queryValues, errs :=
dbhelpers.BuildWhereAndOrderByAndPagination(inf.Params,
map[string]dbhelpers.WhereColumnInfo{
+ "name": {Column: "t.name"},
+ "description": {Column: "t.description"},
+ "lastUpdated": {Column: "t.last_updated"},
+ })
+ if len(errs) > 0 {
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
util.JoinErrs(errs), nil)
+ return
+ }
+
+ query := deleteQueryBase() + where
+ result, err := inf.Tx.NamedExec(query, queryValues)
+ if err != nil {
+ userErr, sysErr, statusCode = api.ParseDBError(err)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr,
sysErr)
+ return
+ }
+ }
+
+ if rowsAffected, err := result.RowsAffected(); err != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("deleting topology: getting rows affected: "+err.Error()))
+ return
+ } else if rowsAffected < 1 {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound,
errors.New("no topology with that key found"), nil)
+ return
+ } else if rowsAffected > 1 {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, fmt.Errorf("topology delete affected too many rows: %d", rowsAffected))
+ return
+ }
+
+ alertsObject := tc.CreateAlerts(tc.SuccessLevel, "topology was
deleted.")
+ api.WriteAlertsObj(w, r, http.StatusOK, alertsObject, topology)
+
+ changeLogMsg := fmt.Sprintf("TOPOLOGY: %s, ACTION: Deleted topology,
keys: {name: %s }", topology.Name, topology.Name)
+ api.CreateChangeLogRawTx(api.ApiChange, changeLogMsg, inf.User, tx)
+}
+
+// Update is the handler for modifying a topology entity in api version 5.0.
+func Update(w http.ResponseWriter, r *http.Request) {
+ inf, userErr, sysErr, statusCode := api.NewInfo(r, []string{"name"},
nil)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+ return
+ }
+ defer inf.Close()
+ tx := inf.Tx.Tx
+
+ topologies, userErr, sysErr, statusCode, _ := readTopologies(r, false)
+ if len(topologies) != 1 {
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
errors.New("cannot find exactly 1 topology with the query string provided"),
nil)
+ return
+ }
+ var topology tc.TopologyV5
+ if err := json.NewDecoder(r.Body).Decode(&topology); err != nil {
+ api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+ return
+ }
+
+ alerts, userErr, sysErr := ValidateTopology(topology, inf)
+ if userErr != nil || sysErr != nil {
+ code := http.StatusBadRequest
+ if sysErr != nil {
+ code = http.StatusInternalServerError
+ }
+ api.HandleErr(w, r, inf.Tx.Tx, code, userErr, sysErr)
+ return
+ }
+
+ requestedName := inf.Params["name"]
+ // check if the entity was already updated
+ userErr, sysErr, statusCode = api.CheckIfUnModifiedByName(r.Header,
inf.Tx, requestedName, "topology")
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, statusCode, userErr, sysErr)
+ return
+ }
+ nodes := DowngradeTopologyNodes(topology.Nodes)
+ userErr, sysErr, statusCode =
checkIfTopologyCanBeAlteredByCurrentUser(inf, nodes)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, statusCode, userErr, sysErr)
+ return
+ }
+
+ oldTopology := topologies[0].(tc.TopologyV5)
+
+ if err := removeParents(tx, oldTopology.Name); err != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError, nil,
err)
+ return
+ }
+
+ var oldNodes, newNodes = map[string]int{}, map[string]int{}
+ for index, node := range oldTopology.Nodes {
+ oldNodes[node.Cachegroup] = index
+ }
+ for index, node := range topology.Nodes {
+ newNodes[node.Cachegroup] = index
+ }
+ var toRemove []string
+ for cachegroupName := range oldNodes {
+ if _, exists := newNodes[cachegroupName]; !exists {
+ toRemove = append(toRemove, cachegroupName)
+ } else {
+ topology.Nodes[newNodes[cachegroupName]].Id =
oldTopology.Nodes[oldNodes[cachegroupName]].Id
+ }
+ }
+
+ if len(toRemove) > 0 {
+ if err := removeNodes(inf.Tx.Tx, oldTopology.Name, &toRemove);
err != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError,
nil, err)
+ return
+ }
+ }
+
+ if userErr, sysErr, statusCode := setTopologyDetails(tx, &topology);
userErr != nil || sysErr != nil {
Review Comment:
These vars don't need to be redeclared.
##########
traffic_ops/traffic_ops_golang/topology/topologies.go:
##########
@@ -419,6 +591,286 @@ func (topology *TOTopology) Create() (error, error, int) {
return nil, nil, 0
}
+func readTopologies(r *http.Request, useIMS bool) ([]interface{}, error,
error, int, *time.Time) {
+ var maxTime time.Time
+ var runSecond bool
+ inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+ if userErr != nil || sysErr != nil {
+ //api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+ return nil, userErr, sysErr, errCode, &maxTime
+ }
+ defer inf.Close()
+
+ interfaces := make([]interface{}, 0)
+
+ queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{
+ "name": {Column: "t.name"},
+ "description": {Column: "t.description"},
+ "lastUpdated": {Column: "t.last_updated"},
+ }
+
+ where, orderBy, pagination, queryValues, errs :=
dbhelpers.BuildWhereAndOrderByAndPagination(inf.Params, queryParamsToQueryCols)
+ if len(errs) > 0 {
+ return nil, util.JoinErrs(errs), nil, http.StatusBadRequest,
&maxTime
+ }
+
+ if useIMS {
+ runSecond, maxTime = ims.TryIfModifiedSinceQuery(inf.Tx,
r.Header, queryValues, selectMaxLastUpdatedQuery(where))
+ if !runSecond {
+ log.Debugln("IMS HIT")
+ return []interface{}{}, nil, nil,
http.StatusNotModified, &maxTime
+ }
+ log.Debugln("IMS MISS")
+ } else {
+ log.Debugln("Non IMS request")
+ }
+
+ // Case where we need to run the second query
+ query := selectQuery() + where + orderBy + pagination
+ rows, err := inf.Tx.NamedQuery(query, queryValues)
+ if err != nil {
+ //api.HandleErr(w, r, tx, http.StatusInternalServerError, nil,
errors.New("topology read: querying: "+err.Error()))
+ return nil, nil, errors.New("topology read: querying: " +
err.Error()), http.StatusInternalServerError, &maxTime
+ }
+ defer log.Close(rows, "unable to close DB connection")
+
+ topologies := map[string]*tc.TopologyV5{}
+ indices := map[int]int{}
+ for index := 0; rows.Next(); index++ {
+ var (
+ name, description string
+ lastUpdated time.Time
+ )
+ topologyNode := tc.TopologyNodeV5{}
+ topologyNode.Parents = []int{}
+ var parents pq.Int64Array
+ if err = rows.Scan(
+ &name,
+ &description,
+ &lastUpdated,
+ &topologyNode.Id,
+ &topologyNode.Cachegroup,
+ &parents,
+ ); err != nil {
+ //api.HandleErr(w, r, tx,
http.StatusInternalServerError, nil, errors.New("topology read: scanning:
"+err.Error()))
+ return nil, nil, errors.New("topology read: scanning: "
+ err.Error()), http.StatusInternalServerError, &maxTime
+ }
+ for _, id := range parents {
+ topologyNode.Parents = append(topologyNode.Parents,
int(id))
+ }
+ indices[topologyNode.Id] = index
+ if _, exists := topologies[name]; !exists {
+ topology := tc.TopologyV5{Nodes: []tc.TopologyNodeV5{}}
+ topologies[name] = &topology
+ topology.Name = name
+ topology.Description = description
+ topology.LastUpdated, err =
util.ConvertTimeFormat(lastUpdated, time.RFC3339)
+ if err != nil {
+ //api.HandleErr(w, r, tx,
http.StatusInternalServerError, nil, errors.New("couldn't convert last updated
time to rfc3339 format: "+err.Error()))
Review Comment:
Commented line should be removed.
##########
traffic_ops/traffic_ops_golang/topology/topologies.go:
##########
@@ -419,6 +591,286 @@ func (topology *TOTopology) Create() (error, error, int) {
return nil, nil, 0
}
+func readTopologies(r *http.Request, useIMS bool) ([]interface{}, error,
error, int, *time.Time) {
+ var maxTime time.Time
+ var runSecond bool
+ inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+ if userErr != nil || sysErr != nil {
+ //api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+ return nil, userErr, sysErr, errCode, &maxTime
+ }
+ defer inf.Close()
+
+ interfaces := make([]interface{}, 0)
+
+ queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{
+ "name": {Column: "t.name"},
+ "description": {Column: "t.description"},
+ "lastUpdated": {Column: "t.last_updated"},
+ }
+
+ where, orderBy, pagination, queryValues, errs :=
dbhelpers.BuildWhereAndOrderByAndPagination(inf.Params, queryParamsToQueryCols)
+ if len(errs) > 0 {
+ return nil, util.JoinErrs(errs), nil, http.StatusBadRequest,
&maxTime
+ }
+
+ if useIMS {
+ runSecond, maxTime = ims.TryIfModifiedSinceQuery(inf.Tx,
r.Header, queryValues, selectMaxLastUpdatedQuery(where))
+ if !runSecond {
+ log.Debugln("IMS HIT")
+ return []interface{}{}, nil, nil,
http.StatusNotModified, &maxTime
+ }
+ log.Debugln("IMS MISS")
+ } else {
+ log.Debugln("Non IMS request")
+ }
+
+ // Case where we need to run the second query
+ query := selectQuery() + where + orderBy + pagination
+ rows, err := inf.Tx.NamedQuery(query, queryValues)
+ if err != nil {
+ //api.HandleErr(w, r, tx, http.StatusInternalServerError, nil,
errors.New("topology read: querying: "+err.Error()))
+ return nil, nil, errors.New("topology read: querying: " +
err.Error()), http.StatusInternalServerError, &maxTime
+ }
+ defer log.Close(rows, "unable to close DB connection")
+
+ topologies := map[string]*tc.TopologyV5{}
+ indices := map[int]int{}
+ for index := 0; rows.Next(); index++ {
+ var (
+ name, description string
+ lastUpdated time.Time
+ )
+ topologyNode := tc.TopologyNodeV5{}
+ topologyNode.Parents = []int{}
+ var parents pq.Int64Array
+ if err = rows.Scan(
+ &name,
+ &description,
+ &lastUpdated,
+ &topologyNode.Id,
+ &topologyNode.Cachegroup,
+ &parents,
+ ); err != nil {
+ //api.HandleErr(w, r, tx,
http.StatusInternalServerError, nil, errors.New("topology read: scanning:
"+err.Error()))
+ return nil, nil, errors.New("topology read: scanning: "
+ err.Error()), http.StatusInternalServerError, &maxTime
+ }
+ for _, id := range parents {
+ topologyNode.Parents = append(topologyNode.Parents,
int(id))
+ }
+ indices[topologyNode.Id] = index
+ if _, exists := topologies[name]; !exists {
+ topology := tc.TopologyV5{Nodes: []tc.TopologyNodeV5{}}
+ topologies[name] = &topology
+ topology.Name = name
+ topology.Description = description
+ topology.LastUpdated, err =
util.ConvertTimeFormat(lastUpdated, time.RFC3339)
+ if err != nil {
+ //api.HandleErr(w, r, tx,
http.StatusInternalServerError, nil, errors.New("couldn't convert last updated
time to rfc3339 format: "+err.Error()))
+ return nil, nil, errors.New("couldn't convert
last updated time to rfc3339 format: " + err.Error()),
http.StatusInternalServerError, &maxTime
+ }
+ }
+ topologies[name].Nodes = append(topologies[name].Nodes,
topologyNode)
+ }
+
+ for _, topology := range topologies {
+ nodeMap := map[int]int{}
+ for index, node := range topology.Nodes {
+ nodeMap[node.Id] = index
+ }
+ for _, node := range topology.Nodes {
+ for parentIndex := 0; parentIndex < len(node.Parents);
parentIndex++ {
+ node.Parents[parentIndex] =
nodeMap[node.Parents[parentIndex]]
+ }
+ }
+ interfaces = append(interfaces, *topology)
+ }
+ return interfaces, nil, nil, http.StatusOK, &maxTime
+}
+
+// Read is the handler for reading topologies in api version 5.0.
+func Read(w http.ResponseWriter, r *http.Request) {
+ inf, userErr, sysErr, statusCode := api.NewInfo(r, nil, nil)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, nil, statusCode, userErr, sysErr)
+ return
+ }
+ defer inf.Close()
+ interfaces, userErr, sysErr, statusCode, maxTime := readTopologies(r,
inf.Config.UseIMS)
+ if statusCode == http.StatusNotModified {
+ api.AddLastModifiedHdr(w, *maxTime)
+ w.WriteHeader(http.StatusNotModified)
+ return
+ }
+ if api.SetLastModifiedHeader(r, inf.Config.UseIMS) {
+ date := maxTime.Format(rfc.LastModifiedFormat)
+ w.Header().Add(rfc.LastModified, date)
+ }
+ api.WriteResp(w, r, interfaces)
+ return
+}
+
+// Delete is the handler for removing a topology entity in api version 5.0.
+func Delete(w http.ResponseWriter, r *http.Request) {
+ inf, userErr, sysErr, statusCode := api.NewInfo(r, []string{"name"},
nil)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+ return
+ }
+ defer inf.Close()
+ tx := inf.Tx.Tx
+
+ topologies, userErr, sysErr, statusCode, _ := readTopologies(r, false)
+ if len(topologies) != 1 {
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
errors.New("cannot find exactly 1 topology with the query string provided"),
nil)
+ return
+ }
+
+ topology := topologies[0].(tc.TopologyV5)
+ nodes := DowngradeTopologyNodes(topology.Nodes)
+ userErr, sysErr, statusCode =
checkIfTopologyCanBeAlteredByCurrentUser(inf, nodes)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, statusCode, userErr, sysErr)
+ return
+ }
+
+ where, _, _, queryValues, errs :=
dbhelpers.BuildWhereAndOrderByAndPagination(inf.Params,
map[string]dbhelpers.WhereColumnInfo{
+ "name": {Column: "t.name"},
+ "description": {Column: "t.description"},
+ "lastUpdated": {Column: "t.last_updated"},
+ })
+ if len(errs) > 0 {
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
util.JoinErrs(errs), nil)
+ return
+ }
+
+ query := deleteQueryBase() + where
+ result, err := inf.Tx.NamedExec(query, queryValues)
+ if err != nil {
+ userErr, sysErr, statusCode = api.ParseDBError(err)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr,
sysErr)
+ return
+ }
+ }
+
+ if rowsAffected, err := result.RowsAffected(); err != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("deleting topology: getting rows affected: "+err.Error()))
+ return
+ } else if rowsAffected < 1 {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound,
errors.New("no topology with that key found"), nil)
+ return
+ } else if rowsAffected > 1 {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, fmt.Errorf("topology delete affected too many rows: %d", rowsAffected))
+ return
+ }
+
+ alertsObject := tc.CreateAlerts(tc.SuccessLevel, "topology was
deleted.")
+ api.WriteAlertsObj(w, r, http.StatusOK, alertsObject, topology)
+
+ changeLogMsg := fmt.Sprintf("TOPOLOGY: %s, ACTION: Deleted topology,
keys: {name: %s }", topology.Name, topology.Name)
+ api.CreateChangeLogRawTx(api.ApiChange, changeLogMsg, inf.User, tx)
+}
+
+// Update is the handler for modifying a topology entity in api version 5.0.
+func Update(w http.ResponseWriter, r *http.Request) {
+ inf, userErr, sysErr, statusCode := api.NewInfo(r, []string{"name"},
nil)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+ return
+ }
+ defer inf.Close()
+ tx := inf.Tx.Tx
+
+ topologies, userErr, sysErr, statusCode, _ := readTopologies(r, false)
+ if len(topologies) != 1 {
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
errors.New("cannot find exactly 1 topology with the query string provided"),
nil)
+ return
+ }
+ var topology tc.TopologyV5
+ if err := json.NewDecoder(r.Body).Decode(&topology); err != nil {
+ api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+ return
+ }
+
+ alerts, userErr, sysErr := ValidateTopology(topology, inf)
+ if userErr != nil || sysErr != nil {
+ code := http.StatusBadRequest
+ if sysErr != nil {
+ code = http.StatusInternalServerError
+ }
+ api.HandleErr(w, r, inf.Tx.Tx, code, userErr, sysErr)
+ return
+ }
+
+ requestedName := inf.Params["name"]
+ // check if the entity was already updated
+ userErr, sysErr, statusCode = api.CheckIfUnModifiedByName(r.Header,
inf.Tx, requestedName, "topology")
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, statusCode, userErr, sysErr)
+ return
+ }
+ nodes := DowngradeTopologyNodes(topology.Nodes)
+ userErr, sysErr, statusCode =
checkIfTopologyCanBeAlteredByCurrentUser(inf, nodes)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, statusCode, userErr, sysErr)
+ return
+ }
+
+ oldTopology := topologies[0].(tc.TopologyV5)
+
+ if err := removeParents(tx, oldTopology.Name); err != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError, nil,
err)
+ return
+ }
+
+ var oldNodes, newNodes = map[string]int{}, map[string]int{}
+ for index, node := range oldTopology.Nodes {
+ oldNodes[node.Cachegroup] = index
+ }
+ for index, node := range topology.Nodes {
+ newNodes[node.Cachegroup] = index
+ }
+ var toRemove []string
+ for cachegroupName := range oldNodes {
+ if _, exists := newNodes[cachegroupName]; !exists {
+ toRemove = append(toRemove, cachegroupName)
+ } else {
+ topology.Nodes[newNodes[cachegroupName]].Id =
oldTopology.Nodes[oldNodes[cachegroupName]].Id
+ }
+ }
+
+ if len(toRemove) > 0 {
+ if err := removeNodes(inf.Tx.Tx, oldTopology.Name, &toRemove);
err != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError,
nil, err)
+ return
+ }
+ }
+
+ if userErr, sysErr, statusCode := setTopologyDetails(tx, &topology);
userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, statusCode, userErr, sysErr)
+ return
+ }
+
+ if userErr, sysErr, statusCode := addNodes(tx, topology.Name,
topology.Nodes); userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, statusCode, userErr, sysErr)
+ return
+ }
+ if userErr, sysErr, statusCode := addParents(tx, topology.Nodes);
userErr != nil || sysErr != nil {
Review Comment:
These vars don't need to be redeclared.
##########
traffic_ops/traffic_ops_golang/topology/topologies.go:
##########
@@ -419,6 +591,286 @@ func (topology *TOTopology) Create() (error, error, int) {
return nil, nil, 0
}
+func readTopologies(r *http.Request, useIMS bool) ([]interface{}, error,
error, int, *time.Time) {
+ var maxTime time.Time
+ var runSecond bool
+ inf, userErr, sysErr, errCode := api.NewInfo(r, nil, nil)
+ if userErr != nil || sysErr != nil {
+ //api.HandleErr(w, r, tx, errCode, userErr, sysErr)
+ return nil, userErr, sysErr, errCode, &maxTime
+ }
+ defer inf.Close()
+
+ interfaces := make([]interface{}, 0)
+
+ queryParamsToQueryCols := map[string]dbhelpers.WhereColumnInfo{
+ "name": {Column: "t.name"},
+ "description": {Column: "t.description"},
+ "lastUpdated": {Column: "t.last_updated"},
+ }
+
+ where, orderBy, pagination, queryValues, errs :=
dbhelpers.BuildWhereAndOrderByAndPagination(inf.Params, queryParamsToQueryCols)
+ if len(errs) > 0 {
+ return nil, util.JoinErrs(errs), nil, http.StatusBadRequest,
&maxTime
+ }
+
+ if useIMS {
+ runSecond, maxTime = ims.TryIfModifiedSinceQuery(inf.Tx,
r.Header, queryValues, selectMaxLastUpdatedQuery(where))
+ if !runSecond {
+ log.Debugln("IMS HIT")
+ return []interface{}{}, nil, nil,
http.StatusNotModified, &maxTime
+ }
+ log.Debugln("IMS MISS")
+ } else {
+ log.Debugln("Non IMS request")
+ }
+
+ // Case where we need to run the second query
+ query := selectQuery() + where + orderBy + pagination
+ rows, err := inf.Tx.NamedQuery(query, queryValues)
+ if err != nil {
+ //api.HandleErr(w, r, tx, http.StatusInternalServerError, nil,
errors.New("topology read: querying: "+err.Error()))
+ return nil, nil, errors.New("topology read: querying: " +
err.Error()), http.StatusInternalServerError, &maxTime
+ }
+ defer log.Close(rows, "unable to close DB connection")
+
+ topologies := map[string]*tc.TopologyV5{}
+ indices := map[int]int{}
+ for index := 0; rows.Next(); index++ {
+ var (
+ name, description string
+ lastUpdated time.Time
+ )
+ topologyNode := tc.TopologyNodeV5{}
+ topologyNode.Parents = []int{}
+ var parents pq.Int64Array
+ if err = rows.Scan(
+ &name,
+ &description,
+ &lastUpdated,
+ &topologyNode.Id,
+ &topologyNode.Cachegroup,
+ &parents,
+ ); err != nil {
+ //api.HandleErr(w, r, tx,
http.StatusInternalServerError, nil, errors.New("topology read: scanning:
"+err.Error()))
+ return nil, nil, errors.New("topology read: scanning: "
+ err.Error()), http.StatusInternalServerError, &maxTime
+ }
+ for _, id := range parents {
+ topologyNode.Parents = append(topologyNode.Parents,
int(id))
+ }
+ indices[topologyNode.Id] = index
+ if _, exists := topologies[name]; !exists {
+ topology := tc.TopologyV5{Nodes: []tc.TopologyNodeV5{}}
+ topologies[name] = &topology
+ topology.Name = name
+ topology.Description = description
+ topology.LastUpdated, err =
util.ConvertTimeFormat(lastUpdated, time.RFC3339)
+ if err != nil {
+ //api.HandleErr(w, r, tx,
http.StatusInternalServerError, nil, errors.New("couldn't convert last updated
time to rfc3339 format: "+err.Error()))
+ return nil, nil, errors.New("couldn't convert
last updated time to rfc3339 format: " + err.Error()),
http.StatusInternalServerError, &maxTime
+ }
+ }
+ topologies[name].Nodes = append(topologies[name].Nodes,
topologyNode)
+ }
+
+ for _, topology := range topologies {
+ nodeMap := map[int]int{}
+ for index, node := range topology.Nodes {
+ nodeMap[node.Id] = index
+ }
+ for _, node := range topology.Nodes {
+ for parentIndex := 0; parentIndex < len(node.Parents);
parentIndex++ {
+ node.Parents[parentIndex] =
nodeMap[node.Parents[parentIndex]]
+ }
+ }
+ interfaces = append(interfaces, *topology)
+ }
+ return interfaces, nil, nil, http.StatusOK, &maxTime
+}
+
+// Read is the handler for reading topologies in api version 5.0.
+func Read(w http.ResponseWriter, r *http.Request) {
+ inf, userErr, sysErr, statusCode := api.NewInfo(r, nil, nil)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, nil, statusCode, userErr, sysErr)
+ return
+ }
+ defer inf.Close()
+ interfaces, userErr, sysErr, statusCode, maxTime := readTopologies(r,
inf.Config.UseIMS)
+ if statusCode == http.StatusNotModified {
+ api.AddLastModifiedHdr(w, *maxTime)
+ w.WriteHeader(http.StatusNotModified)
+ return
+ }
+ if api.SetLastModifiedHeader(r, inf.Config.UseIMS) {
+ date := maxTime.Format(rfc.LastModifiedFormat)
+ w.Header().Add(rfc.LastModified, date)
+ }
+ api.WriteResp(w, r, interfaces)
+ return
+}
+
+// Delete is the handler for removing a topology entity in api version 5.0.
+func Delete(w http.ResponseWriter, r *http.Request) {
+ inf, userErr, sysErr, statusCode := api.NewInfo(r, []string{"name"},
nil)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+ return
+ }
+ defer inf.Close()
+ tx := inf.Tx.Tx
+
+ topologies, userErr, sysErr, statusCode, _ := readTopologies(r, false)
+ if len(topologies) != 1 {
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
errors.New("cannot find exactly 1 topology with the query string provided"),
nil)
+ return
+ }
+
+ topology := topologies[0].(tc.TopologyV5)
+ nodes := DowngradeTopologyNodes(topology.Nodes)
+ userErr, sysErr, statusCode =
checkIfTopologyCanBeAlteredByCurrentUser(inf, nodes)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, statusCode, userErr, sysErr)
+ return
+ }
+
+ where, _, _, queryValues, errs :=
dbhelpers.BuildWhereAndOrderByAndPagination(inf.Params,
map[string]dbhelpers.WhereColumnInfo{
+ "name": {Column: "t.name"},
+ "description": {Column: "t.description"},
+ "lastUpdated": {Column: "t.last_updated"},
+ })
+ if len(errs) > 0 {
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
util.JoinErrs(errs), nil)
+ return
+ }
+
+ query := deleteQueryBase() + where
+ result, err := inf.Tx.NamedExec(query, queryValues)
+ if err != nil {
+ userErr, sysErr, statusCode = api.ParseDBError(err)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr,
sysErr)
+ return
+ }
+ }
+
+ if rowsAffected, err := result.RowsAffected(); err != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, errors.New("deleting topology: getting rows affected: "+err.Error()))
+ return
+ } else if rowsAffected < 1 {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusNotFound,
errors.New("no topology with that key found"), nil)
+ return
+ } else if rowsAffected > 1 {
+ api.HandleErr(w, r, inf.Tx.Tx, http.StatusInternalServerError,
nil, fmt.Errorf("topology delete affected too many rows: %d", rowsAffected))
+ return
+ }
+
+ alertsObject := tc.CreateAlerts(tc.SuccessLevel, "topology was
deleted.")
+ api.WriteAlertsObj(w, r, http.StatusOK, alertsObject, topology)
+
+ changeLogMsg := fmt.Sprintf("TOPOLOGY: %s, ACTION: Deleted topology,
keys: {name: %s }", topology.Name, topology.Name)
+ api.CreateChangeLogRawTx(api.ApiChange, changeLogMsg, inf.User, tx)
+}
+
+// Update is the handler for modifying a topology entity in api version 5.0.
+func Update(w http.ResponseWriter, r *http.Request) {
+ inf, userErr, sysErr, statusCode := api.NewInfo(r, []string{"name"},
nil)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, inf.Tx.Tx, statusCode, userErr, sysErr)
+ return
+ }
+ defer inf.Close()
+ tx := inf.Tx.Tx
+
+ topologies, userErr, sysErr, statusCode, _ := readTopologies(r, false)
+ if len(topologies) != 1 {
+ api.HandleErr(w, r, tx, http.StatusBadRequest,
errors.New("cannot find exactly 1 topology with the query string provided"),
nil)
+ return
+ }
+ var topology tc.TopologyV5
+ if err := json.NewDecoder(r.Body).Decode(&topology); err != nil {
+ api.HandleErr(w, r, tx, http.StatusBadRequest, err, nil)
+ return
+ }
+
+ alerts, userErr, sysErr := ValidateTopology(topology, inf)
+ if userErr != nil || sysErr != nil {
+ code := http.StatusBadRequest
+ if sysErr != nil {
+ code = http.StatusInternalServerError
+ }
+ api.HandleErr(w, r, inf.Tx.Tx, code, userErr, sysErr)
+ return
+ }
+
+ requestedName := inf.Params["name"]
+ // check if the entity was already updated
+ userErr, sysErr, statusCode = api.CheckIfUnModifiedByName(r.Header,
inf.Tx, requestedName, "topology")
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, statusCode, userErr, sysErr)
+ return
+ }
+ nodes := DowngradeTopologyNodes(topology.Nodes)
+ userErr, sysErr, statusCode =
checkIfTopologyCanBeAlteredByCurrentUser(inf, nodes)
+ if userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, statusCode, userErr, sysErr)
+ return
+ }
+
+ oldTopology := topologies[0].(tc.TopologyV5)
+
+ if err := removeParents(tx, oldTopology.Name); err != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError, nil,
err)
+ return
+ }
+
+ var oldNodes, newNodes = map[string]int{}, map[string]int{}
+ for index, node := range oldTopology.Nodes {
+ oldNodes[node.Cachegroup] = index
+ }
+ for index, node := range topology.Nodes {
+ newNodes[node.Cachegroup] = index
+ }
+ var toRemove []string
+ for cachegroupName := range oldNodes {
+ if _, exists := newNodes[cachegroupName]; !exists {
+ toRemove = append(toRemove, cachegroupName)
+ } else {
+ topology.Nodes[newNodes[cachegroupName]].Id =
oldTopology.Nodes[oldNodes[cachegroupName]].Id
+ }
+ }
+
+ if len(toRemove) > 0 {
+ if err := removeNodes(inf.Tx.Tx, oldTopology.Name, &toRemove);
err != nil {
+ api.HandleErr(w, r, tx, http.StatusInternalServerError,
nil, err)
+ return
+ }
+ }
+
+ if userErr, sysErr, statusCode := setTopologyDetails(tx, &topology);
userErr != nil || sysErr != nil {
+ api.HandleErr(w, r, tx, statusCode, userErr, sysErr)
+ return
+ }
+
+ if userErr, sysErr, statusCode := addNodes(tx, topology.Name,
topology.Nodes); userErr != nil || sysErr != nil {
Review Comment:
These vars don't need to be redeclared.
##########
traffic_ops/testing/api/v5/topologies_test.go:
##########
@@ -470,42 +469,42 @@ func TestTopologies(t *testing.T) {
topology := testCase.RequestBody
switch method {
- case "GET", "GET AFTER CHANGES":
- t.Run(name, func(t *testing.T) {
- resp, reqInf, err :=
testCase.ClientSession.GetTopologies(testCase.RequestOpts)
- for _, check := range
testCase.Expectations {
- check(t,
reqInf, resp.Response, resp.Alerts, err)
- }
- })
+ //case "GET", "GET AFTER CHANGES":
+ // t.Run(name, func(t *testing.T) {
+ // resp, reqInf, err :=
testCase.ClientSession.GetTopologies(testCase.RequestOpts)
+ // for _, check := range
testCase.Expectations {
+ // check(t,
reqInf, resp.Response, resp.Alerts, err)
+ // }
+ // })
case "POST":
t.Run(name, func(t *testing.T) {
resp, reqInf, err :=
testCase.ClientSession.CreateTopology(topology, testCase.RequestOpts)
for _, check := range
testCase.Expectations {
check(t,
reqInf, resp.Response, resp.Alerts, err)
}
})
- case "PUT":
- t.Run(name, func(t *testing.T) {
- if _, ok :=
testCase.RequestOpts.QueryParameters["name"]; !ok {
- t.Fatalf("Query
Parameter: \"name\" is required for PUT method tests.")
- }
- resp, reqInf, err :=
testCase.ClientSession.UpdateTopology(testCase.RequestOpts.QueryParameters["name"][0],
topology, testCase.RequestOpts)
- for _, check := range
testCase.Expectations {
- check(t,
reqInf, resp.Response, resp.Alerts, err)
- }
- })
- case "DELETE":
- t.Run(name, func(t *testing.T) {
- if _, ok :=
testCase.RequestOpts.QueryParameters["name"]; !ok {
- t.Fatalf("Query
Parameter: \"name\" is required for DELETE method tests.")
- }
- alerts, reqInf, err :=
testCase.ClientSession.DeleteTopology(testCase.RequestOpts.QueryParameters["name"][0],
testCase.RequestOpts)
- for _, check := range
testCase.Expectations {
- check(t,
reqInf, nil, alerts, err)
- }
- })
+ //case "PUT":
Review Comment:
This test should either not be commented out or the the commented out line
should be removed.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]