up.go

Summary

Maintainability
A
0 mins
Test Coverage
package up

import (
    "io"
    "os"
    "os/exec"
    "time"

    "github.com/apex/log"
    "github.com/pkg/errors"

    "github.com/apex/up/config"
    "github.com/apex/up/internal/util"
    "github.com/apex/up/platform/event"
)

// Config for a project.
type Config = config.Config

// ReadConfig reads the configuration from `path`.
var ReadConfig = config.ReadConfig

// ParseConfigString returns config from JSON string.
var ParseConfigString = config.ParseConfigString

// MustParseConfigString returns config from JSON string.
var MustParseConfigString = config.MustParseConfigString

// Project manager.
type Project struct {
    Platform
    config *Config
    events event.Events
}

// New project.
func New(c *Config, events event.Events) *Project {
    return &Project{
        config: c,
        events: events,
    }
}

// WithPlatform to `platform`.
func (p *Project) WithPlatform(platform Platform) *Project {
    p.Platform = platform
    return p
}

// RunHook runs a hook by name.
func (p *Project) RunHook(name string) error {
    hook := p.config.Hooks.Get(name)

    if hook.IsEmpty() {
        log.Debugf("hook %s is not defined", name)
        return nil
    }

    defer p.events.Time("hook", event.Fields{
        "name": name,
        "hook": hook,
    })()

    for _, command := range hook {
        log.Debugf("hook %q command %q", name, command)

        cmd := exec.Command("sh", "-c", command)
        cmd.Env = os.Environ()
        cmd.Env = append(cmd.Env, util.Env(p.config.Environment)...)
        cmd.Env = append(cmd.Env, "PATH=node_modules/.bin:"+os.Getenv("PATH"))

        b, err := cmd.CombinedOutput()
        if err != nil {
            return errors.Errorf("%q: %s", command, b)
        }
    }

    return nil
}

// RunHooks runs hooks by name.
func (p *Project) RunHooks(names ...string) error {
    for _, n := range names {
        if err := p.RunHook(n); err != nil {
            return errors.Wrapf(err, "%q hook", n)
        }
    }
    return nil
}

// Build the project.
func (p *Project) Build(hooks bool) error {
    defer p.events.Time("platform.build", nil)()

    if hooks {
        if err := p.RunHooks("prebuild", "build"); err != nil {
            return err
        }
    }

    if err := p.Platform.Build(); err != nil {
        return errors.Wrap(err, "building")
    }

    if hooks {
        return p.RunHooks("postbuild")
    }

    return nil
}

// Deploy the project.
func (p *Project) Deploy(d Deploy) error {
    defer p.events.Time("deploy", event.Fields{
        "commit": d.Commit,
        "stage":  d.Stage,
    })()

    if err := p.Build(d.Build); err != nil {
        return errors.Wrap(err, "building")
    }

    if err := p.deploy(d); err != nil {
        return errors.Wrap(err, "deploying")
    }

    if d.Build {
        if err := p.RunHook("clean"); err != nil {
            return errors.Wrap(err, "clean hook")
        }
    }

    return nil
}

// deploy stage.
func (p *Project) deploy(d Deploy) error {
    if err := p.RunHooks("predeploy", "deploy"); err != nil {
        return err
    }

    if err := p.Platform.Deploy(d); err != nil {
        return err
    }

    return p.RunHooks("postdeploy")
}

// Zip returns the zip if supported by the platform.
func (p *Project) Zip() (io.Reader, error) {
    z, ok := p.Platform.(Zipper)
    if !ok {
        return nil, errors.Errorf("platform does not support zips")
    }

    return z.Zip(), nil
}

// Init initializes the runtime such as remote environment variables.
func (p *Project) Init(stage string) error {
    r, ok := p.Platform.(Runtime)
    if !ok {
        return nil
    }

    return r.Init(stage)
}

// CreateStack implementation.
func (p *Project) CreateStack(region, version string) error {
    defer p.events.Time("stack.create", event.Fields{
        "region":  region,
        "version": version,
    })()

    return p.Platform.CreateStack(region, version)
}

// DeleteStack implementation.
func (p *Project) DeleteStack(region string, wait bool) error {
    defer p.events.Time("stack.delete", event.Fields{
        "region": region,
    })()

    return p.Platform.DeleteStack(region, wait)
}

// ShowStack implementation.
func (p *Project) ShowStack(region string) error {
    defer p.events.Time("stack.show", event.Fields{
        "region": region,
    })()

    return p.Platform.ShowStack(region)
}

// ShowMetrics implementation.
func (p *Project) ShowMetrics(region, stage string, start time.Time) error {
    defer p.events.Time("metrics", event.Fields{
        "region": region,
        "stage":  stage,
        "start":  start,
    })()

    return p.Platform.ShowMetrics(region, stage, start)
}

// PlanStack implementation.
func (p *Project) PlanStack(region string) error {
    defer p.events.Time("stack.plan", event.Fields{
        "region": region,
    })()

    return p.Platform.PlanStack(region)
}

// ApplyStack implementation.
func (p *Project) ApplyStack(region string) error {
    defer p.events.Time("stack.apply", event.Fields{
        "region": region,
    })()

    return p.Platform.ApplyStack(region)
}

// Prune implementation.
func (p *Project) Prune(region, stage string, versions int) error {
    pruner, ok := p.Platform.(Pruner)
    if !ok {
        return errors.Errorf("platform does not support pruning")
    }

    return pruner.Prune(region, stage, versions)
}