feat(agent): add contactedAt attribute to agent
arcad/emissary/pipeline/head Something is wrong with the build of this commit
Details
arcad/emissary/pipeline/head Something is wrong with the build of this commit
Details
This commit is contained in:
parent
d2bcdd2999
commit
d02eb91b11
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/auth"
|
"forge.cadoles.com/Cadoles/emissary/internal/auth"
|
||||||
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
"forge.cadoles.com/Cadoles/emissary/internal/datastore"
|
||||||
|
@ -76,6 +77,13 @@ func (a *Authenticator) Authenticate(ctx context.Context, r *http.Request) (auth
|
||||||
return nil, errors.WithStack(err)
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
contactedAt := time.Now()
|
||||||
|
|
||||||
|
agent, err = a.repo.Update(ctx, agent.ID, datastore.WithAgentUpdateContactedAt(contactedAt))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
|
||||||
user := &User{
|
user := &User{
|
||||||
agent: agent,
|
agent: agent,
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ func agentHints(outputMode format.OutputMode) format.Hints {
|
||||||
format.NewProp("Label", "Label"),
|
format.NewProp("Label", "Label"),
|
||||||
format.NewProp("Thumbprint", "Thumbprint"),
|
format.NewProp("Thumbprint", "Thumbprint"),
|
||||||
format.NewProp("Status", "Status"),
|
format.NewProp("Status", "Status"),
|
||||||
format.NewProp("CreatedAt", "CreatedAt"),
|
format.NewProp("ContactedAt", "ContactedAt"),
|
||||||
format.NewProp("UpdatedAt", "UpdatedAt"),
|
format.NewProp("UpdatedAt", "UpdatedAt"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,14 +20,15 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Agent struct {
|
type Agent struct {
|
||||||
ID AgentID `json:"id"`
|
ID AgentID `json:"id"`
|
||||||
Label string `json:"label"`
|
Label string `json:"label"`
|
||||||
Thumbprint string `json:"thumbprint"`
|
Thumbprint string `json:"thumbprint"`
|
||||||
KeySet *SerializableKeySet `json:"keyset,omitempty"`
|
KeySet *SerializableKeySet `json:"keyset,omitempty"`
|
||||||
Metadata map[string]any `json:"metadata,omitempty"`
|
Metadata map[string]any `json:"metadata,omitempty"`
|
||||||
Status AgentStatus `json:"status"`
|
Status AgentStatus `json:"status"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
|
ContactedAt *time.Time `json:"contactedAt,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SerializableKeySet struct {
|
type SerializableKeySet struct {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package datastore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/lestrrat-go/jwx/v2/jwk"
|
"github.com/lestrrat-go/jwx/v2/jwk"
|
||||||
)
|
)
|
||||||
|
@ -68,11 +69,12 @@ func WithAgentQueryThumbprints(thumbprints ...string) AgentQueryOptionFunc {
|
||||||
type AgentUpdateOptionFunc func(*AgentUpdateOptions)
|
type AgentUpdateOptionFunc func(*AgentUpdateOptions)
|
||||||
|
|
||||||
type AgentUpdateOptions struct {
|
type AgentUpdateOptions struct {
|
||||||
Label *string
|
Label *string
|
||||||
Status *AgentStatus
|
Status *AgentStatus
|
||||||
Metadata *map[string]any
|
ContactedAt *time.Time
|
||||||
KeySet *jwk.Set
|
Metadata *map[string]any
|
||||||
Thumbprint *string
|
KeySet *jwk.Set
|
||||||
|
Thumbprint *string
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithAgentUpdateStatus(status AgentStatus) AgentUpdateOptionFunc {
|
func WithAgentUpdateStatus(status AgentStatus) AgentUpdateOptionFunc {
|
||||||
|
@ -104,3 +106,9 @@ func WithAgentUpdateLabel(label string) AgentUpdateOptionFunc {
|
||||||
opts.Label = &label
|
opts.Label = &label
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func WithAgentUpdateContactedAt(contactedAt time.Time) AgentUpdateOptionFunc {
|
||||||
|
return func(opts *AgentUpdateOptions) {
|
||||||
|
opts.ContactedAt = &contactedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -127,7 +127,7 @@ func (r *AgentRepository) Query(ctx context.Context, opts ...datastore.AgentQuer
|
||||||
count := 0
|
count := 0
|
||||||
|
|
||||||
err := r.withTx(ctx, func(tx *sql.Tx) error {
|
err := r.withTx(ctx, func(tx *sql.Tx) error {
|
||||||
query := `SELECT id, label, thumbprint, status, created_at, updated_at FROM agents`
|
query := `SELECT id, label, thumbprint, status, contacted_at, created_at, updated_at FROM agents`
|
||||||
|
|
||||||
limit := 10
|
limit := 10
|
||||||
if options.Limit != nil {
|
if options.Limit != nil {
|
||||||
|
@ -194,12 +194,16 @@ func (r *AgentRepository) Query(ctx context.Context, opts ...datastore.AgentQuer
|
||||||
agent := &datastore.Agent{}
|
agent := &datastore.Agent{}
|
||||||
|
|
||||||
metadata := JSONMap{}
|
metadata := JSONMap{}
|
||||||
|
contactedAt := sql.NullTime{}
|
||||||
|
|
||||||
if err := rows.Scan(&agent.ID, &agent.Label, &agent.Thumbprint, &agent.Status, &agent.CreatedAt, &agent.UpdatedAt); err != nil {
|
if err := rows.Scan(&agent.ID, &agent.Label, &agent.Thumbprint, &agent.Status, &contactedAt, &agent.CreatedAt, &agent.UpdatedAt); err != nil {
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
agent.Metadata = metadata
|
agent.Metadata = metadata
|
||||||
|
if contactedAt.Valid {
|
||||||
|
agent.ContactedAt = &contactedAt.Time
|
||||||
|
}
|
||||||
|
|
||||||
agents = append(agents, agent)
|
agents = append(agents, agent)
|
||||||
}
|
}
|
||||||
|
@ -315,7 +319,7 @@ func (r *AgentRepository) Get(ctx context.Context, id datastore.AgentID) (*datas
|
||||||
|
|
||||||
err := r.withTx(ctx, func(tx *sql.Tx) error {
|
err := r.withTx(ctx, func(tx *sql.Tx) error {
|
||||||
query := `
|
query := `
|
||||||
SELECT "id", "label", "thumbprint", "keyset", "metadata", "status", "created_at", "updated_at"
|
SELECT "id", "label", "thumbprint", "keyset", "metadata", "status", "contacted_at", "created_at", "updated_at"
|
||||||
FROM agents
|
FROM agents
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
`
|
`
|
||||||
|
@ -323,9 +327,10 @@ func (r *AgentRepository) Get(ctx context.Context, id datastore.AgentID) (*datas
|
||||||
row := r.db.QueryRowContext(ctx, query, id)
|
row := r.db.QueryRowContext(ctx, query, id)
|
||||||
|
|
||||||
metadata := JSONMap{}
|
metadata := JSONMap{}
|
||||||
|
contactedAt := sql.NullTime{}
|
||||||
var rawKeySet []byte
|
var rawKeySet []byte
|
||||||
|
|
||||||
if err := row.Scan(&agent.ID, &agent.Label, &agent.Thumbprint, &rawKeySet, &metadata, &agent.Status, &agent.CreatedAt, &agent.UpdatedAt); err != nil {
|
if err := row.Scan(&agent.ID, &agent.Label, &agent.Thumbprint, &rawKeySet, &metadata, &agent.Status, &contactedAt, &agent.CreatedAt, &agent.UpdatedAt); err != nil {
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return datastore.ErrNotFound
|
return datastore.ErrNotFound
|
||||||
}
|
}
|
||||||
|
@ -334,6 +339,9 @@ func (r *AgentRepository) Get(ctx context.Context, id datastore.AgentID) (*datas
|
||||||
}
|
}
|
||||||
|
|
||||||
agent.Metadata = metadata
|
agent.Metadata = metadata
|
||||||
|
if contactedAt.Valid {
|
||||||
|
agent.ContactedAt = &contactedAt.Time
|
||||||
|
}
|
||||||
|
|
||||||
keySet := jwk.NewSet()
|
keySet := jwk.NewSet()
|
||||||
if err := json.Unmarshal(rawKeySet, &keySet); err != nil {
|
if err := json.Unmarshal(rawKeySet, &keySet); err != nil {
|
||||||
|
@ -362,15 +370,11 @@ func (r *AgentRepository) Update(ctx context.Context, id datastore.AgentID, opts
|
||||||
|
|
||||||
err := r.withTx(ctx, func(tx *sql.Tx) error {
|
err := r.withTx(ctx, func(tx *sql.Tx) error {
|
||||||
query := `
|
query := `
|
||||||
UPDATE agents SET updated_at = $2
|
UPDATE agents SET id = $1
|
||||||
`
|
`
|
||||||
|
|
||||||
now := time.Now().UTC()
|
args := []any{id}
|
||||||
|
index := 2
|
||||||
args := []any{
|
|
||||||
id, now,
|
|
||||||
}
|
|
||||||
index := 3
|
|
||||||
|
|
||||||
if options.Status != nil {
|
if options.Status != nil {
|
||||||
query += fmt.Sprintf(`, status = $%d`, index)
|
query += fmt.Sprintf(`, status = $%d`, index)
|
||||||
|
@ -401,23 +405,45 @@ func (r *AgentRepository) Update(ctx context.Context, id datastore.AgentID, opts
|
||||||
index++
|
index++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if options.ContactedAt != nil {
|
||||||
|
query += fmt.Sprintf(`, contacted_at = $%d`, index)
|
||||||
|
utc := options.ContactedAt.UTC()
|
||||||
|
args = append(args, utc)
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
|
||||||
if options.Metadata != nil {
|
if options.Metadata != nil {
|
||||||
query += fmt.Sprintf(`, metadata = $%d`, index)
|
query += fmt.Sprintf(`, metadata = $%d`, index)
|
||||||
args = append(args, JSONMap(*options.Metadata))
|
args = append(args, JSONMap(*options.Metadata))
|
||||||
index++
|
index++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updated := options.Metadata != nil ||
|
||||||
|
options.Status != nil ||
|
||||||
|
options.Label != nil ||
|
||||||
|
options.KeySet != nil ||
|
||||||
|
options.Thumbprint != nil
|
||||||
|
if updated {
|
||||||
|
now := time.Now().UTC()
|
||||||
|
query += fmt.Sprintf(`, updated_at = $%d`, index)
|
||||||
|
args = append(args, now)
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
|
||||||
query += `
|
query += `
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
RETURNING "id", "label", "thumbprint", "keyset", "metadata", "status", "created_at", "updated_at"
|
RETURNING "id", "label", "thumbprint", "keyset", "metadata", "status", "contacted_at", "created_at", "updated_at"
|
||||||
`
|
`
|
||||||
|
|
||||||
|
logger.Debug(ctx, "executing query", logger.F("query", query), logger.F("args", args))
|
||||||
|
|
||||||
row := tx.QueryRowContext(ctx, query, args...)
|
row := tx.QueryRowContext(ctx, query, args...)
|
||||||
|
|
||||||
metadata := JSONMap{}
|
metadata := JSONMap{}
|
||||||
|
contactedAt := sql.NullTime{}
|
||||||
var rawKeySet []byte
|
var rawKeySet []byte
|
||||||
|
|
||||||
if err := row.Scan(&agent.ID, &agent.Label, &agent.Thumbprint, &rawKeySet, &metadata, &agent.Status, &agent.CreatedAt, &agent.UpdatedAt); err != nil {
|
if err := row.Scan(&agent.ID, &agent.Label, &agent.Thumbprint, &rawKeySet, &metadata, &agent.Status, &contactedAt, &agent.CreatedAt, &agent.UpdatedAt); err != nil {
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return datastore.ErrNotFound
|
return datastore.ErrNotFound
|
||||||
}
|
}
|
||||||
|
@ -426,6 +452,9 @@ func (r *AgentRepository) Update(ctx context.Context, id datastore.AgentID, opts
|
||||||
}
|
}
|
||||||
|
|
||||||
agent.Metadata = metadata
|
agent.Metadata = metadata
|
||||||
|
if contactedAt.Valid {
|
||||||
|
agent.ContactedAt = &contactedAt.Time
|
||||||
|
}
|
||||||
|
|
||||||
keySet := jwk.NewSet()
|
keySet := jwk.NewSet()
|
||||||
if err := json.Unmarshal(rawKeySet, &keySet); err != nil {
|
if err := json.Unmarshal(rawKeySet, &keySet); err != nil {
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE agents DROP COLUMN contacted_at;
|
|
@ -0,0 +1 @@
|
||||||
|
ALTER TABLE agents ADD COLUMN contacted_at datetime;
|
Loading…
Reference in New Issue