Skip to content

Commit da14bf8

Browse files
do not allow to toggle a port if no machine is connected (#525)
1 parent 0b4cfa2 commit da14bf8

File tree

4 files changed

+58
-2
lines changed

4 files changed

+58
-2
lines changed

cmd/metal-api/internal/service/switch-service.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ func (r *switchResource) webService() *restful.WebService {
114114
Reads(v1.SwitchPortToggleRequest{}).
115115
Returns(http.StatusOK, "OK", v1.SwitchResponse{}).
116116
Returns(http.StatusConflict, "Conflict", httperrors.HTTPErrorResponse{}).
117+
Returns(http.StatusBadRequest, "Bad input data", httperrors.HTTPErrorResponse{}).
117118
DefaultReturns("Error", httperrors.HTTPErrorResponse{}))
118119

119120
ws.Route(ws.POST("/{id}/notify").
@@ -294,8 +295,8 @@ func (r *switchResource) notifySwitch(request *restful.Request, response *restfu
294295
r.send(request, response, http.StatusOK, v1.NewSwitchNotifyResponse(&newSS))
295296
}
296297

297-
// toggleSwitchPort handles a request to toggle the state of a port on a switch. It reads the request body, validates the requested status is concrete, finds the switch, updates its NIC state if needed, and returns the updated switch on success.
298-
// toggleSwitchPort handles a request to toggle the state of a port on a switch. It reads the request body to get the switch ID, NIC name and desired state. It finds the switch, updates the state of the matching NIC if needed, and returns the updated switch on success.
298+
// toggleSwitchPort handles a request to toggle the state of a port on a switch. It reads the request body, finds the switch, updates its NIC state if needed, and returns the updated switch on success.
299+
// If the given port is not found or the given status is not concrete, a 400 error is returned. Another requirement is that there must be a machine connected to the port.
299300
func (r *switchResource) toggleSwitchPort(request *restful.Request, response *restful.Response) {
300301
var requestPayload v1.SwitchPortToggleRequest
301302
err := request.ReadEntity(&requestPayload)
@@ -345,6 +346,22 @@ func (r *switchResource) toggleSwitchPort(request *restful.Request, response *re
345346
return
346347
}
347348

349+
// now check if there is something connected at the given nic.
350+
machineConnection := false
351+
352+
for _, mcs := range newSwitch.MachineConnections {
353+
for _, mc := range mcs {
354+
if strings.EqualFold(mc.Nic.Name, requestPayload.NicName) {
355+
machineConnection = true
356+
break
357+
}
358+
}
359+
}
360+
if !machineConnection {
361+
r.sendError(request, response, httperrors.BadRequest(fmt.Errorf("switch %q does not have a connected machine at port %q", id, requestPayload.NicName)))
362+
return
363+
}
364+
348365
if updated {
349366
if err := r.ds.UpdateSwitch(oldSwitch, &newSwitch); err != nil {
350367
r.sendError(request, response, defaultError(err))

cmd/metal-api/internal/service/switch-service_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,3 +1386,35 @@ func TestToggleSwitch(t *testing.T) {
13861386
require.Equal(t, v1.SwitchPortStatusDown, result.Nics[0].Actual)
13871387
require.Equal(t, v1.SwitchPortStatusUnknown, result.Connections[0].Nic.Actual)
13881388
}
1389+
1390+
func TestToggleSwitchNicWithoutMachine(t *testing.T) {
1391+
ds, mock := datastore.InitMockDB(t)
1392+
testdata.InitMockDBData(mock)
1393+
log := slog.Default()
1394+
1395+
switchservice := NewSwitch(log, ds)
1396+
container := restful.NewContainer().Add(switchservice)
1397+
1398+
updateRequest := v1.SwitchPortToggleRequest{
1399+
NicName: testdata.Switch1.Nics[1].Name,
1400+
Status: v1.SwitchPortStatusDown,
1401+
}
1402+
1403+
js, err := json.Marshal(updateRequest)
1404+
require.NoError(t, err)
1405+
body := bytes.NewBuffer(js)
1406+
req := httptest.NewRequest("POST", "/v1/switch/"+testdata.Switch1.ID+"/port", body)
1407+
container = injectAdmin(log, container, req)
1408+
req.Header.Add("Content-Type", "application/json")
1409+
w := httptest.NewRecorder()
1410+
container.ServeHTTP(w, req)
1411+
1412+
resp := w.Result()
1413+
defer resp.Body.Close()
1414+
require.Equal(t, http.StatusBadRequest, resp.StatusCode, w.Body.String())
1415+
var result httperrors.HTTPErrorResponse
1416+
err = json.NewDecoder(resp.Body).Decode(&result)
1417+
1418+
require.NoError(t, err)
1419+
require.Equal(t, result.Message, fmt.Sprintf("switch %q does not have a connected machine at port %q", testdata.Switch1.ID, testdata.Switch1.Nics[1].Name))
1420+
}

cmd/metal-api/internal/testdata/testdata.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,7 @@ var (
552552
"1": metal.Connections{
553553
metal.Connection{
554554
Nic: metal.Nic{
555+
Name: "swp1",
555556
MacAddress: metal.MacAddress("21:11:11:11:11:11"),
556557
},
557558
MachineID: "1",

spec/metal-api.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9504,6 +9504,12 @@
95049504
"$ref": "#/definitions/v1.SwitchResponse"
95059505
}
95069506
},
9507+
"400": {
9508+
"description": "Bad input data",
9509+
"schema": {
9510+
"$ref": "#/definitions/httperrors.HTTPErrorResponse"
9511+
}
9512+
},
95079513
"409": {
95089514
"description": "Conflict",
95099515
"schema": {

0 commit comments

Comments
 (0)