// createStageFile stages the src configuration file by processing the src // template and setting the desired owner, group, and mode. It also sets the // StageFile for the template resource. // It returns an error if any. func (t *Template) createStageFile(fileMode os.FileMode) (*os.File, error) { srcData := t.config.SrcData if t.config.Src != "" { log.Debugf("Using source template %s", t.config.Src) if !isFileExist(t.config.Src) { return nil, errors.New("Missing template: " + t.config.Src) } fileData, err := ioutil.ReadFile(t.config.Src) if err != nil { return nil, err } srcData = string(fileData) log.Debugf("Compiling source template from file %s", t.config.Src) } tmpl, err := template.New(path.Base(t.config.Src)).Funcs(t.funcMap).Parse(srcData) if err != nil { return nil, fmt.Errorf("Unable to process template %s, %s", t.config.Src, err) } // create TempFile in Dest directory to avoid cross-filesystem issues errorOcurred := true tempFile, err := ioutil.TempFile(filepath.Dir(t.config.Dest), "."+filepath.Base(t.config.Dest)) if err != nil { return nil, err } defer func() { tempFile.Close() if !t.keepStageFile && errorOcurred { os.Remove(tempFile.Name()) } }() if err = tmpl.Execute(tempFile, nil); err != nil { return nil, err } // Set the owner, group, and mode on the stage file now to make it easier to // compare against the destination configuration file later. err = os.Chmod(tempFile.Name(), fileMode) if err != nil { return nil, err } err = os.Chown(tempFile.Name(), t.config.Uid, t.config.Gid) if err != nil { return nil, err } errorOcurred = false return tempFile, nil }
func (t *Template) exec(cmd string) error { log.Debugf("Running %s", cmd) c := exec.Command("/bin/sh", "-c", cmd) output, err := c.CombinedOutput() if err != nil { log.Errorf("%q", string(output)) return err } log.Debugf("%q", string(output)) return nil }
// sync compares the staged and dest config files and attempts to sync them // if they differ. sync will run a config check command if set before // overwriting the target config file. Finally, sync will run a reload command // if set to have the application or service pick up the changes. // It returns an error if any. func (t *Template) sync(stageFile *os.File, fileMode os.FileMode, doNoOp bool) error { stageFileName := stageFile.Name() if !t.keepStageFile { defer os.Remove(stageFileName) } log.Debugf("Comparing candidate config to %s", t.config.Dest) ok, err := isSameConfig(stageFileName, t.config.Dest) if err != nil { log.Error(err) return err } if doNoOp { log.Warnf("Noop mode enabled. %s will not be modified", t.config.Dest) return nil } if !ok { log.Infof("Target config %s out of sync", t.config.Dest) if t.config.CheckCmd != "" { if err := t.check(stageFileName); err != nil { return errors.New("Config check failed: " + err.Error()) } } log.Debugf("Overwriting target config %s", t.config.Dest) err := os.Rename(stageFileName, t.config.Dest) if err != nil { if strings.Contains(err.Error(), "device or resource busy") { log.Debugf("Rename failed - target is likely a mount.config. Trying to write instead") // try to open the file and write to it var contents []byte var rerr error contents, rerr = ioutil.ReadFile(stageFileName) if rerr != nil { return rerr } err := ioutil.WriteFile(t.config.Dest, contents, fileMode) // make sure owner and group match the temp file, in case the file was created with WriteFile os.Chown(t.config.Dest, t.config.Uid, t.config.Gid) if err != nil { return err } } else { return err } } if t.config.ReloadCmd != "" { if err := t.reload(); err != nil { return err } } log.Infof("Target config %s has been updated", t.config.Dest) } else { log.Debugf("Target config %s in sync", t.config.Dest) } return nil }