ronantakizawa/codereview-bench huggingface dataset:
Repository: hashicorp/nomad
File: client/allocrunner/taskrunner/secrets/nomad_provider.go (Go)

Code before:

func (n *NomadProvider) BuildTemplate() *structs.Template {
	data := fmt.Sprintf(`
		{{ with nomadVar "%s@%s" }}
		{{ range $k, $v := . }}
		secret.%s.{{ $k }}={{ $v }}
		{{ end }}
		{{ end }}`,
		n.secret.Path, n.config.Namespace, n.secret.Name)

	return &structs.Template{
		EmbeddedTmpl: data,
		DestPath:     filepath.Join(n.secretDir, n.tmplFile),
		ChangeMode:   structs.TemplateChangeModeNoop,
		Once:         true,
	}
}

func (n *NomadProvider) Parse() (map[string]string, error) {
	f, err := os.OpenInRoot(n.secretDir, n.tmplFile)
	if err != nil {
		return nil, fmt.Errorf("error opening env template: %v", err)
	}
	defer func() {
		f.Close()
		os.Remove(filepath.Join(n.secretDir, n.tmplFile))
	}()

	return envparse.Parse(f)
}

// validateNomadInputs ensures none of the user provided inputs contain delimiters
// that could be used to inject other CT functions.
func validateNomadInputs(conf *nomadProviderConfig, path string) error {
	// match if a string contains (...), {{, or }}
	var templateValidate = regexp.MustCompile(`\(.*\)|\{\{|\}\}`)

	if templateValidate.MatchString(conf.Namespace) {
		return fmt.Errorf("namespace cannot contain template delimiters or parenthesis")
	}

	if templateValidate.MatchString(path) {
		return fmt.Errorf("path cannot contain template delimiters or parenthesis")
	}

	return nil
}


Review comment (security):
To make this airtight, we probably want to use `root, err := os.OpenRoot(n.secretDir)` (ref https://pkg.go.dev/os#OpenRoot) and then call `root.Open` and `root.Remove`. That'll ensure we're always using the same file handle for both file operations.

(Same for the Vault provider)

Code after:
}

func (n *NomadProvider) BuildTemplate() *structs.Template {
	data := fmt.Sprintf(`
		{{ with nomadVar "%s@%s" }}
		{{ range $k, $v := . }}
		secret.%s.{{ $k }}={{ $v }}
		{{ end }}
		{{ end }}`,
		n.secret.Path, n.config.Namespace, n.secret.Name)

	return &structs.Template{
		EmbeddedTmpl: data,
		DestPath:     filepath.Clean(filepath.Join(n.secretDir, n.tmplFile)),
		ChangeMode:   structs.TemplateChangeModeNoop,
		Once:         true,
	}
}

func (n *NomadProvider) Parse() (map[string]string, error) {
	r, err := os.OpenRoot(n.secretDir)
	if err != nil {
		return nil, fmt.Errorf("error opening task secrets directory: %v", err)
	}
	defer r.Close()

	f, err := r.Open(n.tmplFile)
	if err != nil {
		return nil, fmt.Errorf("error opening env template: %v", err)
	}
	defer func() {
		f.Close()
		r.Remove(n.tmplFile)
	}()

	return envparse.Parse(f)
}

// validateNomadInputs ensures none of the user provided inputs contain delimiters
// that could be used to inject other CT functions.
func validateNomadInputs(conf *nomadProviderConfig, path string) error {
	if strings.ContainsAny(conf.Namespace, "(){}") {
		return errors.New("namespace cannot contain template delimiters or parenthesis")
	}

	if strings.ContainsAny(path, "(){}") {
		return errors.New("path cannot contain template delimiters or parenthesis")
	}

	return nil
}
Repository: hashicorp/nomad
File: client/allocrunner/taskrunner/secrets/nomad_provider.go (Go)

Code before:
	}
	defer func() {
		f.Close()
		os.Remove(filepath.Join(n.secretDir, n.tmplFile))
	}()

	return envparse.Parse(f)
}

// validateNomadInputs ensures none of the user provided inputs contain delimiters
// that could be used to inject other CT functions.
func validateNomadInputs(conf *nomadProviderConfig, path string) error {
	// match if a string contains (...), {{, or }}
	var templateValidate = regexp.MustCompile(`\(.*\)|\{\{|\}\}`)

	if templateValidate.MatchString(conf.Namespace) {
		return fmt.Errorf("namespace cannot contain template delimiters or parenthesis")
	}

	if templateValidate.MatchString(path) {
		return fmt.Errorf("path cannot contain template delimiters or parenthesis")
	}

	return nil
}


Review comment (suggestion):
I'm not sure this regex is matching what we want. We don't care about whether the delimiters being injected are balanced or complete, so we can get away with something much simpler like [`strings.ContainsAny`](https://pkg.go.dev/strings#ContainsAny):

```suggestion
	if strings.Contains(conf.Namespace, "(){}")` {
		return fmt.Errorf("namespace cannot contain template delimiters or parentheses")
	}
	if strings.Contains(path, "(){}")` {
		return fmt.Errorf("path cannot contain template delimiters or parentheses")
	}

```

Same for the Vault provider.

Code after:
		return nil, fmt.Errorf("error opening task secrets directory: %v", err)
	}
	defer r.Close()

	f, err := r.Open(n.tmplFile)
	if err != nil {
		return nil, fmt.Errorf("error opening env template: %v", err)
	}
	defer func() {
		f.Close()
		r.Remove(n.tmplFile)
	}()

	return envparse.Parse(f)
}

// validateNomadInputs ensures none of the user provided inputs contain delimiters
// that could be used to inject other CT functions.
func validateNomadInputs(conf *nomadProviderConfig, path string) error {
	if strings.ContainsAny(conf.Namespace, "(){}") {
		return errors.New("namespace cannot contain template delimiters or parenthesis")
	}

	if strings.ContainsAny(path, "(){}") {
		return errors.New("path cannot contain template delimiters or parenthesis")
	}

	return nil
}
Repository: hashicorp/nomad
File: client/allocrunner/taskrunner/secrets/vault_provider.go (Go)

Code before:

	return &VaultProvider{
		secret:    secret,
		secretDir: secretDir,
		tmplFile:  tmplFile,
		conf:      conf,
	}, nil
}

func (v *VaultProvider) BuildTemplate() *structs.Template {
	indexKey := ".Data"
	if v.conf.Engine == VAULT_KV_V2 {
		indexKey = ".Data.data"
	}

	data := fmt.Sprintf(`
		{{ with secret "%s" }}
		{{ range $k, $v := %s }}
		secret.%s.{{ $k }}={{ $v }}
		{{ end }}
		{{ end }}`,
		v.secret.Path, indexKey, v.secret.Name)

	return &structs.Template{
		EmbeddedTmpl: data,
		DestPath:     filepath.Join(v.secretDir, v.tmplFile),
		ChangeMode:   structs.TemplateChangeModeNoop,
		Once:         true,
	}
}

func (v *VaultProvider) Parse() (map[string]string, error) {
	r, err := os.OpenRoot(v.secretDir)
	if err != nil {
		return nil, fmt.Errorf("error opening task secrets directory: %v", err)
	}
	defer r.Close()

	f, err := r.Open(v.tmplFile)
	if err != nil {
		return nil, fmt.Errorf("error opening env template: %v", err)
	}
	defer func() {
		f.Close()
		r.Remove(v.tmplFile)
	}()

	return envparse.Parse(f)
}


Review comment (security):
```suggestion
		DestPath:     filepath.Clean(filepath.Join(v.secretDir, v.tmplFile)),
```

Code after:

	return &VaultProvider{
		secret:    secret,
		secretDir: secretDir,
		tmplFile:  tmplFile,
		conf:      conf,
	}, nil
}

func (v *VaultProvider) BuildTemplate() *structs.Template {
	indexKey := ".Data"
	if v.conf.Engine == VAULT_KV_V2 {
		indexKey = ".Data.data"
	}

	data := fmt.Sprintf(`
		{{ with secret "%s" }}
		{{ range $k, $v := %s }}
		secret.%s.{{ $k }}={{ $v }}
		{{ end }}
		{{ end }}`,
		v.secret.Path, indexKey, v.secret.Name)

	return &structs.Template{
		EmbeddedTmpl: data,
		DestPath:     filepath.Clean(filepath.Join(v.secretDir, v.tmplFile)),
		ChangeMode:   structs.TemplateChangeModeNoop,
		Once:         true,
	}
}

func (v *VaultProvider) Parse() (map[string]string, error) {
	r, err := os.OpenRoot(v.secretDir)
	if err != nil {
		return nil, fmt.Errorf("error opening task secrets directory: %v", err)
	}
	defer r.Close()

	f, err := r.Open(v.tmplFile)
	if err != nil {
		return nil, fmt.Errorf("error opening env template: %v", err)
	}
	defer func() {
		f.Close()
		r.Remove(v.tmplFile)
	}()

	return envparse.Parse(f)
}
Repository: hashicorp/nomad
File: command/eval_status.go (Go)

Code before:
)

type EvalStatusCommand struct {
	Meta
}

func (c *EvalStatusCommand) Help() string {
	helpText := `
Usage: nomad eval status [options] <evaluation>

  Display information about evaluations. This command can be used to inspect the
  current status of an evaluation as well as determine the reason an evaluation
  did not place all allocations.

General Options:

  ` + generalOptionsUsage(usageOptsDefault) + `

Eval Status Options:

  -monitor
    Monitor an outstanding evaluation

  -verbose
    Show full-length IDs, exact timestamps, and all reconciler annotation
    fields.

  -json
    Output the evaluation in its JSON format. This format will not include
    placed allocations.

  -t
    Format and display evaluation using a Go template. This format will not
    include placed allocations.

  -ui
    Open the evaluation in the browser.
`

	return strings.TrimSpace(helpText)
}

func (c *EvalStatusCommand) Synopsis() string {
	return "Display evaluation status and placement failure reasons"
}

func (c *EvalStatusCommand) AutocompleteFlags() complete.Flags {
	return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
		complete.Flags{
			"-json":    complete.PredictNothing,
			"-monitor": complete.PredictNothing,

Review comment (suggestion):
I wonder if "reconciler annotation fields" shouldn't be rephrased somehow, because "reconciler" seems like a very Nomad-internal thing. 

"all scheduler details?"

Code after:
)

type EvalStatusCommand struct {
	Meta
}

func (c *EvalStatusCommand) Help() string {
	helpText := `
Usage: nomad eval status [options] <evaluation>

  Display information about evaluations. This command can be used to inspect the
  current status of an evaluation as well as determine the reason an evaluation
  did not place all allocations.

General Options:

  ` + generalOptionsUsage(usageOptsDefault) + `

Eval Status Options:

  -monitor
    Monitor an outstanding evaluation

  -verbose
    Show full-length IDs, exact timestamps, and all plan annotation fields.

  -json
    Output the evaluation in its JSON format. This format will not include
    placed allocations.

  -t
    Format and display evaluation using a Go template. This format will not
    include placed allocations.

  -ui
    Open the evaluation in the browser.
`

	return strings.TrimSpace(helpText)
}

func (c *EvalStatusCommand) Synopsis() string {
	return "Display evaluation status and placement failure reasons"
}

func (c *EvalStatusCommand) AutocompleteFlags() complete.Flags {
	return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
		complete.Flags{
			"-json":    complete.PredictNothing,
			"-monitor": complete.PredictNothing,
			"-t":       complete.PredictAnything,
Repository: hashicorp/nomad
File: scheduler/reconciler/reconcile_node_test.go (Go)

Code before:
			for _, tuple := range diff.Place {
				if alloc.NodeID == tuple.Alloc.NodeID && alloc.TaskGroup == "web" {
					must.Eq(t, alloc, tuple.Alloc)
				}
			}
		}
	}
}

// TestNodeDeployments tests various deployment-related scenarios for the node
// reconciler
func TestNodeDeployments(t *testing.T) {
	ci.Parallel(t)

	job := mock.SystemJob()
	tg := job.TaskGroups[0].Copy()
	tg.Name = "other"
	tg.Update = structs.DefaultUpdateStrategy
	job.TaskGroups = append(job.TaskGroups, tg)

	// Create two alive nodes.
	nodes := []*structs.Node{{ID: "foo"}, {ID: "bar"}}

	// The "old" job has a previous modify index
	oldJob := new(structs.Job)
	*oldJob = *job
	oldJob.JobModifyIndex -= 1

	// Stopped job to make sure we handle these correctly
	stoppedJob := job.Copy()
	stoppedJob.Stop = true

	allocs := []*structs.Allocation{}
	for _, n := range nodes {
		a := mock.Alloc()
		a.Job = job
		a.Name = "my-job.web[0]"
		a.NodeID = n.ID
		a.NodeName = n.Name

		allocs = append(allocs, a)
	}

	deploymentID := uuid.Generate()

	// Have an existing successful deployment for this
	testCases := []struct {
		name                         string
		job                          *structs.Job
		existingDeployment           *structs.Deployment
		expectedDeploymentStatus     string

Review comment (suggestion):
Nitpicky, but will save us pain if later changes start mutating/checking other fields:

```suggestion
	oldJob := job.Copy()
```

But also, `oldJob` doesn't seem to be used!

Code after:
				if alloc.NodeID == tuple.Alloc.NodeID && alloc.TaskGroup == "web" {
					must.Eq(t, alloc, tuple.Alloc)
				}
			}
		}
	}
}

// TestNodeDeployments tests various deployment-related scenarios for the node
// reconciler
func TestNodeDeployments(t *testing.T) {
	ci.Parallel(t)

	job := mock.SystemJob()
	tg := job.TaskGroups[0].Copy()
	tg.Name = "other"
	tg.Update = structs.DefaultUpdateStrategy
	job.TaskGroups = append(job.TaskGroups, tg)

	// Create two alive nodes.
	nodes := []*structs.Node{{ID: "foo"}, {ID: "bar"}}

	// Stopped job to make sure we handle these correctly
	stoppedJob := job.Copy()
	stoppedJob.Stop = true

	allocs := []*structs.Allocation{}
	for _, n := range nodes {
		a := mock.Alloc()
		a.Job = job
		a.Name = "my-job.web[0]"
		a.NodeID = n.ID
		a.NodeName = n.Name

		allocs = append(allocs, a)
	}

	newJobWithNoAllocs := job.Copy()
	newJobWithNoAllocs.Name = "new-job"
	newJobWithNoAllocs.Version = 100
	newJobWithNoAllocs.CreateIndex = 1000

	testCases := []struct {
		name                                  string
		job                                   *structs.Job
		existingDeployment                    *structs.Deployment
		newDeployment                         bool
		expectedNewDeploymentStatus           string
		expectedDeploymenStatusUpdateContains string
	}{
		{
Repository: hashicorp/nomad
File: scheduler/scheduler_system.go (Go)

Code before:
	ws := memdb.NewWatchSet()
	s.job, err = s.state.JobByID(ws, s.eval.Namespace, s.eval.JobID)
	if err != nil {
		return false, fmt.Errorf("failed to get job '%s': %v", s.eval.JobID, err)
	}

	numTaskGroups := 0
	if !s.job.Stopped() {
		numTaskGroups = len(s.job.TaskGroups)
	}
	s.queuedAllocs = make(map[string]int, numTaskGroups)

	// Get the ready nodes in the required datacenters
	if !s.job.Stopped() {
		s.nodes, s.notReadyNodes, s.nodesByDC, err = readyNodesInDCsAndPool(
			s.state, s.job.Datacenters, s.job.NodePool)
		if err != nil {
			return false, fmt.Errorf("failed to get ready nodes: %v", err)
		}
	}

	if !s.sysbatch {
		// Get any existing deployment
		s.deployment, err = s.state.LatestDeploymentByJobID(ws, s.eval.Namespace, s.eval.JobID)
		if err != nil {
			return false, fmt.Errorf("failed to get job deployment %q: %v", s.eval.JobID, err)
		}
	}

	// Create a plan
	s.plan = s.eval.MakePlan(s.job)

	// Reset the failed allocations
	s.failedTGAllocs = nil

	// Create an evaluation context
	s.ctx = feasible.NewEvalContext(s.eventsCh, s.state, s.plan, s.logger)

	// Construct the placement stack
	s.stack = feasible.NewSystemStack(s.sysbatch, s.ctx)
	if !s.job.Stopped() {
		s.setJob(s.job)
	}

	// Compute the target job allocations
	if err := s.computeJobAllocs(); err != nil {
		s.logger.Error("failed to compute job allocations", "error", err)
		return false, err
	}

	// If the plan is a no-op, we can bail. If AnnotatePlan is set submit the plan

Review comment (bug):
```suggestion
			return false, fmt.Errorf("failed to get deployment for job %q: %w", s.eval.JobID, err)
```

Code after:
	ws := memdb.NewWatchSet()
	s.job, err = s.state.JobByID(ws, s.eval.Namespace, s.eval.JobID)
	if err != nil {
		return false, fmt.Errorf("failed to get job '%s': %v", s.eval.JobID, err)
	}

	numTaskGroups := 0
	if !s.job.Stopped() {
		numTaskGroups = len(s.job.TaskGroups)
	}
	s.queuedAllocs = make(map[string]int, numTaskGroups)

	// Get the ready nodes in the required datacenters
	if !s.job.Stopped() {
		s.nodes, s.notReadyNodes, s.nodesByDC, err = readyNodesInDCsAndPool(
			s.state, s.job.Datacenters, s.job.NodePool)
		if err != nil {
			return false, fmt.Errorf("failed to get ready nodes: %v", err)
		}
	}

	if !s.sysbatch {
		// Get any existing deployment
		s.deployment, err = s.state.LatestDeploymentByJobID(ws, s.eval.Namespace, s.eval.JobID)
		if err != nil {
			return false, fmt.Errorf("failed to get deployment for job %q: %w", s.eval.JobID, err)
		}
	}

	// Create a plan
	s.plan = s.eval.MakePlan(s.job)

	// Reset the failed allocations
	s.failedTGAllocs = nil

	// Create an evaluation context
	s.ctx = feasible.NewEvalContext(s.eventsCh, s.state, s.plan, s.logger)

	// Construct the placement stack
	s.stack = feasible.NewSystemStack(s.sysbatch, s.ctx)
	if !s.job.Stopped() {
		s.setJob(s.job)
	}

	// Compute the target job allocations
	if err := s.computeJobAllocs(); err != nil {
		s.logger.Error("failed to compute job allocations", "error", err)
		return false, err
	}

	// If the plan is a no-op, we can bail. If AnnotatePlan is set submit the plan
Repository: hashicorp/nomad
File: scheduler/reconciler/reconcile_node.go (Go)

Code before:
				dstate.DesiredTotal += len(result.Place)
			}
			nr.createDeployment(job, tg, dstate, len(result.Update), len(result.Place), allocTuple.Alloc)
		}
	}
	return result
}

func (nr *NodeReconciler) createDeployment(job *structs.Job,
	tg *structs.TaskGroup, dstate *structs.DeploymentState, updates, place int,
	alloc *structs.Allocation) {
	// initialize the deployment
	existingDeployment := false

	if nr.DeploymentCurrent != nil {
		dstate, existingDeployment = nr.DeploymentCurrent.TaskGroups[tg.Name]
	}

	if dstate == nil {
		dstate = &structs.DeploymentState{}
	}

	// check if perhaps there's nothing more to do
	if existingDeployment || tg.Update.IsEmpty() || dstate.DesiredTotal == 0 {
		return
	}

	updatingSpec := updates != 0

	hadRunning := false
	if alloc != nil && alloc.Job != nil {
		if alloc.Job.Version == job.Version && alloc.Job.CreateIndex == job.CreateIndex {
			hadRunning = true
		}
	}

	// Don't create a deployment if it's not the first time running the job
	// and there are no updates to the spec.
	if hadRunning && !updatingSpec {
		return
	}

	// A previous group may have made the deployment already. If not create one.
	if nr.DeploymentCurrent == nil {
		nr.DeploymentCurrent = structs.NewDeployment(job, job.Priority, time.Now().UnixNano())
		nr.DeploymentUpdates = append(
			nr.DeploymentUpdates, &structs.DeploymentStatusUpdate{
				DeploymentID:      nr.DeploymentCurrent.ID,
				Status:            structs.DeploymentStatusRunning,
				StatusDescription: structs.DeploymentStatusDescriptionRunning,
			})

Review comment (refactor):
The caller already knows if `existingDeployment` is true; in the alloc reconciler implementation we take that as an argument and can move this check up.

Code after:
				}
				dstate.DesiredTotal += len(result.Place)
			}

			if dstate == nil {
				dstate = new(structs.DeploymentState)
			}

			// in this case there's nothing to do
			if existingDeployment || tg.Update.IsEmpty() || dstate.DesiredTotal == 0 {
				continue
			}

			// allocs that are in the Place, Update and Migrate buckets are the ones we
			// consider for deployment
			allocsForDeployment := []*structs.Allocation{}
			for _, r := range slices.Concat(result.Place, result.Update, result.Migrate) {
				allocsForDeployment = append(allocsForDeployment, r.Alloc)
			}
			nr.createDeployment(job, tg, dstate, len(result.Update), liveAllocs)
		}
	}

	return result
}

func (nr *NodeReconciler) createDeployment(job *structs.Job,
	tg *structs.TaskGroup, dstate *structs.DeploymentState, updates int,
	allocs []*structs.Allocation) {

	if dstate == nil {
		dstate = &structs.DeploymentState{}
	}

	updatingSpec := updates != 0

	hadRunning := false
	for _, alloc := range allocs {
		if alloc.Job.Version == job.Version && alloc.Job.CreateIndex == job.CreateIndex {
			hadRunning = true
			break
		}
	}

	// Don't create a deployment if it's not the first time running the job
	// and there are no updates to the spec.
	if hadRunning && !updatingSpec {
		return
	}

	// A previous group may have made the deployment already. If not create one.
Repository: hashicorp/nomad
File: command/acl_policy_self.go (Go)

Code before:
	}

	// Check that we have no arguments
	args = flags.Args()
	if l := len(args); l != 0 {
		c.Ui.Error(uiMessageNoArguments)
		c.Ui.Error(commandErrorText(c))
		return 1
	}

	// Get the HTTP client
	client, err := c.Meta.Client()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
		return 1
	}

	// Read the self token to check its type
	token, _, err := client.ACLTokens().Self(nil)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error fetching token: %s", err))
		return 1
	}

	if token.Type == "management" {
		c.Ui.Output("This is a management token with global access. No individual policies are assigned.")
		return 0
	}

	policies, _, err := client.ACLPolicies().Self(nil)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error fetching WI policies: %s", err))
		return 1
	}

	if len(policies) == 0 {
		c.Ui.Output("No policies found.")
	} else {
		if c.json || len(c.tmpl) > 0 {
			out, err := Format(c.json, c.tmpl, policies)
			if err != nil {
				c.Ui.Error(err.Error())
				return 1
			}

			c.Ui.Output(out)
			return 0
		}

		output := make([]string, 0, len(policies)+1)
		output = append(output, "Name|Job ID|Group Name|Task Name")

Review comment (suggestion):
I think you're using "global" here to mean "all-access" but tokens also have a scope (set with the [`-global`](https://developer.hashicorp.com/nomad/commands/acl/token/create#global) flag). So not all management tokens are global because some are local to a region in federated clusters.

```suggestion
		c.Ui.Output("This is a management token. No individual policies are assigned.")
```

Code after:
	}

	// Check that we have no arguments
	args = flags.Args()
	if l := len(args); l != 0 {
		c.Ui.Error(uiMessageNoArguments)
		c.Ui.Error(commandErrorText(c))
		return 1
	}

	// Get the HTTP client
	client, err := c.Meta.Client()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
		return 1
	}

	// Read the self token to check its type
	token, _, err := client.ACLTokens().Self(nil)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error fetching token: %s", err))
		return 1
	}

	if token.Type == "management" {
		c.Ui.Output("This is a management token. No individual policies are assigned.")
		return 0
	}

	policies, _, err := client.ACLPolicies().Self(nil)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error fetching WI policies: %s", err))
		return 1
	}

	if len(policies) == 0 {
		c.Ui.Output("No policies found.")
	} else {
		if c.json || len(c.tmpl) > 0 {
			out, err := Format(c.json, c.tmpl, policies)
			if err != nil {
				c.Ui.Error(err.Error())
				return 1
			}

			c.Ui.Output(out)
			return 0
		}

		output := make([]string, 0, len(policies)+1)
		output = append(output, "Name|Job ID|Group Name|Task Name")