197 lines
5.1 KiB
Go
197 lines
5.1 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package command
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/nomad/api"
|
|
"github.com/hashicorp/nomad/command/asset"
|
|
"github.com/posener/complete"
|
|
)
|
|
|
|
const (
|
|
// DefaultInitName is the default name we use when
|
|
// initializing the example file
|
|
DefaultInitName = "example.nomad.hcl"
|
|
)
|
|
|
|
// JobInitCommand generates a new job template that you can customize to your
|
|
// liking, like vagrant init
|
|
type JobInitCommand struct {
|
|
Meta
|
|
}
|
|
|
|
func (c *JobInitCommand) Help() string {
|
|
helpText := `
|
|
Usage: nomad job init <filename>
|
|
Alias: nomad init <filename>
|
|
|
|
Creates an example job file that can be used as a starting point to customize
|
|
further. If no filename is given, the default of "example.nomad.hcl" will be used.
|
|
|
|
Init Options:
|
|
|
|
-short
|
|
If the short flag is set, a minimal jobspec without comments is emitted.
|
|
|
|
-connect
|
|
If the connect flag is set, the jobspec includes Consul Connect integration.
|
|
|
|
-template
|
|
Specifies a predefined template to initialize. Must be a Nomad Variable that
|
|
lives at nomad/job-templates/<template>
|
|
|
|
-list-templates
|
|
Display a list of possible job templates to pass to -template. Reads from
|
|
all variables pathed at nomad/job-templates/<template>
|
|
`
|
|
return strings.TrimSpace(helpText)
|
|
}
|
|
|
|
func (c *JobInitCommand) Synopsis() string {
|
|
return "Create an example job file"
|
|
}
|
|
|
|
func (c *JobInitCommand) AutocompleteFlags() complete.Flags {
|
|
return mergeAutocompleteFlags(c.Meta.AutocompleteFlags(FlagSetClient),
|
|
complete.Flags{
|
|
"-short": complete.PredictNothing,
|
|
})
|
|
}
|
|
|
|
func (c *JobInitCommand) AutocompleteArgs() complete.Predictor {
|
|
return complete.PredictNothing
|
|
}
|
|
|
|
func (c *JobInitCommand) Name() string { return "job init" }
|
|
|
|
func (c *JobInitCommand) Run(args []string) int {
|
|
var short bool
|
|
var connect bool
|
|
var template string
|
|
var listTemplates bool
|
|
|
|
flags := c.Meta.FlagSet(c.Name(), FlagSetClient)
|
|
flags.Usage = func() { c.Ui.Output(c.Help()) }
|
|
flags.BoolVar(&short, "short", false, "")
|
|
flags.BoolVar(&connect, "connect", false, "")
|
|
flags.StringVar(&template, "template", "", "The name of the job template variable to initialize")
|
|
flags.BoolVar(&listTemplates, "list-templates", false, "")
|
|
|
|
if err := flags.Parse(args); err != nil {
|
|
return 1
|
|
}
|
|
|
|
// Check for misuse
|
|
// Check that we either got no filename or exactly one.
|
|
args = flags.Args()
|
|
if len(args) > 1 {
|
|
c.Ui.Error("This command takes either no arguments or one: <filename>")
|
|
c.Ui.Error(commandErrorText(c))
|
|
return 1
|
|
}
|
|
|
|
filename := DefaultInitName
|
|
if len(args) == 1 {
|
|
filename = args[0]
|
|
}
|
|
|
|
// Check if the file already exists
|
|
_, err := os.Stat(filename)
|
|
if err != nil && !os.IsNotExist(err) {
|
|
c.Ui.Error(fmt.Sprintf("Failed to stat '%s': %v", filename, err))
|
|
return 1
|
|
}
|
|
if !os.IsNotExist(err) && !listTemplates {
|
|
c.Ui.Error(fmt.Sprintf("Job '%s' already exists", filename))
|
|
return 1
|
|
}
|
|
|
|
var jobSpec []byte
|
|
|
|
if listTemplates {
|
|
// Get the HTTP client
|
|
client, err := c.Meta.Client()
|
|
if err != nil {
|
|
c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err))
|
|
return 1
|
|
}
|
|
qo := &api.QueryOptions{
|
|
Namespace: c.Meta.namespace,
|
|
}
|
|
|
|
// Get and list all variables at nomad/job-templates
|
|
vars, _, err := client.Variables().PrefixList("nomad/job-templates/", qo)
|
|
if err != nil {
|
|
c.Ui.Error(fmt.Sprintf("Error retrieving job templates from the server; unable to read variables at path nomad/job-templates/. Error: %s", err))
|
|
return 1
|
|
}
|
|
|
|
if len(vars) == 0 {
|
|
c.Ui.Error("No variables in nomad/job-templates")
|
|
return 1
|
|
} else {
|
|
c.Ui.Output("Use nomad job init -template=<template> with any of the following:")
|
|
for _, v := range vars {
|
|
c.Ui.Output(fmt.Sprintf(" %s", strings.TrimPrefix(v.Path, "nomad/job-templates/")))
|
|
}
|
|
}
|
|
return 0
|
|
} else if template != "" {
|
|
// Get the HTTP client
|
|
client, err := c.Meta.Client()
|
|
if err != nil {
|
|
c.Ui.Error(fmt.Sprintf("Error initializing: %s", err))
|
|
return 1
|
|
}
|
|
|
|
qo := &api.QueryOptions{
|
|
Namespace: c.Meta.namespace,
|
|
}
|
|
sv, _, err := client.Variables().Read("nomad/job-templates/"+template, qo)
|
|
if err != nil {
|
|
if err.Error() == "variable not found" {
|
|
c.Ui.Warn(errVariableNotFound)
|
|
return 1
|
|
}
|
|
c.Ui.Error(fmt.Sprintf("Error retrieving variable: %s", err))
|
|
return 1
|
|
}
|
|
|
|
if v, ok := sv.Items["template"]; ok {
|
|
c.Ui.Output(fmt.Sprintf("Initializing a job template from %s", template))
|
|
jobSpec = []byte(v)
|
|
} else {
|
|
c.Ui.Error(fmt.Sprintf("Job template %q is malformed and is missing a template field. Please visit the jobs/run/templates route in the Nomad UI to add it", template))
|
|
return 1
|
|
}
|
|
|
|
} else {
|
|
switch {
|
|
case connect && !short:
|
|
jobSpec = asset.JobConnect
|
|
case connect && short:
|
|
jobSpec = asset.JobConnectShort
|
|
case !connect && short:
|
|
jobSpec = asset.JobExampleShort
|
|
default:
|
|
jobSpec = asset.JobExample
|
|
}
|
|
}
|
|
|
|
// Write out the example
|
|
err = os.WriteFile(filename, jobSpec, 0660)
|
|
if err != nil {
|
|
c.Ui.Error(fmt.Sprintf("Failed to write '%s': %v", filename, err))
|
|
return 1
|
|
}
|
|
|
|
// Success
|
|
c.Ui.Output(fmt.Sprintf("Example job file written to %s", filename))
|
|
return 0
|
|
}
|