Add support for machine heartbeat (#1541)

* add the last_heartbeat field

* add heartbeat controller

* add endpoint of heartbeat

* heartbeat integration

* add last_heartbeat to cscli machines list
This commit is contained in:
Thibault "bui" Koechlin 2022-05-19 15:47:27 +02:00 committed by GitHub
parent 4b843d145a
commit fe09737d80
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 434 additions and 29 deletions

View file

@ -134,7 +134,7 @@ Note: This command requires database direct access, so is intended to be run on
Run: func(cmd *cobra.Command, args []string) {
machines, err := dbClient.ListMachines()
if err != nil {
log.Errorf("unable to list blockers: %s", err)
log.Errorf("unable to list machines: %s", err)
}
if csConfig.Cscli.Output == "human" {
table := tablewriter.NewWriter(os.Stdout)
@ -143,7 +143,7 @@ Note: This command requires database direct access, so is intended to be run on
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetAlignment(tablewriter.ALIGN_LEFT)
table.SetHeader([]string{"Name", "IP Address", "Last Update", "Status", "Version"})
table.SetHeader([]string{"Name", "IP Address", "Last Update", "Status", "Version", "Last Heartbeat"})
for _, w := range machines {
var validated string
if w.IsValidated {
@ -151,7 +151,12 @@ Note: This command requires database direct access, so is intended to be run on
} else {
validated = emoji.Prohibited.String()
}
table.Append([]string{w.MachineId, w.IpAddress, w.UpdatedAt.Format(time.RFC3339), validated, w.Version})
lastHeartBeat := time.Now().UTC().Sub(*w.LastHeartbeat)
hbDisplay := lastHeartBeat.Truncate(time.Second).String()
if lastHeartBeat > 2*time.Minute {
hbDisplay = fmt.Sprintf("%s %s", emoji.Warning.String(), lastHeartBeat.Truncate(time.Second).String())
}
table.Append([]string{w.MachineId, w.IpAddress, w.UpdatedAt.Format(time.RFC3339), validated, w.Version, hbDisplay})
}
table.Render()
} else if csConfig.Cscli.Output == "json" {
@ -162,7 +167,7 @@ Note: This command requires database direct access, so is intended to be run on
fmt.Printf("%s", string(x))
} else if csConfig.Cscli.Output == "raw" {
csvwriter := csv.NewWriter(os.Stdout)
err := csvwriter.Write([]string{"machine_id", "ip_address", "updated_at", "validated", "version"})
err := csvwriter.Write([]string{"machine_id", "ip_address", "updated_at", "validated", "version", "last_heartbeat"})
if err != nil {
log.Fatalf("failed to write header: %s", err)
}
@ -173,7 +178,7 @@ Note: This command requires database direct access, so is intended to be run on
} else {
validated = "false"
}
err := csvwriter.Write([]string{w.MachineId, w.IpAddress, w.UpdatedAt.Format(time.RFC3339), validated, w.Version})
err := csvwriter.Write([]string{w.MachineId, w.IpAddress, w.UpdatedAt.Format(time.RFC3339), validated, w.Version, time.Now().UTC().Sub(*w.LastHeartbeat).Truncate(time.Second).String()})
if err != nil {
log.Fatalf("failed to write raw output : %s", err)
}

View file

@ -100,6 +100,9 @@ func runOutput(input chan types.Event, overflow chan types.Event, buckets *leaky
}); err != nil {
return errors.Wrapf(err, "authenticate watcher (%s)", apiConfig.Login)
}
//start the heartbeat service
log.Debugf("Starting HeartBeat service")
Client.HeartBeat.StartHeartBeat(context.Background(), &outputsTomb)
LOOP:
for {
select {

1
go.sum
View file

@ -1035,6 +1035,7 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.9-0.20211216111533-8d383106f7e7 h1:M1gcVrIb2lSn2FIL19DG0+/b8nNVKJ7W7b4WcAGZAYM=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View file

@ -32,6 +32,7 @@ type ApiClient struct {
Auth *AuthService
Metrics *MetricsService
Signal *SignalService
HeartBeat *HeartBeatService
}
type service struct {
@ -56,6 +57,7 @@ func NewClient(config *Config) (*ApiClient, error) {
c.Auth = (*AuthService)(&c.common)
c.Metrics = (*MetricsService)(&c.common)
c.Signal = (*SignalService)(&c.common)
c.HeartBeat = (*HeartBeatService)(&c.common)
return c, nil
}
@ -75,6 +77,8 @@ func NewDefaultClient(URL *url.URL, prefix string, userAgent string, client *htt
c.Auth = (*AuthService)(&c.common)
c.Metrics = (*MetricsService)(&c.common)
c.Signal = (*SignalService)(&c.common)
c.HeartBeat = (*HeartBeatService)(&c.common)
return c, nil
}

View file

@ -0,0 +1,61 @@
package apiclient
import (
"context"
"fmt"
"net/http"
"time"
"github.com/crowdsecurity/crowdsec/pkg/types"
log "github.com/sirupsen/logrus"
tomb "gopkg.in/tomb.v2"
)
type HeartBeatService service
func (h *HeartBeatService) Ping(ctx context.Context) (bool, *Response, error) {
u := fmt.Sprintf("%s/heartbeat", h.client.URLPrefix)
req, err := h.client.NewRequest("GET", u, nil)
if err != nil {
return false, nil, err
}
resp, err := h.client.Do(ctx, req, nil)
if err != nil {
return false, resp, err
}
return true, resp, nil
}
func (h *HeartBeatService) StartHeartBeat(ctx context.Context, t *tomb.Tomb) {
t.Go(func() error {
defer types.CatchPanic("crowdsec/apiClient/heartbeat")
hbTimer := time.NewTicker(1 * time.Minute)
for {
select {
case <-hbTimer.C:
log.Debug("heartbeat: sending heartbeat")
ok, resp, err := h.Ping(ctx)
if err != nil {
log.Errorf("heartbeat error : %s", err)
continue
}
if resp.Response.StatusCode != http.StatusOK {
log.Errorf("heartbeat unexpected return code : %d", resp.Response.StatusCode)
continue
}
if !ok {
log.Errorf("heartbeat returned false")
continue
}
case <-t.Dying():
log.Debugf("heartbeat: stopping")
hbTimer.Stop()
return nil
}
}
})
}

View file

@ -87,6 +87,7 @@ func (c *Controller) NewV1() error {
jwtAuth.DELETE("/alerts", handlerV1.DeleteAlerts)
jwtAuth.DELETE("/decisions", handlerV1.DeleteDecisions)
jwtAuth.DELETE("/decisions/:decision_id", handlerV1.DeleteDecisionById)
jwtAuth.GET("/heartbeat", handlerV1.HeartBeat)
}
apiKeyAuth := groupV1.Group("")

View file

@ -0,0 +1,21 @@
package v1
import (
"net/http"
jwt "github.com/appleboy/gin-jwt/v2"
"github.com/gin-gonic/gin"
)
func (c *Controller) HeartBeat(gctx *gin.Context) {
claims := jwt.ExtractClaims(gctx)
/*TBD : use defines rather than hardcoded key to find back owner*/
machineID := claims["id"].(string)
if err := c.DBClient.UpdateMachineLastHeartBeat(machineID); err != nil {
c.HandleDBErrors(gctx, err)
return
}
gctx.Status(http.StatusOK)
}

View file

@ -0,0 +1,17 @@
package apiserver
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestHeartBeat(t *testing.T) {
lapi := SetupLAPITest(t)
w := lapi.RecordResponse("GET", "/v1/heartbeat", emptyBody)
assert.Equal(t, 200, w.Code)
w = lapi.RecordResponse("POST", "/v1/heartbeat", emptyBody)
assert.Equal(t, 405, w.Code)
}

View file

@ -22,6 +22,8 @@ type Machine struct {
UpdatedAt *time.Time `json:"updated_at,omitempty"`
// LastPush holds the value of the "last_push" field.
LastPush *time.Time `json:"last_push,omitempty"`
// LastHeartbeat holds the value of the "last_heartbeat" field.
LastHeartbeat *time.Time `json:"last_heartbeat,omitempty"`
// MachineId holds the value of the "machineId" field.
MachineId string `json:"machineId,omitempty"`
// Password holds the value of the "password" field.
@ -70,7 +72,7 @@ func (*Machine) scanValues(columns []string) ([]interface{}, error) {
values[i] = new(sql.NullInt64)
case machine.FieldMachineId, machine.FieldPassword, machine.FieldIpAddress, machine.FieldScenarios, machine.FieldVersion, machine.FieldStatus:
values[i] = new(sql.NullString)
case machine.FieldCreatedAt, machine.FieldUpdatedAt, machine.FieldLastPush:
case machine.FieldCreatedAt, machine.FieldUpdatedAt, machine.FieldLastPush, machine.FieldLastHeartbeat:
values[i] = new(sql.NullTime)
default:
return nil, fmt.Errorf("unexpected column %q for type Machine", columns[i])
@ -114,6 +116,13 @@ func (m *Machine) assignValues(columns []string, values []interface{}) error {
m.LastPush = new(time.Time)
*m.LastPush = value.Time
}
case machine.FieldLastHeartbeat:
if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field last_heartbeat", values[i])
} else if value.Valid {
m.LastHeartbeat = new(time.Time)
*m.LastHeartbeat = value.Time
}
case machine.FieldMachineId:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field machineId", values[i])
@ -201,6 +210,10 @@ func (m *Machine) String() string {
builder.WriteString(", last_push=")
builder.WriteString(v.Format(time.ANSIC))
}
if v := m.LastHeartbeat; v != nil {
builder.WriteString(", last_heartbeat=")
builder.WriteString(v.Format(time.ANSIC))
}
builder.WriteString(", machineId=")
builder.WriteString(m.MachineId)
builder.WriteString(", password=<sensitive>")

View file

@ -17,6 +17,8 @@ const (
FieldUpdatedAt = "updated_at"
// FieldLastPush holds the string denoting the last_push field in the database.
FieldLastPush = "last_push"
// FieldLastHeartbeat holds the string denoting the last_heartbeat field in the database.
FieldLastHeartbeat = "last_heartbeat"
// FieldMachineId holds the string denoting the machineid field in the database.
FieldMachineId = "machine_id"
// FieldPassword holds the string denoting the password field in the database.
@ -50,6 +52,7 @@ var Columns = []string{
FieldCreatedAt,
FieldUpdatedAt,
FieldLastPush,
FieldLastHeartbeat,
FieldMachineId,
FieldPassword,
FieldIpAddress,
@ -82,6 +85,10 @@ var (
DefaultLastPush func() time.Time
// UpdateDefaultLastPush holds the default value on update for the "last_push" field.
UpdateDefaultLastPush func() time.Time
// DefaultLastHeartbeat holds the default value on creation for the "last_heartbeat" field.
DefaultLastHeartbeat func() time.Time
// UpdateDefaultLastHeartbeat holds the default value on update for the "last_heartbeat" field.
UpdateDefaultLastHeartbeat func() time.Time
// ScenariosValidator is a validator for the "scenarios" field. It is called by the builders before save.
ScenariosValidator func(string) error
// DefaultIsValidated holds the default value on creation for the "isValidated" field.

View file

@ -114,6 +114,13 @@ func LastPush(v time.Time) predicate.Machine {
})
}
// LastHeartbeat applies equality check predicate on the "last_heartbeat" field. It's identical to LastHeartbeatEQ.
func LastHeartbeat(v time.Time) predicate.Machine {
return predicate.Machine(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldLastHeartbeat), v))
})
}
// MachineId applies equality check predicate on the "machineId" field. It's identical to MachineIdEQ.
func MachineId(v string) predicate.Machine {
return predicate.Machine(func(s *sql.Selector) {
@ -433,6 +440,96 @@ func LastPushNotNil() predicate.Machine {
})
}
// LastHeartbeatEQ applies the EQ predicate on the "last_heartbeat" field.
func LastHeartbeatEQ(v time.Time) predicate.Machine {
return predicate.Machine(func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldLastHeartbeat), v))
})
}
// LastHeartbeatNEQ applies the NEQ predicate on the "last_heartbeat" field.
func LastHeartbeatNEQ(v time.Time) predicate.Machine {
return predicate.Machine(func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldLastHeartbeat), v))
})
}
// LastHeartbeatIn applies the In predicate on the "last_heartbeat" field.
func LastHeartbeatIn(vs ...time.Time) predicate.Machine {
v := make([]interface{}, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Machine(func(s *sql.Selector) {
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(v) == 0 {
s.Where(sql.False())
return
}
s.Where(sql.In(s.C(FieldLastHeartbeat), v...))
})
}
// LastHeartbeatNotIn applies the NotIn predicate on the "last_heartbeat" field.
func LastHeartbeatNotIn(vs ...time.Time) predicate.Machine {
v := make([]interface{}, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Machine(func(s *sql.Selector) {
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(v) == 0 {
s.Where(sql.False())
return
}
s.Where(sql.NotIn(s.C(FieldLastHeartbeat), v...))
})
}
// LastHeartbeatGT applies the GT predicate on the "last_heartbeat" field.
func LastHeartbeatGT(v time.Time) predicate.Machine {
return predicate.Machine(func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldLastHeartbeat), v))
})
}
// LastHeartbeatGTE applies the GTE predicate on the "last_heartbeat" field.
func LastHeartbeatGTE(v time.Time) predicate.Machine {
return predicate.Machine(func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldLastHeartbeat), v))
})
}
// LastHeartbeatLT applies the LT predicate on the "last_heartbeat" field.
func LastHeartbeatLT(v time.Time) predicate.Machine {
return predicate.Machine(func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldLastHeartbeat), v))
})
}
// LastHeartbeatLTE applies the LTE predicate on the "last_heartbeat" field.
func LastHeartbeatLTE(v time.Time) predicate.Machine {
return predicate.Machine(func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldLastHeartbeat), v))
})
}
// LastHeartbeatIsNil applies the IsNil predicate on the "last_heartbeat" field.
func LastHeartbeatIsNil() predicate.Machine {
return predicate.Machine(func(s *sql.Selector) {
s.Where(sql.IsNull(s.C(FieldLastHeartbeat)))
})
}
// LastHeartbeatNotNil applies the NotNil predicate on the "last_heartbeat" field.
func LastHeartbeatNotNil() predicate.Machine {
return predicate.Machine(func(s *sql.Selector) {
s.Where(sql.NotNull(s.C(FieldLastHeartbeat)))
})
}
// MachineIdEQ applies the EQ predicate on the "machineId" field.
func MachineIdEQ(v string) predicate.Machine {
return predicate.Machine(func(s *sql.Selector) {

View file

@ -63,6 +63,20 @@ func (mc *MachineCreate) SetNillableLastPush(t *time.Time) *MachineCreate {
return mc
}
// SetLastHeartbeat sets the "last_heartbeat" field.
func (mc *MachineCreate) SetLastHeartbeat(t time.Time) *MachineCreate {
mc.mutation.SetLastHeartbeat(t)
return mc
}
// SetNillableLastHeartbeat sets the "last_heartbeat" field if the given value is not nil.
func (mc *MachineCreate) SetNillableLastHeartbeat(t *time.Time) *MachineCreate {
if t != nil {
mc.SetLastHeartbeat(*t)
}
return mc
}
// SetMachineId sets the "machineId" field.
func (mc *MachineCreate) SetMachineId(s string) *MachineCreate {
mc.mutation.SetMachineId(s)
@ -235,6 +249,10 @@ func (mc *MachineCreate) defaults() {
v := machine.DefaultLastPush()
mc.mutation.SetLastPush(v)
}
if _, ok := mc.mutation.LastHeartbeat(); !ok {
v := machine.DefaultLastHeartbeat()
mc.mutation.SetLastHeartbeat(v)
}
if _, ok := mc.mutation.IsValidated(); !ok {
v := machine.DefaultIsValidated
mc.mutation.SetIsValidated(v)
@ -311,6 +329,14 @@ func (mc *MachineCreate) createSpec() (*Machine, *sqlgraph.CreateSpec) {
})
_node.LastPush = &value
}
if value, ok := mc.mutation.LastHeartbeat(); ok {
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Value: value,
Column: machine.FieldLastHeartbeat,
})
_node.LastHeartbeat = &value
}
if value, ok := mc.mutation.MachineId(); ok {
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{
Type: field.TypeString,

View file

@ -65,6 +65,18 @@ func (mu *MachineUpdate) ClearLastPush() *MachineUpdate {
return mu
}
// SetLastHeartbeat sets the "last_heartbeat" field.
func (mu *MachineUpdate) SetLastHeartbeat(t time.Time) *MachineUpdate {
mu.mutation.SetLastHeartbeat(t)
return mu
}
// ClearLastHeartbeat clears the value of the "last_heartbeat" field.
func (mu *MachineUpdate) ClearLastHeartbeat() *MachineUpdate {
mu.mutation.ClearLastHeartbeat()
return mu
}
// SetMachineId sets the "machineId" field.
func (mu *MachineUpdate) SetMachineId(s string) *MachineUpdate {
mu.mutation.SetMachineId(s)
@ -273,6 +285,10 @@ func (mu *MachineUpdate) defaults() {
v := machine.UpdateDefaultLastPush()
mu.mutation.SetLastPush(v)
}
if _, ok := mu.mutation.LastHeartbeat(); !ok && !mu.mutation.LastHeartbeatCleared() {
v := machine.UpdateDefaultLastHeartbeat()
mu.mutation.SetLastHeartbeat(v)
}
}
// check runs all checks and user-defined validators on the builder.
@ -342,6 +358,19 @@ func (mu *MachineUpdate) sqlSave(ctx context.Context) (n int, err error) {
Column: machine.FieldLastPush,
})
}
if value, ok := mu.mutation.LastHeartbeat(); ok {
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Value: value,
Column: machine.FieldLastHeartbeat,
})
}
if mu.mutation.LastHeartbeatCleared() {
_spec.Fields.Clear = append(_spec.Fields.Clear, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Column: machine.FieldLastHeartbeat,
})
}
if value, ok := mu.mutation.MachineId(); ok {
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeString,
@ -518,6 +547,18 @@ func (muo *MachineUpdateOne) ClearLastPush() *MachineUpdateOne {
return muo
}
// SetLastHeartbeat sets the "last_heartbeat" field.
func (muo *MachineUpdateOne) SetLastHeartbeat(t time.Time) *MachineUpdateOne {
muo.mutation.SetLastHeartbeat(t)
return muo
}
// ClearLastHeartbeat clears the value of the "last_heartbeat" field.
func (muo *MachineUpdateOne) ClearLastHeartbeat() *MachineUpdateOne {
muo.mutation.ClearLastHeartbeat()
return muo
}
// SetMachineId sets the "machineId" field.
func (muo *MachineUpdateOne) SetMachineId(s string) *MachineUpdateOne {
muo.mutation.SetMachineId(s)
@ -733,6 +774,10 @@ func (muo *MachineUpdateOne) defaults() {
v := machine.UpdateDefaultLastPush()
muo.mutation.SetLastPush(v)
}
if _, ok := muo.mutation.LastHeartbeat(); !ok && !muo.mutation.LastHeartbeatCleared() {
v := machine.UpdateDefaultLastHeartbeat()
muo.mutation.SetLastHeartbeat(v)
}
}
// check runs all checks and user-defined validators on the builder.
@ -819,6 +864,19 @@ func (muo *MachineUpdateOne) sqlSave(ctx context.Context) (_node *Machine, err e
Column: machine.FieldLastPush,
})
}
if value, ok := muo.mutation.LastHeartbeat(); ok {
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Value: value,
Column: machine.FieldLastHeartbeat,
})
}
if muo.mutation.LastHeartbeatCleared() {
_spec.Fields.Clear = append(_spec.Fields.Clear, &sqlgraph.FieldSpec{
Type: field.TypeTime,
Column: machine.FieldLastHeartbeat,
})
}
if value, ok := muo.mutation.MachineId(); ok {
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{
Type: field.TypeString,

View file

@ -155,6 +155,7 @@ var (
{Name: "created_at", Type: field.TypeTime, Nullable: true},
{Name: "updated_at", Type: field.TypeTime, Nullable: true},
{Name: "last_push", Type: field.TypeTime, Nullable: true},
{Name: "last_heartbeat", Type: field.TypeTime, Nullable: true},
{Name: "machine_id", Type: field.TypeString, Unique: true},
{Name: "password", Type: field.TypeString},
{Name: "ip_address", Type: field.TypeString},

View file

@ -5232,26 +5232,27 @@ func (m *EventMutation) ResetEdge(name string) error {
// MachineMutation represents an operation that mutates the Machine nodes in the graph.
type MachineMutation struct {
config
op Op
typ string
id *int
created_at *time.Time
updated_at *time.Time
last_push *time.Time
machineId *string
password *string
ipAddress *string
scenarios *string
version *string
isValidated *bool
status *string
clearedFields map[string]struct{}
alerts map[int]struct{}
removedalerts map[int]struct{}
clearedalerts bool
done bool
oldValue func(context.Context) (*Machine, error)
predicates []predicate.Machine
op Op
typ string
id *int
created_at *time.Time
updated_at *time.Time
last_push *time.Time
last_heartbeat *time.Time
machineId *string
password *string
ipAddress *string
scenarios *string
version *string
isValidated *bool
status *string
clearedFields map[string]struct{}
alerts map[int]struct{}
removedalerts map[int]struct{}
clearedalerts bool
done bool
oldValue func(context.Context) (*Machine, error)
predicates []predicate.Machine
}
var _ ent.Mutation = (*MachineMutation)(nil)
@ -5499,6 +5500,55 @@ func (m *MachineMutation) ResetLastPush() {
delete(m.clearedFields, machine.FieldLastPush)
}
// SetLastHeartbeat sets the "last_heartbeat" field.
func (m *MachineMutation) SetLastHeartbeat(t time.Time) {
m.last_heartbeat = &t
}
// LastHeartbeat returns the value of the "last_heartbeat" field in the mutation.
func (m *MachineMutation) LastHeartbeat() (r time.Time, exists bool) {
v := m.last_heartbeat
if v == nil {
return
}
return *v, true
}
// OldLastHeartbeat returns the old "last_heartbeat" field's value of the Machine entity.
// If the Machine object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *MachineMutation) OldLastHeartbeat(ctx context.Context) (v *time.Time, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldLastHeartbeat is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldLastHeartbeat requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldLastHeartbeat: %w", err)
}
return oldValue.LastHeartbeat, nil
}
// ClearLastHeartbeat clears the value of the "last_heartbeat" field.
func (m *MachineMutation) ClearLastHeartbeat() {
m.last_heartbeat = nil
m.clearedFields[machine.FieldLastHeartbeat] = struct{}{}
}
// LastHeartbeatCleared returns if the "last_heartbeat" field was cleared in this mutation.
func (m *MachineMutation) LastHeartbeatCleared() bool {
_, ok := m.clearedFields[machine.FieldLastHeartbeat]
return ok
}
// ResetLastHeartbeat resets all changes to the "last_heartbeat" field.
func (m *MachineMutation) ResetLastHeartbeat() {
m.last_heartbeat = nil
delete(m.clearedFields, machine.FieldLastHeartbeat)
}
// SetMachineId sets the "machineId" field.
func (m *MachineMutation) SetMachineId(s string) {
m.machineId = &s
@ -5863,7 +5913,7 @@ func (m *MachineMutation) Type() string {
// order to get all numeric fields that were incremented/decremented, call
// AddedFields().
func (m *MachineMutation) Fields() []string {
fields := make([]string, 0, 10)
fields := make([]string, 0, 11)
if m.created_at != nil {
fields = append(fields, machine.FieldCreatedAt)
}
@ -5873,6 +5923,9 @@ func (m *MachineMutation) Fields() []string {
if m.last_push != nil {
fields = append(fields, machine.FieldLastPush)
}
if m.last_heartbeat != nil {
fields = append(fields, machine.FieldLastHeartbeat)
}
if m.machineId != nil {
fields = append(fields, machine.FieldMachineId)
}
@ -5908,6 +5961,8 @@ func (m *MachineMutation) Field(name string) (ent.Value, bool) {
return m.UpdatedAt()
case machine.FieldLastPush:
return m.LastPush()
case machine.FieldLastHeartbeat:
return m.LastHeartbeat()
case machine.FieldMachineId:
return m.MachineId()
case machine.FieldPassword:
@ -5937,6 +5992,8 @@ func (m *MachineMutation) OldField(ctx context.Context, name string) (ent.Value,
return m.OldUpdatedAt(ctx)
case machine.FieldLastPush:
return m.OldLastPush(ctx)
case machine.FieldLastHeartbeat:
return m.OldLastHeartbeat(ctx)
case machine.FieldMachineId:
return m.OldMachineId(ctx)
case machine.FieldPassword:
@ -5981,6 +6038,13 @@ func (m *MachineMutation) SetField(name string, value ent.Value) error {
}
m.SetLastPush(v)
return nil
case machine.FieldLastHeartbeat:
v, ok := value.(time.Time)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetLastHeartbeat(v)
return nil
case machine.FieldMachineId:
v, ok := value.(string)
if !ok {
@ -6069,6 +6133,9 @@ func (m *MachineMutation) ClearedFields() []string {
if m.FieldCleared(machine.FieldLastPush) {
fields = append(fields, machine.FieldLastPush)
}
if m.FieldCleared(machine.FieldLastHeartbeat) {
fields = append(fields, machine.FieldLastHeartbeat)
}
if m.FieldCleared(machine.FieldScenarios) {
fields = append(fields, machine.FieldScenarios)
}
@ -6101,6 +6168,9 @@ func (m *MachineMutation) ClearField(name string) error {
case machine.FieldLastPush:
m.ClearLastPush()
return nil
case machine.FieldLastHeartbeat:
m.ClearLastHeartbeat()
return nil
case machine.FieldScenarios:
m.ClearScenarios()
return nil
@ -6127,6 +6197,9 @@ func (m *MachineMutation) ResetField(name string) error {
case machine.FieldLastPush:
m.ResetLastPush()
return nil
case machine.FieldLastHeartbeat:
m.ResetLastHeartbeat()
return nil
case machine.FieldMachineId:
m.ResetMachineId()
return nil

View file

@ -138,12 +138,18 @@ func init() {
machine.DefaultLastPush = machineDescLastPush.Default.(func() time.Time)
// machine.UpdateDefaultLastPush holds the default value on update for the last_push field.
machine.UpdateDefaultLastPush = machineDescLastPush.UpdateDefault.(func() time.Time)
// machineDescLastHeartbeat is the schema descriptor for last_heartbeat field.
machineDescLastHeartbeat := machineFields[3].Descriptor()
// machine.DefaultLastHeartbeat holds the default value on creation for the last_heartbeat field.
machine.DefaultLastHeartbeat = machineDescLastHeartbeat.Default.(func() time.Time)
// machine.UpdateDefaultLastHeartbeat holds the default value on update for the last_heartbeat field.
machine.UpdateDefaultLastHeartbeat = machineDescLastHeartbeat.UpdateDefault.(func() time.Time)
// machineDescScenarios is the schema descriptor for scenarios field.
machineDescScenarios := machineFields[6].Descriptor()
machineDescScenarios := machineFields[7].Descriptor()
// machine.ScenariosValidator is a validator for the "scenarios" field. It is called by the builders before save.
machine.ScenariosValidator = machineDescScenarios.Validators[0].(func(string) error)
// machineDescIsValidated is the schema descriptor for isValidated field.
machineDescIsValidated := machineFields[8].Descriptor()
machineDescIsValidated := machineFields[9].Descriptor()
// machine.DefaultIsValidated holds the default value on creation for the isValidated field.
machine.DefaultIsValidated = machineDescIsValidated.Default.(bool)
metaFields := schema.Meta{}.Fields()

View file

@ -24,6 +24,9 @@ func (Machine) Fields() []ent.Field {
field.Time("last_push").
Default(types.UtcNow).
UpdateDefault(types.UtcNow).Nillable().Optional(),
field.Time("last_heartbeat").
Default(types.UtcNow).
UpdateDefault(types.UtcNow).Nillable().Optional(),
field.String("machineId").Unique(),
field.String("password").Sensitive(),
field.String("ipAddress"),

View file

@ -118,6 +118,14 @@ func (c *Client) UpdateMachineLastPush(machineID string) error {
return nil
}
func (c *Client) UpdateMachineLastHeartBeat(machineID string) error {
_, err := c.Ent.Machine.Update().Where(machine.MachineIdEQ(machineID)).SetLastHeartbeat(time.Now().UTC()).Save(c.CTX)
if err != nil {
return errors.Wrapf(UpdateFail, "updating machine last_heartbeat: %s", err)
}
return nil
}
func (c *Client) UpdateMachineScenarios(scenarios string, ID int) error {
_, err := c.Ent.Machine.UpdateOneID(ID).
SetUpdatedAt(time.Now().UTC()).