github.com/mattevans/edward@v1.9.2/edward/client.go (about)

     1  package edward
     2  
     3  import (
     4  	"log"
     5  	"os"
     6  
     7  	"github.com/pkg/errors"
     8  	"github.com/mattevans/edward/builder"
     9  	"github.com/mattevans/edward/home"
    10  	"github.com/mattevans/edward/instance"
    11  	"github.com/mattevans/edward/output"
    12  	"github.com/mattevans/edward/services"
    13  	"github.com/mattevans/edward/tracker"
    14  	"github.com/mattevans/edward/ui"
    15  	"github.com/mattevans/edward/ui/terminal"
    16  	"github.com/mattevans/edward/worker"
    17  )
    18  
    19  type Client struct {
    20  	UI ui.Provider
    21  
    22  	Config string
    23  
    24  	ServiceChecks func([]services.ServiceOrGroup) error
    25  
    26  	EdwardExecutable string
    27  
    28  	LogFile string // Path to log file
    29  
    30  	Follower TaskFollower
    31  
    32  	// Prevent build, launch and stop phases from running concurrently
    33  	DisableConcurrentPhases bool
    34  
    35  	WorkingDir string
    36  
    37  	telemetryScript string
    38  
    39  	basePath   string
    40  	groupMap   map[string]*services.ServiceGroupConfig
    41  	serviceMap map[string]*services.ServiceConfig
    42  
    43  	Tags []string // Tags to distinguish runners started by this instance of edward
    44  
    45  	DirConfig *home.EdwardConfiguration
    46  
    47  	Backends map[string]string // Service overrides for backends
    48  }
    49  
    50  type TaskFollower interface {
    51  	Handle(update tracker.Task)
    52  	Done()
    53  }
    54  
    55  // NewClient creates an edward client an empty configuration
    56  func NewClient() (*Client, error) {
    57  	wd, err := os.Getwd()
    58  	if err != nil {
    59  		return nil, errors.WithStack(err)
    60  	}
    61  
    62  	dirCfg, err := home.NewConfiguration("")
    63  	if err != nil {
    64  		return nil, errors.WithStack(err)
    65  	}
    66  
    67  	return &Client{
    68  		UI:         &terminal.Provider{},
    69  		Follower:   output.NewFollower(),
    70  		WorkingDir: wd,
    71  		groupMap:   make(map[string]*services.ServiceGroupConfig),
    72  		serviceMap: make(map[string]*services.ServiceConfig),
    73  		DirConfig:  dirCfg,
    74  	}, nil
    75  }
    76  
    77  // NewClientWithConfig creates an Edward client and loads the config from the given path
    78  func NewClientWithConfig(configPath, version string) (*Client, error) {
    79  	wd, err := os.Getwd()
    80  	if err != nil {
    81  		return nil, errors.WithStack(err)
    82  	}
    83  
    84  	dirCfg, err := home.NewConfiguration("")
    85  	if err != nil {
    86  		return nil, errors.WithStack(err)
    87  	}
    88  
    89  	client := &Client{
    90  		UI:         &terminal.Provider{},
    91  		Follower:   output.NewFollower(),
    92  		WorkingDir: wd,
    93  		Config:     configPath,
    94  		groupMap:   make(map[string]*services.ServiceGroupConfig),
    95  		serviceMap: make(map[string]*services.ServiceConfig),
    96  		DirConfig:  dirCfg,
    97  	}
    98  	err = client.LoadConfig(version)
    99  	return client, errors.WithStack(err)
   100  }
   101  
   102  func (c *Client) BasePath() string {
   103  	return c.basePath
   104  }
   105  
   106  func (c *Client) ServiceMap() map[string]*services.ServiceConfig {
   107  	return c.serviceMap
   108  }
   109  
   110  func (c *Client) startAndTrack(sgs []services.ServiceOrGroup, skipBuild bool, noWatch bool, exclude []string, edwardExecutable string) error {
   111  	cfg := services.OperationConfig{
   112  		WorkingDir:       c.WorkingDir,
   113  		EdwardExecutable: edwardExecutable,
   114  		Exclusions:       exclude,
   115  		SkipBuild:        skipBuild,
   116  		NoWatch:          noWatch,
   117  		Tags:             c.Tags,
   118  		LogFile:          c.LogFile,
   119  		Backends:         c.Backends,
   120  	}
   121  
   122  	task := tracker.NewTask(c.Follower.Handle)
   123  	defer c.Follower.Done()
   124  
   125  	poolSize := 1
   126  	if c.DisableConcurrentPhases {
   127  		poolSize = 0
   128  	}
   129  	p := worker.NewPool(poolSize)
   130  	p.Start()
   131  	var err error
   132  	err = services.DoForServices(sgs, task, func(s *services.ServiceConfig, overrides services.ContextOverride, task tracker.Task) error {
   133  		if skipBuild {
   134  			log.Println("skipping build phase")
   135  			err = instance.Launch(c.DirConfig, s, cfg, overrides, task, p)
   136  			if err != nil {
   137  				return errors.WithMessage(err, "Error launching "+s.GetName())
   138  			}
   139  		} else {
   140  			if cfg.IsExcluded(s) {
   141  				return nil
   142  			}
   143  			log.Printf("Building: %s", s.Name)
   144  			b := builder.New(cfg, overrides)
   145  			err := b.Build(c.DirConfig, task, s)
   146  			if err != nil {
   147  				return errors.WithMessage(err, "Error starting "+s.GetName()+": build")
   148  			}
   149  			log.Printf("Launching: %s", s.Name)
   150  			err = instance.Launch(c.DirConfig, s, cfg, overrides, task, p)
   151  			return errors.WithMessage(err, "Error starting "+s.GetName()+": launch")
   152  		}
   153  		return nil
   154  	})
   155  	if err != nil {
   156  		return errors.WithStack(err)
   157  	}
   158  
   159  	p.Stop()
   160  	_ = <-p.Complete()
   161  	return p.Err()
   162  }
   163  
   164  func (c *Client) askForConfirmation(question string) bool {
   165  
   166  	// Skip confirmations for children. For example, for enabling sudo.
   167  	isChild := os.Getenv("ISCHILD")
   168  	if isChild != "" {
   169  		return true
   170  	}
   171  
   172  	return c.UI.Confirm(question)
   173  }