Skip to content

Commit

Permalink
alpha: initial critical support, build vars
Browse files Browse the repository at this point in the history
  • Loading branch information
23doors committed Nov 15, 2021
1 parent 9ec8504 commit 84ad644
Show file tree
Hide file tree
Showing 23 changed files with 222 additions and 111 deletions.
1 change: 1 addition & 0 deletions cmd/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func (e *Executor) newAppsCmd() *cobra.Command {
return config.ErrProjectConfigNotFound
}

// TODO: list apps from state as well
return actions.NewAppList(e.Log(), e.cfg, listOpts).Run(cmd.Context())
},
}
Expand Down
1 change: 1 addition & 0 deletions cmd/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ func (e *Executor) newDeployCmd() *cobra.Command {
f.BoolVar(&opts.Lock, "lock", true, "lock statefile during deploy")
f.DurationVar(&opts.LockWait, "lock-wait", 0, "wait for lock if it is already acquired")
f.BoolVar(&opts.AutoApprove, "yes", false, "auto approve changes")
f.BoolVar(&opts.ForceApprove, "force", false, "force approve even critical changes")
f.StringSliceVarP(&targetApps, "target-apps", "t", nil, "target only specified apps, can specify multiple or separate values with comma in a form of <app type>.<name>, e.g.: static.website,service.api")
f.StringSliceVarP(&skipApps, "skip-apps", "s", nil, "skip specified apps (if they exist), can specify multiple or separate values with comma in a form of <app type>.<name>, e.g.: static.website,service.api")
f.BoolVar(&opts.SkipAllApps, "skip-all-apps", false, "skip all apps (if they exist)")
Expand Down
9 changes: 7 additions & 2 deletions cmd/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ func (e *Executor) commandPreRun(ctx context.Context) error {
if cmd != nil {
skipLoadConfig = cmd.Annotations[cmdSkipLoadConfigAnnotation] == "1"
skipLoadApps = cmd.Annotations[cmdSkipLoadAppsAnnotation] == "1"
skipLoadPlugins = cmd.Annotations[cmdSkipLoadPluginsAnnotation] == "1"
skipCheckConfig = cmd.Annotations[cmdSkipCheckConfigAnnotation] == "1"

if skipLoadConfig {
Expand Down Expand Up @@ -121,7 +120,13 @@ func (e *Executor) commandPreRun(ctx context.Context) error {
}

// Augment/load new commands.
if skipLoadPlugins {
return e.addPluginsCommands(cmd)
}

func (e *Executor) addPluginsCommands(cmd *cobra.Command) error {
skipLoadPlugins := cmd.Annotations[cmdSkipLoadPluginsAnnotation] == "1"

if skipLoadPlugins || e.cfg == nil {
return nil
}

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ require (
github.com/mholt/archiver/v3 v3.5.1
github.com/mitchellh/go-homedir v1.1.0
github.com/otiai10/copy v1.6.0
github.com/outblocks/outblocks-plugin-go v0.0.0-20211112163452-b28f04e185aa
github.com/outblocks/outblocks-plugin-go v0.0.0-20211115132657-c42fed05643f
github.com/pterm/pterm v0.12.33
github.com/spf13/cobra v1.2.1
github.com/spf13/pflag v1.0.5
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -681,8 +681,8 @@ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
github.com/otiai10/mint v1.3.2 h1:VYWnrP5fXmz1MXvjuUvcBrXSjGE6xjON+axB/UrpO3E=
github.com/otiai10/mint v1.3.2/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
github.com/outblocks/outblocks-plugin-go v0.0.0-20211112163452-b28f04e185aa h1:Dqz3zbQTe4euERE1ufU9WQSswrKxrNxNhoOLL8nK6NI=
github.com/outblocks/outblocks-plugin-go v0.0.0-20211112163452-b28f04e185aa/go.mod h1:xosanOIc+3GlRA9b90/xRoYWzRgdhbew9T7+yTeI/TQ=
github.com/outblocks/outblocks-plugin-go v0.0.0-20211115132657-c42fed05643f h1:Z6bxU4JvJSYM5piFCRWB0VMWjcDIXTWWAUNcTyHSfsY=
github.com/outblocks/outblocks-plugin-go v0.0.0-20211115132657-c42fed05643f/go.mod h1:xosanOIc+3GlRA9b90/xRoYWzRgdhbew9T7+yTeI/TQ=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
Expand Down
2 changes: 1 addition & 1 deletion internal/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,5 @@ func IsTermDumb() bool {
return true
}

return true
return false
}
61 changes: 61 additions & 0 deletions internal/util/vars.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package util

import (
"fmt"
"regexp"
"strings"

"github.com/outblocks/outblocks-plugin-go/util"
)

var escapePercent = regexp.MustCompile(`%([^{]|$)`)

type VarEvaluator struct {
*util.BaseVarEvaluator
}

func NewVarEvaluator(vars map[string]interface{}) *VarEvaluator {
return &VarEvaluator{
BaseVarEvaluator: util.NewBaseVarEvaluator(vars).
WithEncoder(varEncoder).
WithVarChar('%').
WithIgnoreInvalid(true),
}
}

func varEncoder(input interface{}) ([]byte, error) {
switch input.(type) {
case string:
return []byte("%s"), nil
case int:
return []byte("%d"), nil
}

return nil, fmt.Errorf("unknown input type")
}

func (e *VarEvaluator) Expand(input string) (string, error) {
input = escapePercent.ReplaceAllString(input, "%$0")

format, params, err := e.ExpandRaw([]byte(input))
if err != nil {
return "", err
}

return fmt.Sprintf(strings.ReplaceAll(string(format), "%{", "%%{"), params...), nil
}

func (e *VarEvaluator) ExpandStringMap(input map[string]string) (map[string]string, error) {
out := make(map[string]string, len(input))

var err error

for k, v := range input {
out[k], err = e.Expand(v)
if err != nil {
return nil, err
}
}

return out, nil
}
8 changes: 4 additions & 4 deletions pkg/actions/app_add.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ func (d *AppAdd) prompt() (interface{}, error) {
return d.promptStatic()
case config.AppTypeService:
return d.promptService()
// TODO: add adding app function
// TODO: add adding function app
default:
return nil, fmt.Errorf("unsupported app type (WIP)")
}
Expand Down Expand Up @@ -518,10 +518,10 @@ func (d *AppAdd) promptStatic() (*staticAppInfo, error) {
AppType: config.AppTypeStatic,
AppURL: d.opts.URL,
AppDir: d.opts.Dir,
AppDeploy: &config.AppDeploy{
AppDeploy: &types.AppDeployInfo{
Plugin: d.opts.DeployPlugin,
},
AppRun: &config.AppRun{
AppRun: &types.AppRunInfo{
Plugin: d.opts.RunPlugin,
Command: d.opts.RunCommand,
},
Expand All @@ -545,7 +545,7 @@ func (d *AppAdd) promptService() (*serviceAppInfo, error) { // nolint: unparam
AppType: config.AppTypeService,
AppURL: d.opts.URL,
AppDir: d.opts.Dir,
AppRun: &config.AppRun{
AppRun: &types.AppRunInfo{
Command: d.opts.RunCommand,
},
},
Expand Down
3 changes: 2 additions & 1 deletion pkg/actions/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type DeployOptions struct {
Lock bool
LockWait time.Duration
AutoApprove bool
ForceApprove bool
TargetApps, SkipApps []string
SkipAllApps bool
}
Expand Down Expand Up @@ -144,7 +145,7 @@ func (d *Deploy) planAndApply(ctx context.Context, verify bool, state *types.Sta

spinner.Stop()

empty, canceled := planPrompt(d.log, deployChanges, dnsChanges, d.opts.AutoApprove)
empty, canceled := planPrompt(d.log, deployChanges, dnsChanges, d.opts.AutoApprove, d.opts.ForceApprove)

shouldApply := !canceled && !empty

Expand Down
49 changes: 44 additions & 5 deletions pkg/actions/deploy_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
dockerclient "github.com/docker/docker/client"
"github.com/outblocks/outblocks-cli/internal/util"
"github.com/outblocks/outblocks-cli/pkg/config"
"github.com/outblocks/outblocks-plugin-go/types"
plugin_util "github.com/outblocks/outblocks-plugin-go/util"
"github.com/outblocks/outblocks-plugin-go/util/errgroup"
"github.com/pterm/pterm"
Expand Down Expand Up @@ -112,9 +113,16 @@ func (d *Deploy) runAppBuildCommand(ctx context.Context, cmd *util.CmdInfo, app
return nil
}

func (d *Deploy) buildStaticApp(ctx context.Context, app *config.StaticApp) error {
func (d *Deploy) buildStaticApp(ctx context.Context, app *config.StaticApp, eval *util.VarEvaluator) error {
var err error

env := plugin_util.MergeStringMaps(app.Env(), app.Build.Env)

env, err = eval.ExpandStringMap(env)
if err != nil {
return err
}

cmd, err := util.NewCmdInfo(app.Build.Command, app.Dir(), util.FlattenEnvMap(env))
if err != nil {
return fmt.Errorf("error preparing build command for %s app: %s: %w", app.Type(), app.Name(), err)
Expand All @@ -123,7 +131,7 @@ func (d *Deploy) buildStaticApp(ctx context.Context, app *config.StaticApp) erro
return d.runAppBuildCommand(ctx, cmd, &app.BasicApp)
}

func (d *Deploy) buildServiceApp(ctx context.Context, app *config.ServiceApp) error {
func (d *Deploy) buildServiceApp(ctx context.Context, app *config.ServiceApp, eval *util.VarEvaluator) error {
dockercontext := filepath.Join(app.Dir(), app.Build.DockerContext)
dockercontext, ok := plugin_util.CheckDir(dockercontext)

Expand All @@ -142,7 +150,12 @@ func (d *Deploy) buildServiceApp(ctx context.Context, app *config.ServiceApp) er
return err
}

buildArgs := util.FlattenEnvMap(app.Build.DockerBuildArgs)
buildArgsMap, err := eval.ExpandStringMap(app.Build.DockerBuildArgs)
if err != nil {
return err
}

buildArgs := util.FlattenEnvMap(buildArgsMap)
for i, arg := range buildArgs {
buildArgs[i] = strings.ReplaceAll(arg, "\"", "\\\"")
}
Expand Down Expand Up @@ -183,6 +196,20 @@ type appBuilder struct {
}

func (d *Deploy) buildApps(ctx context.Context) error {
appTypeMap := make(map[string]*types.App)

if len(d.opts.TargetApps) != 0 || len(d.opts.SkipApps) != 0 {
// Get state apps as well.
state, _, err := getState(ctx, d.cfg, false, 0)
if err != nil {
return err
}

for _, app := range state.Apps {
appTypeMap[app.ID] = &app.App
}
}

var builders []*appBuilder

g, _ := errgroup.WithConcurrency(ctx, defaultConcurrency)
Expand All @@ -194,6 +221,8 @@ func (d *Deploy) buildApps(ctx context.Context) error {
var appsTemp []config.App

for _, app := range apps {
appTypeMap[app.ID()] = app.PluginType()

if len(targetAppIDsMap) > 0 && !targetAppIDsMap[app.ID()] {
continue
}
Expand All @@ -207,7 +236,17 @@ func (d *Deploy) buildApps(ctx context.Context) error {

apps = appsTemp

// Flatten appTypeMap.
appTypes := make([]*types.App, 0, len(appTypeMap))
for _, app := range appTypeMap {
appTypes = append(appTypes, app)
}

appVars := types.AppVarsFromApps(appTypes)

for _, app := range apps {
eval := util.NewVarEvaluator(types.VarsForApp(appVars, app.PluginType(), nil))

// TODO: add build app function
switch app.Type() {
case config.AppTypeStatic:
Expand All @@ -219,15 +258,15 @@ func (d *Deploy) buildApps(ctx context.Context) error {

builders = append(builders, &appBuilder{
app: a,
build: func() error { return d.buildStaticApp(ctx, a) },
build: func() error { return d.buildStaticApp(ctx, a, eval) },
})

case config.AppTypeService:
a := app.(*config.ServiceApp)

builders = append(builders, &appBuilder{
app: a,
build: func() error { return d.buildServiceApp(ctx, a) },
build: func() error { return d.buildServiceApp(ctx, a, eval) },
})
}
}
Expand Down
44 changes: 18 additions & 26 deletions pkg/actions/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import (
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"sync"
"time"

"github.com/outblocks/outblocks-cli/internal/urlutil"
"github.com/outblocks/outblocks-cli/internal/util"
"github.com/outblocks/outblocks-cli/pkg/actions/run"
"github.com/outblocks/outblocks-cli/pkg/clipath"
"github.com/outblocks/outblocks-cli/pkg/config"
Expand Down Expand Up @@ -147,7 +147,7 @@ func (d *Run) prepareRun(cfg *config.Project) (*runInfo, error) {

for _, app := range cfg.Apps {
if app.RunInfo().Command == "" {
return nil, app.YAMLError("$.run.command", "App.Run.Command is required to run app")
return nil, app.YAMLError("$.run.command", "run.command is required to run app")
}

runInfo := app.RunInfo()
Expand Down Expand Up @@ -224,49 +224,39 @@ func (d *Run) prepareRun(cfg *config.Project) (*runInfo, error) {

if _, ok := info.pluginDepsMap[runPlugin]; !ok {
info.pluginDepsMap[runPlugin] = &plugin_go.RunRequest{
Args: runPlugin.CommandArgs(runCommand),
Hosts: hosts,
Args: runPlugin.CommandArgs(runCommand),
}
}

info.pluginDepsMap[runPlugin].Dependencies = append(info.pluginDepsMap[runPlugin].Dependencies, depRun)
}

// Gather envs.
// TODO!: process envs similar as when deployment rather then have same for all, dont add anything automatically
env := make(map[string]string)

// Gather hosts.
for _, app := range info.apps {
prefix := app.App.EnvPrefix()

host, _ := urlutil.ExtractHostname(app.URL)
env[fmt.Sprintf("%sURL", prefix)] = app.URL

hosts[host] = app.IP
}

for _, dep := range info.deps {
// TODO: treat deps differently, only use these that were added as needs
// + add secrets from plugin.PrepareRunDependency()
prefix := dep.Dependency.EnvPrefix()
appVars := types.AppVarsFromAppRun(info.apps)

env[fmt.Sprintf("%sHOST", prefix)] = loopbackHost
env[fmt.Sprintf("%sPORT", prefix)] = strconv.Itoa(dep.Port)
}
// TODO: treat deps differently, run them first, inject secrets
// + add secrets from plugin.PrepareRunDependency()

// Fill envs per app/dep.
var err error

for _, app := range info.apps {
app.App.Env = plugin_util.MergeStringMaps(cfg.Defaults.Run.Env, app.App.Env, env)
eval := util.NewVarEvaluator(types.VarsForApp(appVars, app.App, nil))
app.App.Env = plugin_util.MergeStringMaps(cfg.Defaults.Run.Env, app.App.Env)

app.App.Env["URL"] = app.URL
app.App.Env["PORT"] = strconv.Itoa(app.Port)
app.App.Env, err = eval.ExpandStringMap(app.App.Env)
if err != nil {
return nil, err
}
}

for _, dep := range info.deps {
dep.Env = plugin_util.MergeStringMaps(cfg.Defaults.Run.Env, dep.Env, env)

dep.Env["IP"] = dep.IP
dep.Env["PORT"] = strconv.Itoa(dep.Port)
dep.Env = plugin_util.MergeStringMaps(cfg.Defaults.Run.Env, dep.Env)
}

return info, nil
Expand All @@ -293,6 +283,8 @@ func (d *Run) runAll(ctx context.Context, runInfo *runInfo) ([]*run.PluginRunRes
localRets []*run.LocalRunResult
)

// TODO: next - dependency env merging

// Process remote plugin dependencies.
if len(runInfo.pluginDepsMap) > 0 {
pluginRet, err := run.ThroughPlugin(ctx, runInfo.pluginDepsMap)
Expand Down
Loading

0 comments on commit 84ad644

Please sign in to comment.