예제 #1
0
// 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
}
예제 #2
0
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
}
예제 #3
0
// 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
}