github.com/henvic/wedeploycli@v1.7.6-0.20200319005353-3630f582f284/command/deploy/remote/remote.go (about)

     1  package deployremote
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"math/rand"
     8  	"path/filepath"
     9  	"regexp"
    10  	"strings"
    11  
    12  	"github.com/henvic/wedeploycli/color"
    13  	"github.com/henvic/wedeploycli/command/canceled"
    14  	"github.com/henvic/wedeploycli/command/deploy/internal/getproject"
    15  	"github.com/henvic/wedeploycli/command/internal/we"
    16  	"github.com/henvic/wedeploycli/deployment"
    17  	"github.com/henvic/wedeploycli/deployment/transport/git"
    18  	"github.com/henvic/wedeploycli/deployment/transport/gogit"
    19  	"github.com/henvic/wedeploycli/fancy"
    20  	"github.com/henvic/wedeploycli/inspector"
    21  	"github.com/henvic/wedeploycli/isterm"
    22  	"github.com/henvic/wedeploycli/namesgenerator"
    23  	"github.com/henvic/wedeploycli/services"
    24  	"github.com/henvic/wedeploycli/verbose"
    25  )
    26  
    27  // RemoteDeployment of services
    28  type RemoteDeployment struct {
    29  	Params deployment.Params
    30  
    31  	Experimental bool
    32  
    33  	path     string
    34  	services services.ServiceInfoList
    35  	remap    []string
    36  	ctx      context.Context
    37  }
    38  
    39  // Feedback about a deployment.
    40  type Feedback struct {
    41  	GroupUID string
    42  	Services services.ServiceInfoList
    43  }
    44  
    45  // Run does the remote deployment procedures
    46  func (rd *RemoteDeployment) Run(ctx context.Context) (f Feedback, err error) {
    47  	rd.ctx = ctx
    48  	wectx := we.Context()
    49  
    50  	if rd.path, err = getWorkingDirectory(); err != nil {
    51  		return f, err
    52  	}
    53  
    54  	rd.Params.ProjectID, err = getproject.MaybeID(rd.Params.ProjectID, rd.Params.Region)
    55  
    56  	if err != nil {
    57  		return f, err
    58  	}
    59  
    60  	err = rd.loadServicesList()
    61  	f.Services = rd.services
    62  
    63  	if err != nil {
    64  		return f, err
    65  	}
    66  
    67  	if len(rd.services) == 0 {
    68  		return f, errors.New("no service available for deployment was found")
    69  	}
    70  
    71  	if err = rd.checkImage(); err != nil {
    72  		return f, err
    73  	}
    74  
    75  	rd.verboseRemappedServices()
    76  
    77  	var deploy = &deployment.Deploy{
    78  		ConfigContext: wectx,
    79  
    80  		Params:   rd.Params,
    81  		Path:     rd.path,
    82  		Services: rd.services,
    83  	}
    84  
    85  	err = deploy.Do(ctx, rd.getTransport())
    86  	f.GroupUID = deploy.GetGroupUID()
    87  	return f, err
    88  }
    89  
    90  func (rd *RemoteDeployment) getTransport() deployment.Transport {
    91  	if rd.Experimental {
    92  		return &gogit.Transport{}
    93  	}
    94  
    95  	return &git.Transport{}
    96  }
    97  
    98  func (rd *RemoteDeployment) checkImage() error {
    99  	if rd.Params.Image == "" || len(rd.services) <= 1 {
   100  		return nil
   101  	}
   102  
   103  	var first = rd.services[0]
   104  	var pkg = first.Package()
   105  	var original = pkg.Image
   106  	var diff = []string{}
   107  	var f = fmt.Sprintf("%s is %s (%s)", first.ServiceID, pkg.Image, first.Location)
   108  
   109  	for _, s := range rd.services {
   110  		pkg := s.Package()
   111  
   112  		if original == "" {
   113  			original = pkg.Image
   114  		}
   115  
   116  		if original != pkg.Image {
   117  			diff = append(diff, fmt.Sprintf("%s is %s (%s)", s.ServiceID, pkg.Image, s.Location))
   118  		}
   119  	}
   120  
   121  	// found services with duplicated ID "email" on %v and %v
   122  	if len(diff) != 0 {
   123  		diff = append([]string{f}, diff...)
   124  		return fmt.Errorf("refusing to overwrite image for services with different images:\n%v",
   125  			strings.Join(diff, "\n"))
   126  	}
   127  
   128  	return nil
   129  }
   130  
   131  func (rd *RemoteDeployment) verboseRemappedServices() {
   132  	if !verbose.Enabled {
   133  		return
   134  	}
   135  
   136  	for _, s := range rd.remap {
   137  		verbose.Debug("Service " + s + " had service ID mapped or inferred")
   138  	}
   139  }
   140  
   141  func (rd *RemoteDeployment) loadServicesList() (err error) {
   142  	if err = rd.loadServicesListFromPath(); err != nil {
   143  		return err
   144  	}
   145  
   146  	if err = rd.checkServiceIDs(); err != nil {
   147  		return err
   148  	}
   149  
   150  	return rd.remapServicesWithEmptyIDs()
   151  }
   152  
   153  func (rd *RemoteDeployment) getServiceIDFromBaseDirOrRandom(s services.ServiceInfo) (newServiceID string) {
   154  	r := regexp.MustCompile("^[0-9a-z]*$")
   155  	serviceID := strings.ToLower(filepath.Base(s.Location))
   156  
   157  	if !r.MatchString(serviceID) {
   158  		serviceID = fmt.Sprintf("%s%d", namesgenerator.GetRandomAdjective(), rand.Intn(99))
   159  	}
   160  
   161  	verbose.Debug(fmt.Sprintf("service in %v assigned with id %v", s.Location, serviceID))
   162  	return serviceID
   163  }
   164  
   165  func (rd *RemoteDeployment) remapServicesWithEmptyIDs() error {
   166  	for k, s := range rd.services {
   167  		if s.ServiceID != "" {
   168  			continue
   169  		}
   170  
   171  		rd.services[k].ServiceID = rd.getServiceIDFromBaseDirOrRandom(s)
   172  		rd.remap = append(rd.remap, s.Location)
   173  	}
   174  
   175  	return nil
   176  }
   177  
   178  func (rd *RemoteDeployment) checkServiceIDs() error {
   179  	if len(rd.services) == 1 {
   180  		return rd.checkServiceParameter()
   181  	}
   182  
   183  	if rd.Params.ServiceID != "" {
   184  		return errors.New("service id parameter is not allowed when deploying multiple services")
   185  	}
   186  
   187  	return rd.checkEmptyIDOnMultipleDeployment()
   188  }
   189  
   190  func (rd *RemoteDeployment) checkEmptyIDOnMultipleDeployment() error {
   191  	var empty []services.ServiceInfo
   192  	for _, s := range rd.services {
   193  		if s.ServiceID == "" {
   194  			empty = append(empty, s)
   195  		}
   196  	}
   197  
   198  	if len(empty) == 0 {
   199  		return nil
   200  	}
   201  
   202  	fmt.Println(fancy.Info("Multiple services found without LCP.json IDs:"))
   203  
   204  	for _, e := range empty {
   205  		fmt.Printf("%v %v\n", e.Location, fancy.Tip("using "+filepath.Base(e.Location)))
   206  	}
   207  
   208  	fmt.Println("")
   209  
   210  	q := fmt.Sprintf("Do you want to %s?",
   211  		color.Format(color.FgMagenta, color.Bold, "continue"))
   212  
   213  	switch ok, askErr := fancy.Boolean(q); {
   214  	case askErr != nil:
   215  		return askErr
   216  	case ok:
   217  		fmt.Println("")
   218  		return nil
   219  	}
   220  
   221  	return canceled.CancelCommand("deployment canceled")
   222  }
   223  
   224  func (rd *RemoteDeployment) checkServiceParameter() error {
   225  	if rd.Params.ServiceID == "" && rd.services[0].ServiceID != "" {
   226  		return nil
   227  	}
   228  
   229  	if rd.Params.ServiceID != "" {
   230  		rd.services[0].ServiceID = rd.Params.ServiceID
   231  		rd.remap = append(rd.remap, rd.services[0].Location)
   232  		return nil
   233  	}
   234  
   235  	if !isterm.Check() {
   236  		return errors.New("service ID is missing (and a terminal was not found for typing one)")
   237  	}
   238  
   239  	var optionServiceID = rd.getServiceIDFromBaseDirOrRandom(rd.services[0])
   240  	fmt.Println(fancy.Question("Your service doesn't have an ID. Type one") + " " +
   241  		fancy.Tip("or use: "+optionServiceID))
   242  
   243  	serviceID, err := fancy.Prompt()
   244  	switch {
   245  	case err != nil:
   246  		return err
   247  	case serviceID == "":
   248  		serviceID = optionServiceID
   249  	}
   250  
   251  	rd.Params.ServiceID = serviceID
   252  	rd.services[0].ServiceID = serviceID
   253  	rd.remap = append(rd.remap, rd.services[0].Location)
   254  	return nil
   255  }
   256  
   257  func (rd *RemoteDeployment) loadServicesListFromPath() (err error) {
   258  	var overview = inspector.ContextOverview{}
   259  	if err = overview.Load(rd.path); err != nil {
   260  		return err
   261  	}
   262  
   263  	rd.services = overview.Services
   264  
   265  	if len(rd.services) == 0 {
   266  		rd.services = append(rd.services, services.ServiceInfo{
   267  			Location:  rd.path,
   268  			ServiceID: rd.Params.ServiceID,
   269  		})
   270  	}
   271  
   272  	for k := range rd.services {
   273  		rd.services[k].ProjectID = rd.Params.ProjectID
   274  	}
   275  
   276  	return nil
   277  }