github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/nomad/resource_job.go (about)

     1  package nomad
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/gob"
     6  	"fmt"
     7  	"log"
     8  	"reflect"
     9  	"strings"
    10  
    11  	"github.com/hashicorp/nomad/api"
    12  	"github.com/hashicorp/nomad/jobspec"
    13  	"github.com/hashicorp/nomad/nomad/structs"
    14  	"github.com/hashicorp/terraform/helper/schema"
    15  )
    16  
    17  func resourceJob() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceJobRegister,
    20  		Update: resourceJobRegister,
    21  		Delete: resourceJobDeregister,
    22  		Read:   resourceJobRead,
    23  		Exists: resourceJobExists,
    24  
    25  		Schema: map[string]*schema.Schema{
    26  			"jobspec": {
    27  				Description:      "Job specification. If you want to point to a file use the file() function.",
    28  				Required:         true,
    29  				Type:             schema.TypeString,
    30  				DiffSuppressFunc: jobspecDiffSuppress,
    31  			},
    32  
    33  			"deregister_on_destroy": {
    34  				Description: "If true, the job will be deregistered on destroy.",
    35  				Optional:    true,
    36  				Default:     true,
    37  				Type:        schema.TypeBool,
    38  			},
    39  
    40  			"deregister_on_id_change": {
    41  				Description: "If true, the job will be deregistered when the job ID changes.",
    42  				Optional:    true,
    43  				Default:     true,
    44  				Type:        schema.TypeBool,
    45  			},
    46  		},
    47  	}
    48  }
    49  
    50  func resourceJobRegister(d *schema.ResourceData, meta interface{}) error {
    51  	client := meta.(*api.Client)
    52  
    53  	// Get the jobspec itself
    54  	jobspecRaw := d.Get("jobspec").(string)
    55  
    56  	// Parse it
    57  	jobspecStruct, err := jobspec.Parse(strings.NewReader(jobspecRaw))
    58  	if err != nil {
    59  		return fmt.Errorf("error parsing jobspec: %s", err)
    60  	}
    61  
    62  	// Initialize and validate
    63  	jobspecStruct.Canonicalize()
    64  	if err := jobspecStruct.Validate(); err != nil {
    65  		return fmt.Errorf("Error validating job: %v", err)
    66  	}
    67  
    68  	// If we have an ID and its not equal to this jobspec, then we
    69  	// have to deregister the old job before we register the new job.
    70  	prevId := d.Id()
    71  	if !d.Get("deregister_on_id_change").(bool) {
    72  		// If we aren't deregistering on ID change, just pretend we
    73  		// don't have a prior ID.
    74  		prevId = ""
    75  	}
    76  	if prevId != "" && prevId != jobspecStruct.ID {
    77  		log.Printf(
    78  			"[INFO] Deregistering %q before registering %q",
    79  			prevId, jobspecStruct.ID)
    80  
    81  		log.Printf("[DEBUG] Deregistering job: %q", prevId)
    82  		_, _, err := client.Jobs().Deregister(prevId, nil)
    83  		if err != nil {
    84  			return fmt.Errorf(
    85  				"error deregistering previous job %q "+
    86  					"before registering new job %q: %s",
    87  				prevId, jobspecStruct.ID, err)
    88  		}
    89  
    90  		// Success! Clear our state.
    91  		d.SetId("")
    92  	}
    93  
    94  	// Convert it so that we can use it with the API
    95  	jobspecAPI, err := convertStructJob(jobspecStruct)
    96  	if err != nil {
    97  		return fmt.Errorf("error converting jobspec: %s", err)
    98  	}
    99  
   100  	// Register the job
   101  	_, _, err = client.Jobs().Register(jobspecAPI, nil)
   102  	if err != nil {
   103  		return fmt.Errorf("error applying jobspec: %s", err)
   104  	}
   105  
   106  	d.SetId(jobspecAPI.ID)
   107  
   108  	return nil
   109  }
   110  
   111  func resourceJobDeregister(d *schema.ResourceData, meta interface{}) error {
   112  	client := meta.(*api.Client)
   113  
   114  	// If deregistration is disabled, then do nothing
   115  	if !d.Get("deregister_on_destroy").(bool) {
   116  		log.Printf(
   117  			"[WARN] Job %q will not deregister since 'deregister_on_destroy'"+
   118  				" is false", d.Id())
   119  		return nil
   120  	}
   121  
   122  	id := d.Id()
   123  	log.Printf("[DEBUG] Deregistering job: %q", id)
   124  	_, _, err := client.Jobs().Deregister(id, nil)
   125  	if err != nil {
   126  		return fmt.Errorf("error deregistering job: %s", err)
   127  	}
   128  
   129  	return nil
   130  }
   131  
   132  func resourceJobExists(d *schema.ResourceData, meta interface{}) (bool, error) {
   133  	client := meta.(*api.Client)
   134  
   135  	id := d.Id()
   136  	log.Printf("[DEBUG] Checking if job exists: %q", id)
   137  	_, _, err := client.Jobs().Info(id, nil)
   138  	if err != nil {
   139  		// As of Nomad 0.4.1, the API client returns an error for 404
   140  		// rather than a nil result, so we must check this way.
   141  		if strings.Contains(err.Error(), "404") {
   142  			return false, nil
   143  		}
   144  
   145  		return true, fmt.Errorf("error checking for job: %#v", err)
   146  	}
   147  
   148  	return true, nil
   149  }
   150  
   151  func resourceJobRead(d *schema.ResourceData, meta interface{}) error {
   152  	// We don't do anything at the moment. Exists is used to
   153  	// remove non-existent jobs but read doesn't have to do anything.
   154  	return nil
   155  }
   156  
   157  // convertStructJob is used to take a *structs.Job and convert it to an *api.Job.
   158  //
   159  // This is unfortunate but it is how Nomad itself does it (this is copied
   160  // line for line from Nomad). We'll mimic them exactly to get this done.
   161  func convertStructJob(in *structs.Job) (*api.Job, error) {
   162  	gob.Register([]map[string]interface{}{})
   163  	gob.Register([]interface{}{})
   164  	var apiJob *api.Job
   165  	buf := new(bytes.Buffer)
   166  	if err := gob.NewEncoder(buf).Encode(in); err != nil {
   167  		return nil, err
   168  	}
   169  	if err := gob.NewDecoder(buf).Decode(&apiJob); err != nil {
   170  		return nil, err
   171  	}
   172  	return apiJob, nil
   173  }
   174  
   175  // jobspecDiffSuppress is the DiffSuppressFunc used by the schema to
   176  // check if two jobspecs are equal.
   177  func jobspecDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
   178  	// Parse the old job
   179  	oldJob, err := jobspec.Parse(strings.NewReader(old))
   180  	if err != nil {
   181  		return false
   182  	}
   183  
   184  	// Parse the new job
   185  	newJob, err := jobspec.Parse(strings.NewReader(new))
   186  	if err != nil {
   187  		return false
   188  	}
   189  
   190  	// Init
   191  	oldJob.Canonicalize()
   192  	newJob.Canonicalize()
   193  
   194  	// Check for jobspec equality
   195  	return reflect.DeepEqual(oldJob, newJob)
   196  }