Files
threshold/internal/servers/repository.go
T

153 lines
4.3 KiB
Go
Raw Normal View History

2020-08-12 00:40:39 -04:00
package servers
import (
"context"
"errors"
"fmt"
"time"
"github.com/hashicorp/boundary/globals"
2020-08-12 00:40:39 -04:00
"github.com/hashicorp/boundary/internal/db"
2020-08-19 16:47:18 -04:00
"github.com/hashicorp/boundary/internal/kms"
2020-08-12 00:40:39 -04:00
"github.com/hashicorp/boundary/internal/types/resource"
)
const (
defaultLiveness = 15 * time.Second
)
2020-09-03 13:24:34 -04:00
type ServerType string
const (
ServerTypeController ServerType = "controller"
ServerTypeWorker ServerType = "worker"
)
func (s ServerType) String() string {
return string(s)
}
// Repository is the servers database repository
2020-08-12 00:40:39 -04:00
type Repository struct {
2020-08-19 16:47:18 -04:00
reader db.Reader
writer db.Writer
kms *kms.Kms
2020-08-12 00:40:39 -04:00
}
// NewRepository creates a new servers Repository. Supports the options: WithLimit
2020-08-12 00:40:39 -04:00
// which sets a default limit on results returned by repo operations.
func NewRepository(r db.Reader, w db.Writer, kms *kms.Kms) (*Repository, error) {
2020-08-12 00:40:39 -04:00
if r == nil {
2020-08-19 16:47:18 -04:00
return nil, errors.New("error creating server repository with nil reader")
2020-08-12 00:40:39 -04:00
}
if w == nil {
2020-08-19 16:47:18 -04:00
return nil, errors.New("error creating server repository with nil writer")
2020-08-12 00:40:39 -04:00
}
2020-08-19 16:47:18 -04:00
if kms == nil {
return nil, errors.New("error creating server repository with nil kms")
2020-08-12 00:40:39 -04:00
}
return &Repository{
2020-08-19 16:47:18 -04:00
reader: r,
writer: w,
kms: kms,
2020-08-12 00:40:39 -04:00
}, nil
}
// list will return a listing of resources and honor the WithLimit option or the
// repo defaultLimit
2020-09-03 13:24:34 -04:00
func (r *Repository) ListServers(ctx context.Context, serverType ServerType, opt ...Option) ([]*Server, error) {
2020-08-12 00:40:39 -04:00
opts := getOpts(opt...)
liveness := opts.withLiveness
if liveness == 0 {
liveness = defaultLiveness
}
updateTime := time.Now().Add(-1 * liveness)
2020-08-24 14:10:41 -04:00
var servers []*Server
if err := r.reader.SearchWhere(
ctx,
&servers,
"type = $1 and update_time > $2",
[]interface{}{serverType, updateTime.Format(time.RFC3339)},
db.WithLimit(-1),
); err != nil {
return nil, fmt.Errorf("error listing servers: %w", err)
2020-08-12 00:40:39 -04:00
}
2020-08-24 14:10:41 -04:00
return servers, nil
2020-08-12 00:40:39 -04:00
}
// UpsertServer adds or updates a server in the DB
func (r *Repository) UpsertServer(ctx context.Context, server *Server, opt ...Option) ([]*Server, int, error) {
2020-08-12 00:40:39 -04:00
if server == nil {
return nil, db.NoRowsAffected, errors.New("cannot update server that is nil")
}
// Ensure, for now at least, the private ID is always equivalent to the name
server.PrivateId = server.Name
// Build query
q := `
2020-08-26 13:15:12 -04:00
insert into server
2020-08-12 00:40:39 -04:00
(private_id, type, name, description, address, update_time)
values
($1, $2, $3, $4, $5, $6)
2020-08-26 13:15:12 -04:00
on conflict on constraint server_pkey
2020-08-12 00:40:39 -04:00
do update set
name = $3,
description = $4,
address = $5,
update_time = $6;
`
rowsAffected, err := r.writer.Exec(ctx, q,
[]interface{}{server.PrivateId,
server.Type,
server.Name,
server.Description,
server.Address,
time.Now().Format(time.RFC3339)})
2020-08-12 00:40:39 -04:00
if err != nil {
return nil, db.NoRowsAffected, fmt.Errorf("error performing status upsert: %w", err)
}
// If updating a controller, done
if server.Type == resource.Controller.String() {
return nil, int(rowsAffected), nil
}
// Fetch current controllers to feed to the workers
2020-09-03 13:24:34 -04:00
controllers, err := r.ListServers(ctx, ServerTypeController)
2020-08-12 00:40:39 -04:00
return controllers, len(controllers), err
}
type RecoveryNonce struct {
Nonce string
}
// AddRecoveryNonce adds a nonce
func (r *Repository) AddRecoveryNonce(ctx context.Context, nonce string, opt ...Option) error {
if nonce == "" {
return errors.New("empty nonce provided")
}
rn := &RecoveryNonce{Nonce: nonce}
if err := r.writer.Create(ctx, rn); err != nil {
return fmt.Errorf("error performing nonce insertion: %w", err)
}
return nil
}
// CleanupNonces removes nonces that no longer need to be stored
func (r *Repository) CleanupNonces(ctx context.Context, opt ...Option) (int, error) {
// If something was inserted before 3x the actual validity period, clean it out
endTime := time.Now().Add(-3 * globals.RecoveryTokenValidityPeriod)
rows, err := r.writer.Delete(ctx, &RecoveryNonce{}, db.WithWhere(deleteWhereSql, endTime))
if err != nil {
return db.NoRowsAffected, fmt.Errorf("error performing nonce cleanup: %w", err)
}
return rows, nil
}
// ListNonces lists nonces. Used only for tests at the moment.
func (r *Repository) ListNonces(ctx context.Context, opt ...Option) ([]*RecoveryNonce, error) {
var nonces []*RecoveryNonce
if err := r.reader.SearchWhere(ctx, &nonces, "", nil, db.WithLimit(-1)); err != nil {
return nil, fmt.Errorf("error listing nonces: %w", err)
}
return nonces, nil
}