Skip to content

Commit

Permalink
Add events endpoint to be able to batch them (#269)
Browse files Browse the repository at this point in the history
  • Loading branch information
majst01 authored Apr 21, 2022
1 parent eee5304 commit 7e935d6
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 47 deletions.
92 changes: 66 additions & 26 deletions cmd/metal-api/internal/service/machine-service.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/metal-stack/metal-lib/httperrors"
"github.com/metal-stack/metal-lib/pkg/tag"
"github.com/metal-stack/metal-lib/zapup"
"go.uber.org/multierr"
"go.uber.org/zap"

mdmv1 "github.com/metal-stack/masterdata-api/api/v1"
Expand Down Expand Up @@ -328,6 +329,16 @@ func (r machineResource) webService() *restful.WebService {
Returns(http.StatusOK, "OK", v1.MachineRecentProvisioningEvents{}).
DefaultReturns("Error", httperrors.HTTPErrorResponse{}))

ws.Route(ws.POST("/event").
To(editor(r.addProvisioningEvents)).
Operation("addProvisioningEvents").
Doc("adds machine provisioning events").
Metadata(restfulspec.KeyOpenAPITags, tags).
Reads(v1.MachineProvisioningEvents{}).
Writes(v1.MachineRecentProvisioningEventsResponse{}).
Returns(http.StatusOK, "OK", v1.MachineRecentProvisioningEventsResponse{}).
DefaultReturns("Error", httperrors.HTTPErrorResponse{}))

ws.Route(ws.POST("/{id}/power/on").
To(editor(r.machineOn)).
Operation("machineOn").
Expand Down Expand Up @@ -2046,52 +2057,81 @@ func (r machineResource) getProvisioningEventContainer(request *restful.Request,
return
}
}
func (r machineResource) addProvisioningEvents(request *restful.Request, response *restful.Response) {
var requestPayload v1.MachineProvisioningEvents
err := request.ReadEntity(&requestPayload)
if checkError(request, response, utils.CurrentFuncName(), err) {
return
}

var provisionError error
result := v1.MachineRecentProvisioningEventsResponse{}
for machineID, event := range requestPayload {
_, err := r.addProvisionEventForMachine(machineID, event)
if err != nil {
result.Failed = append(result.Failed, machineID)
provisionError = multierr.Append(provisionError, fmt.Errorf("unable to add provisioning event for machine:%s %w", machineID, err))
continue
}
result.Events++
}
if checkError(request, response, utils.CurrentFuncName(), provisionError) {
return
}

err = response.WriteHeaderAndEntity(http.StatusOK, result)
if err != nil {
zapup.MustRootLogger().Error("Failed to send response", zap.Error(err))
return
}
}
func (r machineResource) addProvisioningEvent(request *restful.Request, response *restful.Response) {
var requestPayload v1.MachineProvisioningEvent
err := request.ReadEntity(&requestPayload)
if checkError(request, response, utils.CurrentFuncName(), err) {
return
}

id := request.PathParameter("id")
m, err := r.ds.FindMachineByID(id)

ec, err := r.addProvisionEventForMachine(id, requestPayload)
if checkError(request, response, utils.CurrentFuncName(), err) {
return
}

err = response.WriteHeaderAndEntity(http.StatusOK, v1.NewMachineRecentProvisioningEvents(ec))
if err != nil {
zapup.MustRootLogger().Error("Failed to send response", zap.Error(err))
return
}
}

func (r machineResource) addProvisionEventForMachine(machineID string, e v1.MachineProvisioningEvent) (*metal.ProvisioningEventContainer, error) {
m, err := r.ds.FindMachineByID(machineID)
if err != nil && !metal.IsNotFound(err) {
if checkError(request, response, utils.CurrentFuncName(), err) {
return
}
return nil, err
}

// an event can actually create an empty machine. This enables us to also catch the very first PXE Booting event
// in a machine lifecycle
if m == nil {
m = &metal.Machine{
Base: metal.Base{
ID: id,
ID: machineID,
},
}
err = r.ds.CreateMachine(m)
if checkError(request, response, utils.CurrentFuncName(), err) {
return
if err != nil {
return nil, err
}
}

var requestPayload v1.MachineProvisioningEvent
err = request.ReadEntity(&requestPayload)
if checkError(request, response, utils.CurrentFuncName(), err) {
return
}
ok := metal.AllProvisioningEventTypes[metal.ProvisioningEventType(requestPayload.Event)]
ok := metal.AllProvisioningEventTypes[metal.ProvisioningEventType(e.Event)]
if !ok {
if checkError(request, response, utils.CurrentFuncName(), errors.New("unknown provisioning event")) {
return
}
return nil, errors.New("unknown provisioning event")
}

ec, err := r.provisioningEventForMachine(id, requestPayload)
if checkError(request, response, utils.CurrentFuncName(), err) {
return
}

err = response.WriteHeaderAndEntity(http.StatusOK, v1.NewMachineRecentProvisioningEvents(ec))
if err != nil {
zapup.MustRootLogger().Error("Failed to send response", zap.Error(err))
return
}
return r.provisioningEventForMachine(machineID, e)
}

func (r machineResource) provisioningEventForMachine(machineID string, e v1.MachineProvisioningEvent) (*metal.ProvisioningEventContainer, error) {
Expand Down
35 changes: 35 additions & 0 deletions cmd/metal-api/internal/service/machine-service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,41 @@ func TestAddProvisioningEvent(t *testing.T) {
}
}

func TestAddProvisioningEvents(t *testing.T) {
ds, mock := datastore.InitMockDB()
testdata.InitMockDBData(mock)

machineservice, err := NewMachine(ds, &emptyPublisher{}, bus.DirectEndpoints(), ipam.New(goipam.New()), nil, nil, nil, nil, 0)
require.NoError(t, err)

machineID := "2"
container := restful.NewContainer().Add(machineservice)
preparing := string(metal.ProvisioningEventPreparing)
events := v1.MachineProvisioningEvents{}
events[machineID] = v1.MachineProvisioningEvent{
Event: preparing,
Message: "starting metal-hammer",
}
js, err := json.Marshal(events)
require.NoError(t, err)
body := bytes.NewBuffer(js)
req := httptest.NewRequest("POST", "/v1/machine/event", body)
container = injectEditor(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.StatusOK, resp.StatusCode, w.Body.String())
var result v1.MachineRecentProvisioningEventsResponse
err = json.NewDecoder(resp.Body).Decode(&result)

require.NoError(t, err)
require.Equal(t, uint64(1), result.Events)
require.Equal(t, []string(nil), result.Failed)
}

func TestOnMachine(t *testing.T) {
tests := []struct {
cmd metal.MachineCommand
Expand Down
7 changes: 7 additions & 0 deletions cmd/metal-api/internal/service/v1/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,19 @@ type MachineRecentProvisioningEvents struct {
IncompleteProvisioningCycles string `json:"incomplete_provisioning_cycles" description:"the amount of incomplete provisioning cycles in the event container"`
}

type MachineRecentProvisioningEventsResponse struct {
Events uint64 `json:"events" description:"number of events stored"`
Failed []string `json:"failed" description:"slice of machineIDs for which event was not published"`
}

type MachineProvisioningEvent struct {
Time time.Time `json:"time" description:"the time that this event was received" optional:"true" readOnly:"true"`
Event string `json:"event" description:"the event emitted by the machine"`
Message string `json:"message" description:"an additional message to add to the event" optional:"true"`
}

type MachineProvisioningEvents map[string]MachineProvisioningEvent

type MachineNics []MachineNic

type MachineNic struct {
Expand Down
14 changes: 6 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ require (
github.com/emicklei/go-restful-openapi/v2 v2.8.0
github.com/emicklei/go-restful/v3 v3.7.4
github.com/go-logr/zapr v1.2.3
github.com/go-openapi/spec v0.20.4
github.com/go-openapi/spec v0.20.5
github.com/go-stack/stack v1.8.1
github.com/google/go-cmp v0.5.7
github.com/google/uuid v1.3.0
Expand All @@ -26,7 +26,8 @@ require (
github.com/spf13/cobra v1.4.0
github.com/spf13/viper v1.10.1
github.com/stretchr/testify v1.7.1
github.com/testcontainers/testcontainers-go v0.12.0
github.com/testcontainers/testcontainers-go v0.13.0
go.uber.org/multierr v1.8.0
go.uber.org/zap v1.21.0
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
Expand All @@ -39,13 +40,11 @@ require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/Microsoft/hcsshim v0.9.2 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 // indirect
github.com/avast/retry-go v3.0.0+incompatible // indirect
github.com/benbjohnson/clock v1.1.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/containerd/cgroups v1.0.3 // indirect
github.com/containerd/containerd v1.6.1 // indirect
Expand All @@ -60,7 +59,7 @@ require (
github.com/go-logr/logr v1.2.2 // indirect
github.com/go-openapi/errors v0.19.6 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.0 // indirect
github.com/go-openapi/runtime v0.20.0 // indirect
github.com/go-openapi/strfmt v0.19.5 // indirect
github.com/go-openapi/swag v0.19.15 // indirect
Expand Down Expand Up @@ -92,7 +91,7 @@ require (
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/moby/sys/mount v0.2.0 // indirect
github.com/moby/sys/mountinfo v0.5.0 // indirect
github.com/moby/term v0.0.0-20210610120745-9d4ed1856297 // indirect
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/morikuni/aec v1.0.0 // indirect
Expand All @@ -116,7 +115,6 @@ require (
go.mongodb.org/mongo-driver v1.8.3 // indirect
go.opencensus.io v0.23.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 // indirect
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
Expand Down
Loading

0 comments on commit 7e935d6

Please sign in to comment.