Documentation
¶
Index ¶
- Variables
- func ListNodeByPublicKey(ctx context.Context, svc nodev1.NodeServiceClient, csaKey string) (*nodev1.Node, error)
- type DeleteJobInput
- type DeleteJobOutput
- type DeleteJobsInput
- type DeleteJobsOutput
- type DisableNodeInput
- type DisableNodeOutput
- type DisableNodesInput
- type DisableNodesOutput
- type JDOpDeps
- type JobSpec
- type NodeToRegister
- type NodeToUpdate
- type ProposeJobInput
- type ProposeJobOutput
- type ProposeJobsInput
- type ProposeJobsOutput
- type RegisterNodeInput
- type RegisterNodeOutput
- type RegisterNodesInput
- type RegisterNodesOutput
- type RevokeJobInput
- type RevokeJobOutput
- type RevokeJobsInput
- type RevokeJobsOutput
- type UpdateNodeInput
- type UpdateNodeOutput
- type UpdateNodesInput
- type UpdateNodesOutput
Constants ¶
This section is empty.
Variables ¶
var OpJDDeleteJob = fwops.NewOperation( "jd-delete-job", semver.MustParse("1.0.0"), "Delete a job", func(b fwops.Bundle, deps JDOpDeps, in DeleteJobInput) (DeleteJobOutput, error) { res, err := deps.Offchain.DeleteJob(b.GetContext(), &jobv1.DeleteJobRequest{ IdOneof: &jobv1.DeleteJobRequest_Id{Id: in.JobID}, }) if err != nil { return DeleteJobOutput{}, fmt.Errorf("failed to delete job %q: %w", in.JobID, err) } if res.GetJob().GetDeletedAt() == nil { return DeleteJobOutput{}, fmt.Errorf("delete job %q response did not confirm deletion", in.JobID) } b.Logger.Infow("deleted job", "job_id", in.JobID) return DeleteJobOutput{}, nil }, )
OpJDDeleteJob deletes a single job.
var OpJDDisableNode = fwops.NewOperation( "jd-disable-node", semver.MustParse("1.0.0"), "Disable a node by CSA key", func(b fwops.Bundle, deps JDOpDeps, in DisableNodeInput) (DisableNodeOutput, error) { ctx := b.GetContext() nodeInfo, err := ListNodeByPublicKey(ctx, deps.Offchain, in.CSAKey) if err != nil { return DisableNodeOutput{}, fmt.Errorf("failed to look up node with csa_key %s: %w", in.CSAKey, err) } if nodeInfo == nil { b.Logger.Infow("node not found, skipping disable", "csa_key", in.CSAKey) return DisableNodeOutput{Skipped: true}, nil } if !nodeInfo.GetIsEnabled() { b.Logger.Infow("node already disabled, skipping", "node_id", nodeInfo.GetId()) return DisableNodeOutput{NodeID: nodeInfo.GetId(), Skipped: true}, nil } if _, err = deps.Offchain.DisableNode(ctx, &nodev1.DisableNodeRequest{Id: nodeInfo.GetId()}); err != nil { return DisableNodeOutput{}, fmt.Errorf("failed to disable node %q (%s): %w", nodeInfo.GetId(), nodeInfo.GetName(), err) } b.Logger.Infow("disabled node", "node_id", nodeInfo.GetId(), "name", nodeInfo.GetName()) return DisableNodeOutput{NodeID: nodeInfo.GetId()}, nil }, )
OpJDDisableNode disables a node by CSA key.
var OpJDProposeJob = fwops.NewOperation( "jd-propose-job", semver.MustParse("1.0.0"), "Propose a job spec to nodes matched by label", func(b fwops.Bundle, deps JDOpDeps, in ProposeJobInput) (ProposeJobOutput, error) { ctx := b.GetContext() d := domain.NewDomain("", in.Domain) domainKey := d.Key() envName := deps.EnvName selectors := make([]*ptypes.Selector, 0, 2+len(in.NodeLabels)) selectors = append(selectors, &ptypes.Selector{Key: "product", Op: ptypes.SelectorOp_EQ, Value: &domainKey}, &ptypes.Selector{Key: "environment", Op: ptypes.SelectorOp_EQ, Value: &envName}, ) for k, v := range in.NodeLabels { selectors = append(selectors, &ptypes.Selector{Key: k, Op: ptypes.SelectorOp_EQ, Value: &v}) } resp, err := deps.Offchain.ListNodes(ctx, &nodev1.ListNodesRequest{ Filter: &nodev1.ListNodesRequest_Filter{Enabled: 1, Selectors: selectors}, }) if err != nil { return ProposeJobOutput{}, fmt.Errorf("failed to list nodes: %w", err) } nodes := resp.GetNodes() if len(nodes) == 0 { return ProposeJobOutput{}, fmt.Errorf("no nodes matched domain=%q env=%q labels=%v", in.Domain, deps.EnvName, in.NodeLabels) } var merr error for _, node := range nodes { if _, err = deps.Offchain.ProposeJob(ctx, &jobv1.ProposeJobRequest{ NodeId: node.GetId(), Spec: in.JobspecTOML, Labels: labelsFromMap(in.JobLabels), }); err != nil { merr = errors.Join(merr, fmt.Errorf("failed to propose job to node %q: %w", node.GetId(), err)) } } if merr != nil { return ProposeJobOutput{}, merr } b.Logger.Infow("proposed job", "node_labels", in.NodeLabels, "node_count", len(nodes)) return ProposeJobOutput{}, nil }, )
OpJDProposeJob proposes a single job spec to nodes matched by label. Fails if no nodes match the domain/environment/label selectors.
var OpJDRegisterNode = fwops.NewOperation( "jd-register-node", semver.MustParse("1.0.0"), "Register a node", func(b fwops.Bundle, deps JDOpDeps, in RegisterNodeInput) (RegisterNodeOutput, error) { ctx := b.GetContext() existing, err := ListNodeByPublicKey(ctx, deps.Offchain, in.CSAKey) if err != nil { return RegisterNodeOutput{}, fmt.Errorf("failed to check if node is already registered: %w", err) } if existing != nil { b.Logger.Infow("node already registered, skipping", "name", in.Name, "csa_key", in.CSAKey) return RegisterNodeOutput{NodeID: existing.GetId(), Skipped: true}, nil } labels := in.Labels if labels == nil { labels = make(map[string]string) } d := domain.NewDomain("", in.Domain) nodeID, err := cldf_engine_offchain.RegisterNode(ctx, deps.Offchain, in.Name, in.CSAKey, in.IsBootstrap, d, deps.EnvName, labels) if err != nil { return RegisterNodeOutput{}, fmt.Errorf("failed to register node %q (csa_key: %s): %w", in.Name, in.CSAKey, err) } b.Logger.Infow("registered node", "name", in.Name, "id", nodeID) return RegisterNodeOutput{NodeID: nodeID}, nil }, )
OpJDRegisterNode registers a single node.
var OpJDRevokeJob = fwops.NewOperation( "jd-revoke-job", semver.MustParse("1.0.0"), "Revoke a job", func(b fwops.Bundle, deps JDOpDeps, in RevokeJobInput) (RevokeJobOutput, error) { _, err := deps.Offchain.RevokeJob(b.GetContext(), &jobv1.RevokeJobRequest{ IdOneof: &jobv1.RevokeJobRequest_Id{Id: in.JobID}, }) if err != nil { if isJDNotFound(err) { b.Logger.Infow("job already absent or revoked", "job_id", in.JobID) return RevokeJobOutput{AlreadyAbsent: true}, nil } return RevokeJobOutput{}, fmt.Errorf("failed to revoke job %q: %w", in.JobID, err) } b.Logger.Infow("revoked job", "job_id", in.JobID) return RevokeJobOutput{}, nil }, )
OpJDRevokeJob revokes a single job.
var OpJDUpdateNode = fwops.NewOperation( "jd-update-node", semver.MustParse("1.0.0"), "Update a node's name and/or labels", func(b fwops.Bundle, deps JDOpDeps, in UpdateNodeInput) (UpdateNodeOutput, error) { ctx := b.GetContext() resp, err := deps.Offchain.GetNode(ctx, &nodev1.GetNodeRequest{Id: in.ID}) if err != nil { return UpdateNodeOutput{}, fmt.Errorf("failed to get node %q: %w", in.ID, err) } nodeInfo := resp.GetNode() existing, lookupErr := ListNodeByPublicKey(ctx, deps.Offchain, in.CSAKey) if lookupErr != nil { b.Logger.Warnw("could not verify CSA key conflict, skipping check", "csa_key", in.CSAKey, "error", lookupErr) } else if existing != nil && existing.GetId() != in.ID { return UpdateNodeOutput{}, fmt.Errorf( "CSA key %s is already registered to node %q (%s), cannot reassign to %q", in.CSAKey, existing.GetId(), existing.GetName(), in.ID, ) } name := nodeInfo.GetName() if in.Name != "" { name = in.Name } labels := nodeInfo.GetLabels() if in.Labels != nil { labels = labelsFromMap(in.Labels) } if _, err = deps.Offchain.UpdateNode(ctx, &nodev1.UpdateNodeRequest{ Id: in.ID, Name: name, PublicKey: in.CSAKey, Labels: labels, }); err != nil { return UpdateNodeOutput{}, fmt.Errorf("failed to update node %q: %w", in.ID, err) } b.Logger.Infow("updated node", "id", in.ID, "name", name, "old_csa_key", nodeInfo.GetPublicKey(), "new_csa_key", in.CSAKey) return UpdateNodeOutput{NodeID: in.ID}, nil }, )
OpJDUpdateNode updates a node's name and/or labels.
var SeqJDDeleteJobs = fwops.NewSequence( "seq-jd-delete-jobs", semver.MustParse("1.0.0"), "Delete multiple jobs", func(b fwops.Bundle, deps JDOpDeps, in DeleteJobsInput) (DeleteJobsOutput, error) { resp, err := deps.Offchain.ListProposals(b.GetContext(), &jobv1.ListProposalsRequest{ Filter: &jobv1.ListProposalsRequest_Filter{JobIds: in.JobIDs}, }) if err != nil { return DeleteJobsOutput{}, fmt.Errorf("failed to list proposals: %w", err) } if len(resp.Proposals) == 0 { return DeleteJobsOutput{}, fmt.Errorf("no proposals found for job ids %v", in.JobIDs) } eligible := eligibleJobIDsForDeletion(resp.Proposals) if len(eligible) == 0 { return DeleteJobsOutput{}, errors.New("no jobs eligible for deletion: no proposals in PROPOSED, APPROVED, or PENDING state") } var deletedIDs []string var failed int var merr error for _, jobID := range eligible { _, err := fwops.ExecuteOperation(b, OpJDDeleteJob, deps, DeleteJobInput{JobID: jobID}) if err != nil { failed++ merr = errors.Join(merr, err) b.Logger.Errorw("failed to delete job", "job_id", jobID, "error", err) continue } deletedIDs = append(deletedIDs, jobID) } b.Logger.Infow("delete_jobs complete", "total", len(eligible), "deleted", len(deletedIDs), "failed", failed) return DeleteJobsOutput{DeletedJobIDs: deletedIDs}, merr }, )
SeqJDDeleteJobs deletes multiple jobs.
var SeqJDDisableNodes = fwops.NewSequence( "seq-jd-disable-nodes", semver.MustParse("1.0.0"), "Disable multiple nodes by CSA key", func(b fwops.Bundle, deps JDOpDeps, in DisableNodesInput) (DisableNodesOutput, error) { var disabledIDs, skippedCSAKeys []string var failed int var merr error for _, csaKey := range in.CSAKeys { report, err := fwops.ExecuteOperation(b, OpJDDisableNode, deps, DisableNodeInput{CSAKey: csaKey}) if err != nil { failed++ merr = errors.Join(merr, err) b.Logger.Errorw("failed to disable node", "csa_key", csaKey, "error", err) continue } if report.Output.Skipped { skippedCSAKeys = append(skippedCSAKeys, csaKey) } else { disabledIDs = append(disabledIDs, report.Output.NodeID) } } b.Logger.Infow("disable_nodes complete", "total", len(in.CSAKeys), "disabled", len(disabledIDs), "skipped", len(skippedCSAKeys), "failed", failed) return DisableNodesOutput{DisabledNodeIDs: disabledIDs, SkippedCSAKeys: skippedCSAKeys}, merr }, )
SeqJDDisableNodes disables multiple nodes by CSA key.
var SeqJDProposeJobs = fwops.NewSequence( "seq-jd-propose-jobs", semver.MustParse("1.0.0"), "Propose multiple job specs", func(b fwops.Bundle, deps JDOpDeps, in ProposeJobsInput) (ProposeJobsOutput, error) { var failed int var merr error for i, j := range in.Jobs { _, err := fwops.ExecuteOperation(b, OpJDProposeJob, deps, ProposeJobInput{ Domain: in.Domain, NodeLabels: j.NodeLabels, JobLabels: j.JobLabels, JobspecTOML: j.JobspecTOML, }) if err != nil { failed++ merr = errors.Join(merr, fmt.Errorf("job[%d]: propose failed: %w", i, err)) b.Logger.Errorw("failed to propose job", "index", i, "node_labels", j.NodeLabels, "error", err) } } b.Logger.Infow("propose_jobs complete", "total", len(in.Jobs), "succeeded", len(in.Jobs)-failed, "failed", failed) return ProposeJobsOutput{}, merr }, )
SeqJDProposeJobs proposes multiple job specs. Failures are collected.
var SeqJDRegisterNodes = fwops.NewSequence( "seq-jd-register-nodes", semver.MustParse("1.0.0"), "Register multiple nodes", func(b fwops.Bundle, deps JDOpDeps, in RegisterNodesInput) (RegisterNodesOutput, error) { var registeredIDs, skippedIDs []string var failed int var merr error for _, n := range in.Nodes { report, err := fwops.ExecuteOperation(b, OpJDRegisterNode, deps, RegisterNodeInput{ Domain: in.Domain, Name: n.Name, CSAKey: n.CSAKey, IsBootstrap: n.IsBootstrap, Labels: n.Labels, }) if err != nil { failed++ merr = errors.Join(merr, err) b.Logger.Errorw("failed to register node", "name", n.Name, "error", err) continue } if report.Output.Skipped { skippedIDs = append(skippedIDs, report.Output.NodeID) } else { registeredIDs = append(registeredIDs, report.Output.NodeID) } } b.Logger.Infow("register_nodes complete", "total", len(in.Nodes), "registered", len(registeredIDs), "skipped", len(skippedIDs), "failed", failed) return RegisterNodesOutput{RegisteredNodeIDs: registeredIDs, SkippedNodeIDs: skippedIDs}, merr }, )
SeqJDRegisterNodes registers multiple nodes.
var SeqJDRevokeJobs = fwops.NewSequence( "seq-jd-revoke-jobs", semver.MustParse("1.0.0"), "Revoke multiple jobs", func(b fwops.Bundle, deps JDOpDeps, in RevokeJobsInput) (RevokeJobsOutput, error) { var revokedIDs, alreadyAbsentIDs []string var failed int var merr error for _, jobID := range in.JobIDs { report, err := fwops.ExecuteOperation(b, OpJDRevokeJob, deps, RevokeJobInput{JobID: jobID}) if err != nil { failed++ merr = errors.Join(merr, err) b.Logger.Errorw("failed to revoke job", "job_id", jobID, "error", err) continue } if report.Output.AlreadyAbsent { alreadyAbsentIDs = append(alreadyAbsentIDs, jobID) } else { revokedIDs = append(revokedIDs, jobID) } } b.Logger.Infow("revoke_jobs complete", "total", len(in.JobIDs), "revoked", len(revokedIDs), "already_absent", len(alreadyAbsentIDs), "failed", failed) return RevokeJobsOutput{RevokedJobIDs: revokedIDs, AlreadyAbsentJobIDs: alreadyAbsentIDs}, merr }, )
SeqJDRevokeJobs revokes multiple jobs. Failures are collected.
var SeqJDUpdateNodes = fwops.NewSequence( "seq-jd-update-nodes", semver.MustParse("1.0.0"), "Update multiple nodes", func(b fwops.Bundle, deps JDOpDeps, in UpdateNodesInput) (UpdateNodesOutput, error) { var updatedIDs []string var failed int var merr error for _, n := range in.Nodes { _, err := fwops.ExecuteOperation(b, OpJDUpdateNode, deps, UpdateNodeInput(n)) if err != nil { failed++ merr = errors.Join(merr, err) b.Logger.Errorw("failed to update node", "id", n.ID, "error", err) continue } updatedIDs = append(updatedIDs, n.ID) } b.Logger.Infow("update_nodes complete", "total", len(in.Nodes), "updated", len(updatedIDs), "failed", failed) return UpdateNodesOutput{UpdatedNodeIDs: updatedIDs}, merr }, )
SeqJDUpdateNodes updates multiple nodes.
Functions ¶
func ListNodeByPublicKey ¶
func ListNodeByPublicKey(ctx context.Context, svc nodev1.NodeServiceClient, csaKey string) (*nodev1.Node, error)
ListNodeByPublicKey returns the node whose public key matches csaKey, or (nil, nil).
Types ¶
type DeleteJobInput ¶
type DeleteJobInput struct {
JobID string `json:"job_id"`
}
DeleteJobInput is the serializable input of OpJDDeleteJob.
type DeleteJobOutput ¶
type DeleteJobOutput struct{}
DeleteJobOutput is the serializable output of OpJDDeleteJob.
type DeleteJobsInput ¶
type DeleteJobsInput struct {
JobIDs []string `json:"job_ids" yaml:"job_ids"`
}
DeleteJobsInput is the serializable input of SeqJDDeleteJobs.
type DeleteJobsOutput ¶
type DeleteJobsOutput struct {
DeletedJobIDs []string `json:"deleted_job_ids"`
}
DeleteJobsOutput is the serializable output of SeqJDDeleteJobs.
type DisableNodeInput ¶
type DisableNodeInput struct {
CSAKey string `json:"csa_key"`
}
DisableNodeInput is the serializable input of OpJDDisableNode.
type DisableNodeOutput ¶
DisableNodeOutput is the serializable output of OpJDDisableNode.
type DisableNodesInput ¶
type DisableNodesInput struct {
CSAKeys []string `json:"csa_keys" yaml:"csa_keys"`
}
DisableNodesInput is the serializable input of SeqJDDisableNodes.
type DisableNodesOutput ¶
type DisableNodesOutput struct {
DisabledNodeIDs []string `json:"disabled_node_ids"`
SkippedCSAKeys []string `json:"skipped_csa_keys"`
}
DisableNodesOutput is the serializable output of SeqJDDisableNodes.
type JobSpec ¶
type JobSpec struct {
NodeLabels map[string]string `json:"node_labels" yaml:"node_labels"`
JobLabels map[string]string `json:"job_labels,omitempty" yaml:"job_labels,omitempty"`
JobspecTOML string `json:"jobspec_toml" yaml:"jobspec_toml"`
}
JobSpec is the per-job input for ProposeJobsInput.
type NodeToRegister ¶
type NodeToRegister struct {
Name string `json:"name" yaml:"name"`
CSAKey string `json:"csa_key" yaml:"csa_key"`
IsBootstrap bool `json:"is_bootstrap" yaml:"is_bootstrap"`
Labels map[string]string `json:"labels" yaml:"labels"`
}
NodeToRegister is the per-node input for RegisterNodesInput.
type NodeToUpdate ¶
type NodeToUpdate struct {
ID string `json:"id" yaml:"id"`
CSAKey string `json:"csa_key" yaml:"csa_key"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
}
NodeToUpdate is the per-node input for UpdateNodesInput.
type ProposeJobInput ¶
type ProposeJobInput struct {
Domain string `json:"domain"`
NodeLabels map[string]string `json:"node_labels"`
JobLabels map[string]string `json:"job_labels,omitempty"`
JobspecTOML string `json:"jobspec_toml"`
}
ProposeJobInput is the serializable input of OpJDProposeJob.
type ProposeJobOutput ¶
type ProposeJobOutput struct{}
ProposeJobOutput is the serializable output of OpJDProposeJob. Empty: ProposeJob targets nodes by label and returns no proposal IDs.
type ProposeJobsInput ¶
type ProposeJobsInput struct {
Domain string `json:"domain" yaml:"domain"`
Jobs []JobSpec `json:"jobs" yaml:"jobs"`
}
ProposeJobsInput is the serializable input of SeqJDProposeJobs.
type ProposeJobsOutput ¶
type ProposeJobsOutput struct{}
ProposeJobsOutput is the serializable output of SeqJDProposeJobs.
type RegisterNodeInput ¶
type RegisterNodeInput struct {
Domain string `json:"domain"`
Name string `json:"name"`
CSAKey string `json:"csa_key"`
IsBootstrap bool `json:"is_bootstrap"`
Labels map[string]string `json:"labels"`
}
RegisterNodeInput is the serializable input of OpJDRegisterNode.
type RegisterNodeOutput ¶
RegisterNodeOutput is the serializable output of OpJDRegisterNode.
type RegisterNodesInput ¶
type RegisterNodesInput struct {
Domain string `json:"domain" yaml:"domain"`
Nodes []NodeToRegister `json:"nodes" yaml:"nodes"`
}
RegisterNodesInput is the serializable input of SeqJDRegisterNodes.
type RegisterNodesOutput ¶
type RegisterNodesOutput struct {
RegisteredNodeIDs []string `json:"registered_node_ids"`
SkippedNodeIDs []string `json:"skipped_node_ids"`
}
RegisterNodesOutput is the serializable output of SeqJDRegisterNodes.
type RevokeJobInput ¶
type RevokeJobInput struct {
JobID string `json:"job_id"`
}
RevokeJobInput is the serializable input of OpJDRevokeJob.
type RevokeJobOutput ¶
type RevokeJobOutput struct {
AlreadyAbsent bool `json:"already_absent"`
}
RevokeJobOutput is the serializable output of OpJDRevokeJob.
type RevokeJobsInput ¶
type RevokeJobsInput struct {
JobIDs []string `json:"job_ids" yaml:"job_ids"`
}
RevokeJobsInput is the serializable input of SeqJDRevokeJobs.
type RevokeJobsOutput ¶
type RevokeJobsOutput struct {
RevokedJobIDs []string `json:"revoked_job_ids"`
AlreadyAbsentJobIDs []string `json:"already_absent_job_ids"`
}
RevokeJobsOutput is the serializable output of SeqJDRevokeJobs.
type UpdateNodeInput ¶
type UpdateNodeInput struct {
ID string `json:"id"`
CSAKey string `json:"csa_key"`
Name string `json:"name,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
}
UpdateNodeInput is the serializable input of OpJDUpdateNode.
type UpdateNodeOutput ¶
type UpdateNodeOutput struct {
NodeID string `json:"node_id"`
}
UpdateNodeOutput is the serializable output of OpJDUpdateNode.
type UpdateNodesInput ¶
type UpdateNodesInput struct {
Nodes []NodeToUpdate `json:"nodes" yaml:"nodes"`
}
UpdateNodesInput is the serializable input of SeqJDUpdateNodes.
type UpdateNodesOutput ¶
type UpdateNodesOutput struct {
UpdatedNodeIDs []string `json:"updated_node_ids"`
}
UpdateNodesOutput is the serializable output of SeqJDUpdateNodes.