示例#1
0
// Backups returns a slice of all app backup timestamps.
func (a *App) Backups() ([]string, error) {
	dir := a.BackupDirectoryPath()

	// If the backup folder does not exists, then return nil.
	e, err := utils.Exists(dir)
	if err != nil {
		return nil, err
	} else if !e {
		return nil, nil
	}

	// Get all files in the apps directory.
	files, err := ioutil.ReadDir(dir)
	if err != nil {
		return nil, err
	}

	backups := make([]string, len(files))

	// Get all the backup timestampts.
	for i, f := range files {
		// Skip if not a directory.
		if !f.IsDir() {
			continue
		}

		backups[i] = f.Name()
	}

	return backups, nil
}
示例#2
0
// hostFingerprintExists checks whenever a host fingerprint exists.
func hostFingerprintExists(host string) (bool, error) {
	// Get the know_hosts file path.
	path := config.Config.KnownHostsFilePath()

	// Check if file exists.
	e, err := utils.Exists(path)
	if err != nil {
		return false, err
	} else if !e {
		return false, nil
	}

	// Open the file.
	file, err := os.Open(path)
	if err != nil {
		return false, err
	}
	defer file.Close()

	// Read the file line by line.
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		// Check if the host exists.
		if strings.HasPrefix(strings.TrimSpace(scanner.Text()), host) {
			return true, nil
		}
	}

	// Check if an error occurred.
	if err = scanner.Err(); err != nil {
		return false, err
	}

	return false, nil
}
示例#3
0
// Snapshot create a btrfs snapshot of a subvolume.
func Snapshot(subvolumeDir string, snapshotDir string, readonly bool) error {
	// Check if the passed subvolume directory is a btrfs subvolume.
	if !IsSubvolume(subvolumeDir) {
		return fmt.Errorf("failed to create btrfs snapshot: the subvolume directory '%s' is not a btrfs subvolume!", subvolumeDir)
	}

	// The destination snapshot directory should not exist!
	e, err := utils.Exists(snapshotDir)
	if err != nil {
		return err
	} else if e {
		return fmt.Errorf("failed to create btrfs snapshot: the snapshot directory '%s' already exists!", snapshotDir)
	}

	// Create the snapshot directory.
	if readonly {
		err = utils.RunCommand("btrfs", "subvolume", "snapshot", "-r", subvolumeDir, snapshotDir)
	} else {
		err = utils.RunCommand("btrfs", "subvolume", "snapshot", subvolumeDir, snapshotDir)
	}
	if err != nil {
		return fmt.Errorf("failed to create btrfs snapshot '%s': %v", snapshotDir, err)
	}

	// Force changed blocks to disk, update the super block.
	err = utils.RunCommand("sync")
	if err != nil {
		return fmt.Errorf("failed to force changed blocks to disk, update the super block: %v", err)
	}

	return nil
}
示例#4
0
// restoreState loads the turtle state file if present and
// restores the previous running apps.
func restoreState() error {
	var s state

	// Set the turtle state file path.
	statePath := config.Config.StateFilePath()

	// Skip if it does not exists.
	e, err := utils.Exists(statePath)
	if err != nil {
		return err
	} else if !e {
		return nil
	}

	// Load and decode the file.
	_, err = toml.DecodeFile(statePath, &s)
	if err != nil {
		return fmt.Errorf("failed to load turtle state file '%s': %v", statePath, err)
	}

	// Get all apps.
	curApps := apps.Apps()

	// Collect all errors during startup.
	var allErr string

	// Start the apps which where running during the last turtle daemon shutdown.
	for _, rApp := range s.RunningApps {
		for _, app := range curApps {
			// Skip if this is not our searched app or if it is already running.
			if rApp != app.Name() || app.IsRunning() {
				continue
			}

			// Start the app.
			err = app.Start()
			if err != nil {
				allErr += err.Error() + "\n"
			}
		}
	}

	// Trim the new linew at the end.
	allErr = strings.TrimSpace(allErr)

	// Return the error(s) if present.
	if len(allErr) > 0 {
		return fmt.Errorf(allErr)
	}

	return nil
}
示例#5
0
// Remove the app.
func (a *App) Remove(removeBackups bool) error {
	// Lock the task mutex.
	// The app should not be started during a remove process.
	a.taskMutex.Lock()
	defer a.taskMutex.Unlock()

	// Abort if any app task is running.
	if a.IsTaskRunning() {
		return fmt.Errorf("the app is running!")
	}

	if !removeBackups {
		// Create a backup first.
		// Call the private method, because we locked the taskMutex already.
		err := a.backup()
		if err != nil {
			return err
		}
	}

	// Remove the app subvolume if it exists.
	e, err := utils.Exists(a.path)
	if err != nil {
		return err
	} else if e {
		if err = btrfs.DeleteSubvolume(a.path); err != nil {
			return err
		}
	}

	func() {
		// Lock the apps mutex.
		appsMutex.Lock()
		defer appsMutex.Unlock()

		// Remove the app from the apps map.
		delete(apps, a.name)
	}()

	// Remove all backups if requested.
	if removeBackups {
		if err = a.RemoveAllBackups(); err != nil {
			return err
		}
	}

	return nil
}
示例#6
0
// Turtlefile returns the app's turtlefile.
func (a *App) Turtlefile() (*turtlefile.Turtlefile, error) {
	// Lock the mutex.
	a.turtlefileMutex.Lock()
	defer a.turtlefileMutex.Unlock()

	// Only load the turtlefile if not already loaded.
	if a.turtlefile != nil {
		return a.turtlefile, nil
	}

	// Obtain the turtlefile path.
	turtlefilePath := a.SourceDirectoryPath() + "/" + turtlefile.TurtlefileFilename
	tStat, err := os.Stat(turtlefilePath)
	if os.IsNotExist(err) {
		return nil, fmt.Errorf("Turtlefile is missing in source directory!")
	} else if err != nil {
		return nil, fmt.Errorf("failed to obtain state of file '%s': %v", turtlefilePath, err)
	}

	// If the path is a directory, then check if the turtlefile exists in it.
	if tStat.IsDir() {
		turtlefilePath += "/" + turtlefile.TurtlefileFilename
		e, err := utils.Exists(turtlefilePath)
		if err != nil {
			return nil, fmt.Errorf("failed to check if file exists '%s': %v", turtlefilePath, err)
		} else if !e {
			return nil, fmt.Errorf("Turtlefile is missing in source directory!")
		}
	}

	// Load the turtlefile.
	t, err := turtlefile.Load(turtlefilePath)
	if err != nil {
		return nil, err
	}

	// Check if the turtlefile is valid.
	if err = t.IsValid(); err != nil {
		return nil, fmt.Errorf("the turtlefile is invalid: %v", err)
	}

	// Set the app's turtlefile pointer.
	a.turtlefile = t

	return t, nil
}
示例#7
0
// Add and register a new turtle App.
// A turtle App name should not not contain any whitespaces.
func Add(name, sourceURL, branch string) (err error) {
	var a *App

	// Cleanup on any error.
	defer func() {
		if err == nil || a == nil {
			return
		}

		// Remove the app subvolume if it exists.
		e, errC := utils.Exists(a.path)
		if errC != nil {
			log.Errorf("failed to cleanup failed add app action: %v", errC)
		} else if e {
			if errC = btrfs.DeleteSubvolume(a.path); errC != nil {
				log.Errorf("failed to cleanup failed add app action: %v", errC)
			}
		}
	}()

	// Lock the mutex.
	appsMutex.Lock()
	defer appsMutex.Unlock()

	// Check if an app with the same name already exists.
	_, ok := apps[name]
	if ok {
		return fmt.Errorf("an App with the name '%s' already exists!", name)
	}

	// Create a new app value.
	a, err = newApp(name)
	if err != nil {
		return fmt.Errorf("failed to create add: %v", err)
	}

	// Set the app's settings.
	a.settings.SourceURL = sourceURL
	a.settings.Branch = branch

	// Create the app's subvolume.
	if err = btrfs.CreateSubvolume(a.path); err != nil {
		return fmt.Errorf("failed to prepare app's environment: %v", err)
	}

	// Save the app's settings to the app settings file.
	if err = a.saveSettings(); err != nil {
		return fmt.Errorf("failed to prepare app's environment: %v", err)
	}

	// Create the source directory.
	err = utils.MkDirIfNotExists(a.SourceDirectoryPath())
	if err != nil {
		return fmt.Errorf("failed to prepare app's environment: %v", err)
	}

	// Clone the source in a new task.
	if err = a.cloneSource(); err != nil {
		return err
	}

	// Finally add the app to the map.
	apps[name] = a

	return nil
}