github.com/hazelops/ize@v1.1.12-0.20230915191306-97d7c0e48f11/internal/commands/tunnel_down.go (about)

     1  package commands
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io/fs"
     7  	"os/exec"
     8  	"syscall"
     9  
    10  	"github.com/hazelops/ize/internal/config"
    11  	"github.com/pterm/pterm"
    12  	"github.com/sirupsen/logrus"
    13  	"github.com/spf13/cobra"
    14  )
    15  
    16  var explainTunnelDownTmpl = `
    17  # Change to the dir and send an exit request
    18  (cd {{.EnvDir}} && $(aws ssm get-parameter --name "/{{.Env}}/terraform-output" --with-decryption | jq -r '.Parameter.Value' | base64 -d | jq -r '.cmd.value.tunnel.down'))
    19  `
    20  
    21  type TunnelDownOptions struct {
    22  	Config  *config.Project
    23  	Explain bool
    24  }
    25  
    26  func NewTunnelDownOptions(project *config.Project) *TunnelDownOptions {
    27  	return &TunnelDownOptions{
    28  		Config: project,
    29  	}
    30  }
    31  
    32  func NewCmdTunnelDown(project *config.Project) *cobra.Command {
    33  	o := NewTunnelDownOptions(project)
    34  
    35  	cmd := &cobra.Command{
    36  		Use:   "down",
    37  		Short: "Close tunnel",
    38  		Long:  "Close tunnel",
    39  		RunE: func(cmd *cobra.Command, args []string) error {
    40  			cmd.SilenceUsage = true
    41  
    42  			if o.Explain {
    43  				err := o.Config.Generate(explainTunnelDownTmpl, nil)
    44  				if err != nil {
    45  					return err
    46  				}
    47  
    48  				return nil
    49  			}
    50  
    51  			err := o.Complete()
    52  			if err != nil {
    53  				return err
    54  			}
    55  
    56  			err = o.Validate()
    57  			if err != nil {
    58  				return err
    59  			}
    60  
    61  			err = o.Run()
    62  			if err != nil {
    63  				return err
    64  			}
    65  
    66  			return nil
    67  		},
    68  	}
    69  
    70  	cmd.Flags().BoolVar(&o.Explain, "explain", false, "bash alternative shown")
    71  
    72  	return cmd
    73  }
    74  
    75  func (o *TunnelDownOptions) Complete() error {
    76  	return nil
    77  }
    78  
    79  func (o *TunnelDownOptions) Validate() error {
    80  	if len(o.Config.Env) == 0 {
    81  		return fmt.Errorf("env must be specified")
    82  	}
    83  
    84  	return nil
    85  }
    86  
    87  func (o *TunnelDownOptions) Run() error {
    88  	c := exec.Command(
    89  		"ssh", "-S", "bastion.sock", "-O", "exit", "",
    90  	)
    91  
    92  	if o.Config.LogLevel == "debug" {
    93  		c.Args = append(c.Args, "-vvv")
    94  	}
    95  
    96  	out := &bytes.Buffer{}
    97  	c.Stdout = out
    98  	c.Stderr = out
    99  	c.Dir = o.Config.EnvDir
   100  
   101  	err := c.Run()
   102  	if err != nil {
   103  		patherr, ok := err.(*fs.PathError)
   104  		if ok {
   105  			return fmt.Errorf("unable to access folder '%s': %w", c.Dir, patherr.Err)
   106  		}
   107  		exiterr := err.(*exec.ExitError)
   108  		status := exiterr.Sys().(syscall.WaitStatus)
   109  		if status.ExitStatus() != 255 {
   110  			logrus.Debug(out.String())
   111  			return fmt.Errorf("unable to bring the tunnel down: %w", err)
   112  		}
   113  		return fmt.Errorf("unable to bring the tunnel down: tunnel is not active")
   114  	}
   115  
   116  	pterm.Success.Println("Tunnel is down!")
   117  
   118  	return nil
   119  }