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:
parent
4b843d145a
commit
fe09737d80
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
1
go.sum
|
@ -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=
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
61
pkg/apiclient/heartbeat.go
Normal file
61
pkg/apiclient/heartbeat.go
Normal 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
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
|
@ -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("")
|
||||
|
|
21
pkg/apiserver/controllers/v1/heartbeat.go
Normal file
21
pkg/apiserver/controllers/v1/heartbeat.go
Normal 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)
|
||||
}
|
17
pkg/apiserver/heartbeat_test.go
Normal file
17
pkg/apiserver/heartbeat_test.go
Normal 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)
|
||||
}
|
|
@ -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>")
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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()).
|
||||
|
|
Loading…
Reference in a new issue