open-nomad/nomad/periodic_endpoint.go
Tim Gross f61f801e77
provide RPCContext to all RPC handlers (#15430)
Upcoming work to instrument the rate of RPC requests by consumer (and eventually
rate limit) requires that we thread the `RPCContext` through all RPC
handlers so that we can access the underlying connection. This changeset adds
the context to everywhere we intend to initially support it and intentionally
excludes streaming RPCs and client RPCs.

To improve the ergonomics of adding the context everywhere its needed and to
clarify the requirements of dynamic vs static handlers, I've also done a good
bit of refactoring here:

* canonicalized the RPC handler fields so they're as close to identical as
  possible without introducing unused fields (i.e. I didn't add loggers if the
  handler doesn't use them already).
* canonicalized the imports in the handler files.
* added a `NewExampleEndpoint` function for each handler that ensures we're
  constructing the handlers with the required arguments.
* reordered the registration in server.go to match the order of the files (to
  make it easier to see if we've missed one), and added a bunch of commentary
  there as to what the difference between static and dynamic handlers is.
2022-12-01 10:05:15 -05:00

75 lines
1.9 KiB
Go

package nomad
import (
"fmt"
"time"
"github.com/armon/go-metrics"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-memdb"
"github.com/hashicorp/nomad/acl"
"github.com/hashicorp/nomad/nomad/structs"
)
// Periodic endpoint is used for periodic job interactions
type Periodic struct {
srv *Server
ctx *RPCContext
logger hclog.Logger
}
func NewPeriodicEndpoint(srv *Server, ctx *RPCContext) *Periodic {
return &Periodic{srv: srv, ctx: ctx, logger: srv.logger.Named("periodic")}
}
// Force is used to force a new instance of a periodic job
func (p *Periodic) Force(args *structs.PeriodicForceRequest, reply *structs.PeriodicForceResponse) error {
if done, err := p.srv.forward("Periodic.Force", args, args, reply); done {
return err
}
defer metrics.MeasureSince([]string{"nomad", "periodic", "force"}, time.Now())
// Check for write-job permissions
if aclObj, err := p.srv.ResolveToken(args.AuthToken); err != nil {
return err
} else if aclObj != nil && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilityDispatchJob) && !aclObj.AllowNsOp(args.RequestNamespace(), acl.NamespaceCapabilitySubmitJob) {
return structs.ErrPermissionDenied
}
// Validate the arguments
if args.JobID == "" {
return fmt.Errorf("missing job ID for evaluation")
}
// Lookup the job
snap, err := p.srv.fsm.State().Snapshot()
if err != nil {
return err
}
ws := memdb.NewWatchSet()
job, err := snap.JobByID(ws, args.RequestNamespace(), args.JobID)
if err != nil {
return err
}
if job == nil {
return fmt.Errorf("job not found")
}
if !job.IsPeriodic() {
return fmt.Errorf("can't force launch non-periodic job")
}
// Force run the job.
eval, err := p.srv.periodicDispatcher.ForceRun(args.RequestNamespace(), job.ID)
if err != nil {
return fmt.Errorf("force launch for job %q failed: %v", job.ID, err)
}
reply.EvalID = eval.ID
reply.EvalCreateIndex = eval.CreateIndex
reply.Index = eval.CreateIndex
return nil
}