github.com/grahambrereton-form3/tilt@v0.10.18/internal/cli/up.go (about)

     1  package cli
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  	"net/url"
     8  	"os"
     9  	"time"
    10  
    11  	"github.com/fatih/color"
    12  	"github.com/mattn/go-isatty"
    13  	"github.com/opentracing/opentracing-go"
    14  	"github.com/spf13/cobra"
    15  	"golang.org/x/sync/errgroup"
    16  	"k8s.io/klog"
    17  
    18  	"github.com/windmilleng/tilt/internal/analytics"
    19  	"github.com/windmilleng/tilt/internal/engine"
    20  	engineanalytics "github.com/windmilleng/tilt/internal/engine/analytics"
    21  	"github.com/windmilleng/tilt/internal/hud"
    22  	"github.com/windmilleng/tilt/internal/k8s"
    23  	"github.com/windmilleng/tilt/internal/output"
    24  	"github.com/windmilleng/tilt/internal/store"
    25  	"github.com/windmilleng/tilt/internal/tiltfile"
    26  	"github.com/windmilleng/tilt/internal/tracer"
    27  	"github.com/windmilleng/tilt/pkg/assets"
    28  	"github.com/windmilleng/tilt/pkg/logger"
    29  	"github.com/windmilleng/tilt/pkg/model"
    30  	"github.com/windmilleng/tilt/web"
    31  )
    32  
    33  const DefaultWebHost = "localhost"
    34  const DefaultWebPort = 10350
    35  const DefaultWebDevPort = 46764
    36  
    37  var updateModeFlag string = string(engine.UpdateModeAuto)
    38  var webModeFlag model.WebMode = model.DefaultWebMode
    39  var webPort = 0
    40  var webHost = DefaultWebHost
    41  var webDevPort = 0
    42  var noBrowser bool = false
    43  var logActionsFlag bool = false
    44  
    45  type upCmd struct {
    46  	watch     bool
    47  	traceTags string
    48  	hud       bool
    49  	fileName  string
    50  }
    51  
    52  func (c *upCmd) register() *cobra.Command {
    53  	cmd := &cobra.Command{
    54  		Use:   "up [<resource_name1>] [<resource_name2>] [...]",
    55  		Short: "stand up one or more resources (if no resource names specified, stand up all known resources specified in Tiltfile)",
    56  	}
    57  
    58  	cmd.Flags().BoolVar(&c.watch, "watch", true, "If true, services will be automatically rebuilt and redeployed when files change. Otherwise, each service will be started once.")
    59  	cmd.Flags().Var(&webModeFlag, "web-mode", "Values: local, prod. Controls whether to use prod assets or a local dev server")
    60  	cmd.Flags().StringVar(&updateModeFlag, "update-mode", string(engine.UpdateModeAuto),
    61  		fmt.Sprintf("Control the strategy Tilt uses for updating instances. Possible values: %v", engine.AllUpdateModes))
    62  	cmd.Flags().StringVar(&c.traceTags, "traceTags", "", "tags to add to spans for easy querying, of the form: key1=val1,key2=val2")
    63  	cmd.Flags().BoolVar(&c.hud, "hud", true, "If true, tilt will open in HUD mode.")
    64  	cmd.Flags().BoolVar(&logActionsFlag, "logactions", false, "log all actions and state changes")
    65  	cmd.Flags().IntVar(&webPort, "port", DefaultWebPort, "Port for the Tilt HTTP server. Set to 0 to disable.")
    66  	cmd.Flags().StringVar(&webHost, "host", DefaultWebHost, "Host for the Tilt HTTP server. Set to 0.0.0.0 to listen on all interfaces.")
    67  	cmd.Flags().IntVar(&webDevPort, "webdev-port", DefaultWebDevPort, "Port for the Tilt Dev Webpack server. Only applies when using --web-mode=local")
    68  	cmd.Flags().Lookup("logactions").Hidden = true
    69  	cmd.Flags().StringVar(&c.fileName, "file", tiltfile.FileName, "Path to Tiltfile")
    70  	cmd.Flags().BoolVar(&noBrowser, "no-browser", false, "If true, web UI will not open on startup.")
    71  
    72  	return cmd
    73  }
    74  
    75  func (c *upCmd) run(ctx context.Context, args []string) error {
    76  	a := analytics.Get(ctx)
    77  	cmdUpTags := engineanalytics.CmdUpTags(map[string]string{
    78  		"watch": fmt.Sprintf("%v", c.watch),
    79  		"mode":  string(updateModeFlag),
    80  	})
    81  	a.Incr("cmd.up", cmdUpTags.AsMap())
    82  	a.IncrIfUnopted("analytics.up.optdefault")
    83  	defer a.Flush(time.Second)
    84  
    85  	span, ctx := opentracing.StartSpanFromContext(ctx, "Up")
    86  	defer span.Finish()
    87  
    88  	tags := tracer.TagStrToMap(c.traceTags)
    89  
    90  	for k, v := range tags {
    91  		span.SetTag(k, v)
    92  	}
    93  
    94  	deferred := logger.NewDeferredLogger(ctx)
    95  	ctx = redirectLogs(ctx, deferred)
    96  
    97  	logOutput(fmt.Sprintf("Starting Tilt (%s)…", buildStamp()))
    98  
    99  	if analytics.IsAnalyticsDisabledFromEnv() {
   100  		logOutput("Tilt analytics manually disabled by environment")
   101  	}
   102  
   103  	threads, err := wireCmdUp(ctx, a, cmdUpTags)
   104  	if err != nil {
   105  		deferred.SetOutput(deferred.Original())
   106  		return err
   107  	}
   108  
   109  	upper := threads.upper
   110  	h := threads.hud
   111  
   112  	l := store.NewLogActionLogger(ctx, upper.Dispatch)
   113  	deferred.SetOutput(l)
   114  	ctx = redirectLogs(ctx, l)
   115  
   116  	if trace {
   117  		traceID, err := tracer.TraceID(ctx)
   118  		if err != nil {
   119  			return err
   120  		}
   121  		logger.Get(ctx).Infof("TraceID: %s", traceID)
   122  	}
   123  
   124  	g, ctx := errgroup.WithContext(ctx)
   125  	ctx, cancel := context.WithCancel(ctx)
   126  	defer cancel()
   127  
   128  	hudEnabled := c.hud && isatty.IsTerminal(os.Stdout.Fd())
   129  	if hudEnabled {
   130  		err := output.CaptureAllOutput(logger.Get(ctx).Writer(logger.InfoLvl))
   131  		if err != nil {
   132  			logger.Get(ctx).Infof("Error capturing stdout and stderr: %v", err)
   133  		}
   134  		g.Go(func() error {
   135  			err := h.Run(ctx, upper.Dispatch, hud.DefaultRefreshInterval)
   136  			return err
   137  		})
   138  	}
   139  
   140  	g.Go(func() error {
   141  		defer cancel()
   142  		return upper.Start(ctx, args, threads.tiltBuild, c.watch, c.fileName, hudEnabled, a.UserOpt(), threads.token, string(threads.cloudAddress))
   143  	})
   144  
   145  	err = g.Wait()
   146  	if err != context.Canceled {
   147  		return err
   148  	} else {
   149  		return nil
   150  	}
   151  }
   152  
   153  func redirectLogs(ctx context.Context, l logger.Logger) context.Context {
   154  	ctx = logger.WithLogger(ctx, l)
   155  	log.SetOutput(l.Writer(logger.InfoLvl))
   156  	klog.SetOutput(l.Writer(logger.InfoLvl))
   157  	return ctx
   158  }
   159  
   160  func logOutput(s string) {
   161  	log.SetFlags(log.Flags() &^ (log.Ldate | log.Ltime))
   162  	log.Print(color.GreenString(s))
   163  }
   164  
   165  func provideUpdateModeFlag() engine.UpdateModeFlag {
   166  	return engine.UpdateModeFlag(updateModeFlag)
   167  }
   168  
   169  func provideLogActions() store.LogActionsFlag {
   170  	return store.LogActionsFlag(logActionsFlag)
   171  }
   172  
   173  func provideKubectlLogLevel() k8s.KubectlLogLevel {
   174  	return k8s.KubectlLogLevel(klogLevel)
   175  }
   176  
   177  func provideWebMode(b model.TiltBuild) (model.WebMode, error) {
   178  	switch webModeFlag {
   179  	case model.LocalWebMode, model.ProdWebMode, model.PrecompiledWebMode:
   180  		return webModeFlag, nil
   181  	case model.DefaultWebMode:
   182  		if b.Dev {
   183  			return model.LocalWebMode, nil
   184  		} else {
   185  			return model.ProdWebMode, nil
   186  		}
   187  	}
   188  	return "", model.UnrecognizedWebModeError(string(webModeFlag))
   189  }
   190  
   191  func provideWebHost() model.WebHost {
   192  	return model.WebHost(webHost)
   193  }
   194  
   195  func provideWebPort() model.WebPort {
   196  	return model.WebPort(webPort)
   197  }
   198  
   199  func provideNoBrowserFlag() model.NoBrowser {
   200  	return model.NoBrowser(noBrowser)
   201  }
   202  
   203  func provideWebURL(webHost model.WebHost, webPort model.WebPort) (model.WebURL, error) {
   204  	if webPort == 0 {
   205  		return model.WebURL{}, nil
   206  	}
   207  
   208  	u, err := url.Parse(fmt.Sprintf("http://%s:%d/", webHost, webPort))
   209  	if err != nil {
   210  		return model.WebURL{}, err
   211  	}
   212  	return model.WebURL(*u), nil
   213  }
   214  
   215  func provideAssetServer(mode model.WebMode, version model.WebVersion) (assets.Server, error) {
   216  	if mode == model.ProdWebMode {
   217  		return assets.NewProdServer(assets.ProdAssetBucket, version)
   218  	}
   219  	if mode == model.PrecompiledWebMode || mode == model.LocalWebMode {
   220  		path, err := web.StaticPath()
   221  		if err != nil {
   222  			return nil, err
   223  		}
   224  		pkgDir := assets.PackageDir(path)
   225  		if mode == model.PrecompiledWebMode {
   226  			return assets.NewPrecompiledServer(pkgDir), nil
   227  		} else {
   228  			return assets.NewDevServer(pkgDir, model.WebDevPort(webDevPort))
   229  		}
   230  	}
   231  	return nil, model.UnrecognizedWebModeError(string(mode))
   232  }