Skip to content

Commit

Permalink
do not allow to toggle a port if no machine is connected (#525)
Browse files Browse the repository at this point in the history
  • Loading branch information
ulrichSchreiner authored May 29, 2024
1 parent 0b4cfa2 commit da14bf8
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 2 deletions.
21 changes: 19 additions & 2 deletions cmd/metal-api/internal/service/switch-service.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ func (r *switchResource) webService() *restful.WebService {
Reads(v1.SwitchPortToggleRequest{}).
Returns(http.StatusOK, "OK", v1.SwitchResponse{}).
Returns(http.StatusConflict, "Conflict", httperrors.HTTPErrorResponse{}).
Returns(http.StatusBadRequest, "Bad input data", httperrors.HTTPErrorResponse{}).
DefaultReturns("Error", httperrors.HTTPErrorResponse{}))

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

// 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.
// 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.
// 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.
// 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.
func (r *switchResource) toggleSwitchPort(request *restful.Request, response *restful.Response) {
var requestPayload v1.SwitchPortToggleRequest
err := request.ReadEntity(&requestPayload)
Expand Down Expand Up @@ -345,6 +346,22 @@ func (r *switchResource) toggleSwitchPort(request *restful.Request, response *re
return
}

// now check if there is something connected at the given nic.
machineConnection := false

for _, mcs := range newSwitch.MachineConnections {
for _, mc := range mcs {
if strings.EqualFold(mc.Nic.Name, requestPayload.NicName) {
machineConnection = true
break
}
}
}
if !machineConnection {
r.sendError(request, response, httperrors.BadRequest(fmt.Errorf("switch %q does not have a connected machine at port %q", id, requestPayload.NicName)))
return
}

if updated {
if err := r.ds.UpdateSwitch(oldSwitch, &newSwitch); err != nil {
r.sendError(request, response, defaultError(err))
Expand Down
32 changes: 32 additions & 0 deletions cmd/metal-api/internal/service/switch-service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1386,3 +1386,35 @@ func TestToggleSwitch(t *testing.T) {
require.Equal(t, v1.SwitchPortStatusDown, result.Nics[0].Actual)
require.Equal(t, v1.SwitchPortStatusUnknown, result.Connections[0].Nic.Actual)
}

func TestToggleSwitchNicWithoutMachine(t *testing.T) {
ds, mock := datastore.InitMockDB(t)
testdata.InitMockDBData(mock)
log := slog.Default()

switchservice := NewSwitch(log, ds)
container := restful.NewContainer().Add(switchservice)

updateRequest := v1.SwitchPortToggleRequest{
NicName: testdata.Switch1.Nics[1].Name,
Status: v1.SwitchPortStatusDown,
}

js, err := json.Marshal(updateRequest)
require.NoError(t, err)
body := bytes.NewBuffer(js)
req := httptest.NewRequest("POST", "/v1/switch/"+testdata.Switch1.ID+"/port", body)
container = injectAdmin(log, container, req)
req.Header.Add("Content-Type", "application/json")
w := httptest.NewRecorder()
container.ServeHTTP(w, req)

resp := w.Result()
defer resp.Body.Close()
require.Equal(t, http.StatusBadRequest, resp.StatusCode, w.Body.String())
var result httperrors.HTTPErrorResponse
err = json.NewDecoder(resp.Body).Decode(&result)

require.NoError(t, err)
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))
}
1 change: 1 addition & 0 deletions cmd/metal-api/internal/testdata/testdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,7 @@ var (
"1": metal.Connections{
metal.Connection{
Nic: metal.Nic{
Name: "swp1",
MacAddress: metal.MacAddress("21:11:11:11:11:11"),
},
MachineID: "1",
Expand Down
6 changes: 6 additions & 0 deletions spec/metal-api.json
Original file line number Diff line number Diff line change
Expand Up @@ -9504,6 +9504,12 @@
"$ref": "#/definitions/v1.SwitchResponse"
}
},
"400": {
"description": "Bad input data",
"schema": {
"$ref": "#/definitions/httperrors.HTTPErrorResponse"
}
},
"409": {
"description": "Conflict",
"schema": {
Expand Down

0 comments on commit da14bf8

Please sign in to comment.