github.com/greenboxal/deis@v1.12.1/deisctl/backend/fleet/unit.go (about)

     1  package fleet
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path"
     8  	"strconv"
     9  	"strings"
    10  
    11  	"github.com/coreos/fleet/unit"
    12  )
    13  
    14  // path hierarchy for finding systemd service templates
    15  var templatePaths = []string{
    16  	os.Getenv("DEISCTL_UNITS"),
    17  	path.Join(os.Getenv("HOME"), ".deis", "units"),
    18  	"/var/lib/deis/units",
    19  }
    20  
    21  // and the same for systemd service "decorators" for optionally isolating
    22  // control plane, data plane, and router mesh
    23  var decoratorPaths = []string{
    24  	path.Join(os.Getenv("DEISCTL_UNITS"), "decorators"),
    25  	path.Join(os.Getenv("HOME"), ".deis", "units", "decorators"),
    26  	"/var/lib/deis/units/decorators",
    27  }
    28  
    29  // Units returns a list of units filtered by target
    30  func (c *FleetClient) Units(target string) (units []string, err error) {
    31  	allUnits, err := c.Fleet.Units()
    32  	if err != nil {
    33  		return
    34  	}
    35  	// Look for units starting with the given target name first. If the given
    36  	// name starts with "deis-", this will easily locate platform components,
    37  	// but we search without canonicalizing the target name FIRST so we have the
    38  	// opportunity to locate application containers (whose containers do not
    39  	// adhere to the same naming convention as the platform's own components).
    40  	for _, u := range allUnits {
    41  		if strings.HasPrefix(u.Name, target) {
    42  			units = append(units, u.Name)
    43  		}
    44  	}
    45  	// If none are found, canonicalize the target string and search again. This
    46  	// will locate platform components that were referenced by a target string
    47  	// NOT already beginning with "deis-".
    48  	if len(units) == 0 {
    49  		canonTarget := strings.ToLower(target)
    50  		if !strings.HasPrefix(canonTarget, "deis-") {
    51  			canonTarget = "deis-" + canonTarget
    52  		}
    53  		for _, u := range allUnits {
    54  			if strings.HasPrefix(u.Name, canonTarget) {
    55  				units = append(units, u.Name)
    56  			}
    57  		}
    58  	}
    59  	// If still nothing is found, then we have an error on our hands.
    60  	if len(units) == 0 {
    61  		err = fmt.Errorf("could not find unit: %s", target)
    62  	}
    63  	return
    64  }
    65  
    66  // nextUnit returns the next unit number for a given component
    67  func (c *FleetClient) nextUnit(component string) (num int, err error) {
    68  	units, err := c.Units(component)
    69  	if err != nil {
    70  		return
    71  	}
    72  	num, err = nextUnitNum(units)
    73  	if err != nil {
    74  		return
    75  	}
    76  	return
    77  }
    78  
    79  // lastUnit returns the last unit number for a given component
    80  func (c *FleetClient) lastUnit(component string) (num int, err error) {
    81  	units, err := c.Units(component)
    82  	if err != nil {
    83  		return
    84  	}
    85  	num, err = lastUnitNum(units)
    86  	if err != nil {
    87  		return
    88  	}
    89  	return
    90  }
    91  
    92  // NewUnit takes a component type and returns a Fleet unit
    93  // that includes the relevant systemd service template
    94  func NewUnit(component string, templatePaths []string, decorate bool) (uf *unit.UnitFile, err error) {
    95  	template, err := readTemplate(component, templatePaths)
    96  	if err != nil {
    97  		return
    98  	}
    99  	if decorate {
   100  		decorator, err := readDecorator(component)
   101  		if err != nil {
   102  			return nil, err
   103  		}
   104  		uf, err = unit.NewUnitFile(string(template) + "\n" + string(decorator))
   105  	} else {
   106  		uf, err = unit.NewUnitFile(string(template))
   107  	}
   108  	return
   109  }
   110  
   111  // formatUnitName returns a properly formatted systemd service name
   112  // using the given component type and number
   113  func formatUnitName(component string, num int) (unitName string, err error) {
   114  	component = strings.TrimPrefix(component, "deis-")
   115  	if num == 0 {
   116  		return "deis-" + component + ".service", nil
   117  	}
   118  	return "deis-" + component + "@" + strconv.Itoa(num) + ".service", nil
   119  }
   120  
   121  // readTemplate returns the contents of a systemd template for the given component
   122  func readTemplate(component string, templatePaths []string) (out []byte, err error) {
   123  	templateName := "deis-" + component + ".service"
   124  	var templateFile string
   125  
   126  	// look in $DEISCTL_UNITS env var, then the local and global root paths
   127  	for _, p := range templatePaths {
   128  		if p == "" {
   129  			continue
   130  		}
   131  		filename := path.Join(p, templateName)
   132  		if _, err := os.Stat(filename); err == nil {
   133  			templateFile = filename
   134  			break
   135  		}
   136  	}
   137  
   138  	if templateFile == "" {
   139  		return nil, fmt.Errorf("Could not find unit template for %v", component)
   140  	}
   141  	out, err = ioutil.ReadFile(templateFile)
   142  	if err != nil {
   143  		return
   144  	}
   145  	return
   146  }
   147  
   148  // readDecorator returns the contents of a file containing a snippet that can
   149  // optionally be grafted on to the end of a corresponding systemd unit to
   150  // achieve isolation of the control plane, data plane, and router mesh
   151  func readDecorator(component string) (out []byte, err error) {
   152  	decoratorName := "deis-" + component + ".service.decorator"
   153  	var decoratorFile string
   154  
   155  	// look in $DEISCTL_UNITS env var, then the local and global root paths
   156  	for _, p := range decoratorPaths {
   157  		filename := path.Join(p, decoratorName)
   158  		if _, err := os.Stat(filename); err == nil {
   159  			decoratorFile = filename
   160  			break
   161  		}
   162  	}
   163  
   164  	if decoratorFile == "" {
   165  		return
   166  	}
   167  	out, err = ioutil.ReadFile(decoratorFile)
   168  	return
   169  }