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

     1  package deploy
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"time"
    11  
    12  	"github.com/hashicorp/errwrap"
    13  	"github.com/henvic/ctxsignal"
    14  	"github.com/henvic/wedeploycli/cmdflagsfromhost"
    15  	"github.com/henvic/wedeploycli/color"
    16  	"github.com/henvic/wedeploycli/command/deploy/internal/getproject"
    17  	deployremote "github.com/henvic/wedeploycli/command/deploy/remote"
    18  	"github.com/henvic/wedeploycli/command/internal/we"
    19  	"github.com/henvic/wedeploycli/deployment"
    20  	"github.com/henvic/wedeploycli/jsonerror"
    21  	"github.com/henvic/wedeploycli/logs"
    22  	"github.com/henvic/wedeploycli/services"
    23  	"github.com/spf13/cobra"
    24  )
    25  
    26  var setupHost = cmdflagsfromhost.SetupHost{
    27  	Pattern: cmdflagsfromhost.RegionPattern | cmdflagsfromhost.FullHostPattern,
    28  
    29  	Requires: cmdflagsfromhost.Requires{
    30  		Auth:    true,
    31  		Project: true,
    32  	},
    33  
    34  	UseProjectFromWorkingDirectory: true,
    35  
    36  	AllowMissingProject:        true,
    37  	PromptMissingProject:       true,
    38  	HideServicesPrompt:         true,
    39  	AllowCreateProjectOnPrompt: true,
    40  }
    41  
    42  var (
    43  	params       deployment.Params
    44  	metadata     string
    45  	follow       bool
    46  	experimental bool
    47  )
    48  
    49  // DeployCmd runs services
    50  var DeployCmd = &cobra.Command{
    51  	Use:   "deploy",
    52  	Short: "Deploy your services",
    53  	Example: `  lcp deploy
    54    lcp deploy https://gitlab.com/user/repo
    55    lcp deploy user/repo#branch`,
    56  	Args:    cobra.MaximumNArgs(1),
    57  	PreRunE: preRun,
    58  	RunE:    run,
    59  }
    60  
    61  func checkMetadata() error {
    62  	if metadata == "" {
    63  		return nil
    64  	}
    65  
    66  	if err := json.Unmarshal([]byte(metadata), &deployment.Metadata{}); err != nil {
    67  		return errwrap.Wrapf(
    68  			"error parsing metadata: {{err}}",
    69  			jsonerror.FriendlyUnmarshal(err))
    70  	}
    71  
    72  	return nil
    73  }
    74  
    75  func preRun(cmd *cobra.Command, args []string) error {
    76  	params.Quiet = params.Quiet || params.SkipProgress // be quieter on skip progress as well
    77  	params.Metadata = json.RawMessage(metadata)
    78  
    79  	if err := checkMetadata(); err != nil {
    80  		return err
    81  	}
    82  
    83  	if err := maybePreRunDeployFromGitRepo(cmd, args); err != nil {
    84  		return err
    85  	}
    86  
    87  	return setupHost.Process(context.Background(), we.Context())
    88  }
    89  
    90  func run(cmd *cobra.Command, args []string) (err error) {
    91  	params.Remote = setupHost.Remote()
    92  
    93  	var sil services.ServiceInfoList
    94  	switch {
    95  	case len(args) != 0:
    96  		sil, err = fromGitRepo(args[0])
    97  	default:
    98  		sil, err = local()
    99  	}
   100  
   101  	if err != nil || !follow {
   102  		return err
   103  	}
   104  
   105  	return followLogs(sil)
   106  }
   107  
   108  func handleCopyPackage() (err error) {
   109  	if params.CopyPackage == "" {
   110  		return nil
   111  	}
   112  
   113  	if _, err = os.Stat(params.CopyPackage); err != nil {
   114  		return errwrap.Wrapf("invalid --copy-pkg value: {{err}}", err)
   115  	}
   116  
   117  	params.CopyPackage, err = filepath.Abs(params.CopyPackage)
   118  	return err
   119  }
   120  
   121  func local() (sil services.ServiceInfoList, err error) {
   122  	if err = handleCopyPackage(); err != nil {
   123  		return sil, err
   124  	}
   125  
   126  	params.ProjectID = setupHost.Project()
   127  	params.Region = setupHost.Region()
   128  	params.ServiceID = setupHost.Service()
   129  
   130  	var rd = &deployremote.RemoteDeployment{
   131  		Params:       params,
   132  		Experimental: experimental,
   133  	}
   134  
   135  	ctx, cancel := ctxsignal.WithTermination(context.Background())
   136  	defer cancel()
   137  
   138  	var f deployremote.Feedback
   139  	f, err = rd.Run(ctx)
   140  	return f.Services, err
   141  }
   142  
   143  func maybePreRunDeployFromGitRepo(cmd *cobra.Command, args []string) error {
   144  	if len(args) != 1 {
   145  		return nil
   146  	}
   147  
   148  	if s, _ := cmd.Flags().GetString("service"); s != "" {
   149  		return errors.New("deploying with custom service ids isn't supported using git repositories")
   150  	}
   151  
   152  	if params.CopyPackage != "" {
   153  		return errors.New("can't create a local package when deploying with a git remote")
   154  	}
   155  
   156  	return nil
   157  }
   158  
   159  func fromGitRepo(repo string) (services.ServiceInfoList, error) {
   160  	if params.Image != "" {
   161  		return nil, errors.New("overwriting image when deploying from a git repository is not supported")
   162  	}
   163  
   164  	if metadata != "" {
   165  		return nil, errors.New("using metadata when deploying from a git repository is not supported")
   166  	}
   167  
   168  	var err error
   169  	params.Region = setupHost.Region()
   170  	params.ProjectID, err = getproject.MaybeID(setupHost.Project(), params.Region)
   171  
   172  	if err != nil {
   173  		return nil, err
   174  	}
   175  
   176  	return deployment.DeployFromGitRepository(context.Background(), we.Context(), params, repo)
   177  }
   178  
   179  func followLogs(sil services.ServiceInfoList) error {
   180  	now := time.Now()
   181  	since := 10 * time.Second
   182  	// BUG(henvic): this preliminary version only loads logs from 10s ago onwards.
   183  
   184  	if len(sil) == 0 {
   185  		panic("no services found to list logs")
   186  	}
   187  
   188  	var projectID = sil[0].ProjectID
   189  
   190  	f := &logs.Filter{
   191  		Project:  projectID,
   192  		Services: sil.GetIDs(),
   193  
   194  		Since: fmt.Sprintf("%v000000000", now.Add(-since).Unix()),
   195  	}
   196  
   197  	watcher := &logs.Watcher{
   198  		Filter: f,
   199  	}
   200  
   201  	ctx, cancel := ctxsignal.WithTermination(context.Background())
   202  	defer cancel()
   203  
   204  	fmt.Println()
   205  	fmt.Println(color.Format(color.FgBlue, color.Bold,
   206  		fmt.Sprintf("Showing logs from %v ago onwards.", since)))
   207  	fmt.Printf("You can exit anytime.\n\n")
   208  	time.Sleep(220 * time.Millisecond)
   209  
   210  	watcher.Watch(ctx, we.Context())
   211  
   212  	if _, err := ctxsignal.Closed(ctx); err == nil {
   213  		fmt.Println()
   214  	}
   215  
   216  	return nil
   217  }
   218  
   219  func init() {
   220  	DeployCmd.Flags().StringVar(&params.Image, "image", "", "Use different image for service")
   221  	DeployCmd.Flags().StringVar(&metadata, "metadata", "", "Metadata in JSON")
   222  	DeployCmd.Flags().BoolVar(&params.OnlyBuild, "only-build", false,
   223  		"Skip deployment (only build)")
   224  	DeployCmd.Flags().BoolVar(&params.SkipProgress, "skip-progress", false,
   225  		"Skip watching deployment progress, quiet")
   226  	DeployCmd.Flags().BoolVarP(&params.Quiet, "quiet", "q", false,
   227  		"Suppress progress animations")
   228  	DeployCmd.Flags().BoolVar(&follow, "follow", false,
   229  		"Follow logs after deployment")
   230  	DeployCmd.Flags().BoolVar(
   231  		&experimental,
   232  		"experimental", false, "Enable experimental deployment")
   233  	DeployCmd.Flags().StringVar(&params.CopyPackage, "copy-pkg", "",
   234  		"Path to copy the deployment package to (for debugging)")
   235  	_ = DeployCmd.Flags().MarkHidden("metadata")
   236  	_ = DeployCmd.Flags().MarkHidden("follow")
   237  	_ = DeployCmd.Flags().MarkHidden("experimental")
   238  	_ = DeployCmd.Flags().MarkHidden("copy-pkg")
   239  
   240  	setupHost.Init(DeployCmd)
   241  }