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 }