scratch/scratch.go

Summary

Maintainability
A
35 mins
Test Coverage
package scratch

import (
    "io/ioutil"
    "os"
    "path/filepath"
    "strings"

    "github.com/efritz/ij/logging"
    "github.com/efritz/ij/paths"
    "github.com/efritz/ij/util"
)

type ScratchSpace struct {
    project       string
    scratch       string
    runpath       string
    workspace     string
    keepWorkspace bool
}

const (
    ScratchDir   = ".ij"
    WorkspaceDir = "workspace"
    ScriptsDir   = "scripts"
    LogsDir      = "logs"
    OutLogSuffix = ".out.log"
    ErrLogSuffix = ".err.log"
)

func NewScratchSpace(runID, projectDir, scratchRoot string, keepWorkspace bool) *ScratchSpace {
    var (
        scratch   = filepath.Join(scratchRoot, ScratchDir)
        runpath   = filepath.Join(scratch, runID)
        workspace = filepath.Join(runpath, WorkspaceDir)
    )

    return &ScratchSpace{
        project:       projectDir,
        scratch:       scratch,
        runpath:       runpath,
        workspace:     workspace,
        keepWorkspace: keepWorkspace,
    }
}

func (s *ScratchSpace) Setup() error {
    return paths.EnsureDirExists(s.workspace, os.ModePerm)
}

func (s *ScratchSpace) Teardown() error {
    if err := os.RemoveAll(s.runpath); err != nil {
        return err
    }

    entries, err := paths.DirContents(s.scratch)
    if err != nil {
        return err
    }

    if len(entries) == 0 {
        return os.RemoveAll(s.scratch)
    }

    return nil
}

func (s *ScratchSpace) Project() string {
    return s.project
}

func (s *ScratchSpace) Scratch() string {
    return s.scratch
}

func (s *ScratchSpace) Runpath() string {
    return s.runpath
}

func (s *ScratchSpace) Workspace() string {
    return s.workspace
}

func (s *ScratchSpace) WriteScript(script string) (string, error) {
    scriptID, err := util.MakeID()
    if err != nil {
        return "", err
    }

    path, err := buildPath(filepath.Join(s.runpath, ScriptsDir, scriptID))
    if err != nil {
        return "", err
    }

    if err := ioutil.WriteFile(path, []byte(script), 0700); err != nil {
        return "", err
    }

    return path, nil
}

func (s *ScratchSpace) MakeLogFiles(prefix string) (*os.File, *os.File, error) {
    outpath, err := buildPath(filepath.Join(s.runpath, LogsDir, prefix+OutLogSuffix))
    if err != nil {
        return nil, nil, err
    }

    errpath, err := buildPath(filepath.Join(s.runpath, LogsDir, prefix+ErrLogSuffix))
    if err != nil {
        return nil, nil, err
    }

    outfile, err := os.Create(outpath)
    if err != nil {
        return nil, nil, err
    }

    errfile, err := os.Create(errpath)
    if err != nil {
        outfile.Close()
        return nil, nil, err
    }

    return outfile, errfile, nil
}

func (s *ScratchSpace) Prune(logger logging.Logger) error {
    if !s.keepWorkspace {
        logger.Debug(
            nil,
            "Removing workspace",
        )

        if err := os.RemoveAll(s.workspace); err != nil {
            return err
        }

        logger.Debug(
            nil,
            "Removing scripts directory",
        )

        if err := os.RemoveAll(filepath.Join(s.runpath, ScriptsDir)); err != nil {
            return err
        }
    }

    logger.Debug(
        nil,
        "Pruning empty logfiles",
    )

    return filepath.Walk(filepath.Join(s.runpath, LogsDir), func(path string, _ os.FileInfo, err error) error {
        if strings.HasSuffix(path, ErrLogSuffix) {
            info, err := os.Stat(path)
            if err != nil {
                return err
            }

            if info.Size() == 0 {
                if err := os.Remove(path); err != nil {
                    return err
                }
            }
        }

        return nil
    })
}

//
// Helpers

func buildPath(path string) (string, error) {
    if err := paths.EnsureParentExists(path, os.ModePerm); err != nil {
        return "", err
    }

    return path, nil
}