mirror of
https://github.com/foomo/gograpple.git
synced 2025-10-16 12:35:37 +00:00
chore: reorganize, fix goreleaser and update readme
This commit is contained in:
parent
e4b0f420fd
commit
a3d29b4915
@ -2,11 +2,11 @@
|
|||||||
# Build customization
|
# Build customization
|
||||||
builds:
|
builds:
|
||||||
- binary: gograpple
|
- binary: gograpple
|
||||||
main: ./cmd/main.go
|
main: ./main.go
|
||||||
env:
|
env:
|
||||||
- CGO_ENABLED=0
|
- CGO_ENABLED=0
|
||||||
ldflags:
|
ldflags:
|
||||||
- -s -w -X github.com/foomo/gograpple/cmd/actions.version={{.Version}}
|
- -s -w -X github.com/foomo/gograpple/cmd.version={{.Version}}
|
||||||
goos:
|
goos:
|
||||||
- darwin
|
- darwin
|
||||||
- linux
|
- linux
|
||||||
|
|||||||
4
Makefile
4
Makefile
@ -1,8 +1,8 @@
|
|||||||
build:
|
build:
|
||||||
go build -o bin/gograpple cmd/gograpple/main.go
|
go build -o bin/gograpple main.go
|
||||||
|
|
||||||
install:
|
install:
|
||||||
go build -o /usr/local/bin/gograpple cmd/gograpple/main.go
|
go build -o /usr/local/bin/gograpple main.go
|
||||||
|
|
||||||
test:
|
test:
|
||||||
go test ./...
|
go test ./...
|
||||||
15
README.md
15
README.md
@ -3,9 +3,16 @@
|
|||||||
gograpple that go program and delve into the high seas ...
|
gograpple that go program and delve into the high seas ...
|
||||||
or in other words: delve debugger injection for your golang code running in k8 pods
|
or in other words: delve debugger injection for your golang code running in k8 pods
|
||||||
|
|
||||||
|
## requirements
|
||||||
|
- helm
|
||||||
|
- kubectl
|
||||||
|
- docker
|
||||||
|
|
||||||
## quick start
|
## quick start
|
||||||
```
|
```
|
||||||
go install github.com/foomo/gograpple/cmd/gograpple@latest
|
brew install foomo/gograpple/gograpple
|
||||||
|
OR
|
||||||
|
go install github.com/foomo/gograpple@latest
|
||||||
```
|
```
|
||||||
start patch debugging in interactive mode
|
start patch debugging in interactive mode
|
||||||
```
|
```
|
||||||
@ -15,6 +22,12 @@ when you configure your patch correctly a file will be saved in your cwd and the
|
|||||||
|
|
||||||
## common issues
|
## common issues
|
||||||
|
|
||||||
|
### stuck with patched deployment
|
||||||
|
in case your deployment is styck in patched state, use
|
||||||
|
```
|
||||||
|
gograpple rollback [namespace] [deployment]
|
||||||
|
```
|
||||||
|
|
||||||
### vscode
|
### vscode
|
||||||
> The debug session doesnt start until the entrypoint is triggered more than once.
|
> The debug session doesnt start until the entrypoint is triggered more than once.
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
package actions
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/foomo/gograpple"
|
"github.com/foomo/gograpple/internal/grapple"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HostPort struct {
|
type HostPort struct {
|
||||||
@ -14,7 +14,7 @@ type HostPort struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewHostPort(host string, port int) *HostPort {
|
func NewHostPort(host string, port int) *HostPort {
|
||||||
addr, err := gograpple.CheckTCPConnection(host, port)
|
addr, err := grapple.CheckTCPConnection(host, port)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
host = addr.IP.String()
|
host = addr.IP.String()
|
||||||
port = addr.Port
|
port = addr.Port
|
||||||
@ -45,7 +45,7 @@ func (lf *HostPort) Set(value string) error {
|
|||||||
default:
|
default:
|
||||||
return fmt.Errorf("invalid address %q provided", value)
|
return fmt.Errorf("invalid address %q provided", value)
|
||||||
}
|
}
|
||||||
addr, err := gograpple.CheckTCPConnection(lf.Host, lf.Port)
|
addr, err := grapple.CheckTCPConnection(lf.Host, lf.Port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1,124 +0,0 @@
|
|||||||
package actions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/foomo/gograpple"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
|
|
||||||
rootCmd.PersistentFlags().StringVarP(&flagDir, "dir", "d", ".", "Specifies working directory")
|
|
||||||
rootCmd.PersistentFlags().StringVarP(&flagNamespace, "namespace", "n", "default", "namespace name")
|
|
||||||
rootCmd.PersistentFlags().BoolVarP(&flagVerbose, "verbose", "v", false, "Specifies should command output be displayed")
|
|
||||||
rootCmd.PersistentFlags().StringVarP(&flagPod, "pod", "p", "", "pod name (default most recent one)")
|
|
||||||
rootCmd.PersistentFlags().StringVarP(&flagContainer, "container", "c", "", "container name (default deployment name)")
|
|
||||||
rootCmd.PersistentFlags().BoolVarP(&flagDebug, "debug", "", false, "debug mode")
|
|
||||||
patchCmd.Flags().StringVar(&flagImage, "image", "alpine:latest", "image to be used for patching (default alpine:latest)")
|
|
||||||
patchCmd.Flags().StringArrayVarP(&flagMounts, "mount", "m", []string{}, "host path to be mounted (default none)")
|
|
||||||
patchCmd.Flags().BoolVar(&flagRollback, "rollback", false, "rollback deployment to a previous state")
|
|
||||||
delveCmd.Flags().StringVar(&flagSourcePath, "source", "", ".go file source path (default cwd)")
|
|
||||||
delveCmd.Flags().Var(flagArgs, "args", "go file args")
|
|
||||||
delveCmd.Flags().Var(flagListen, "listen", "delve host:port to listen on")
|
|
||||||
delveCmd.Flags().BoolVar(&flagVscode, "vscode", false, "launch a debug configuration in vscode")
|
|
||||||
delveCmd.Flags().BoolVar(&flagContinue, "continue", false, "start delve server execution without waiting for client connection")
|
|
||||||
delveCmd.Flags().BoolVar(&flagJSONLog, "json-log", false, "log as json")
|
|
||||||
interactiveCmd.Flags().BoolVar(&flagAttach, "attach", false, "debug with attach (default will patch)")
|
|
||||||
interactiveCmd.Flags().StringVar(&flagSaveDir, "save", ".", "directory to save interactive configuration")
|
|
||||||
rootCmd.AddCommand(versionCmd, patchCmd, shellCmd, delveCmd, interactiveCmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
flagImage string
|
|
||||||
flagDir string
|
|
||||||
flagVerbose bool
|
|
||||||
flagNamespace string
|
|
||||||
flagPod string
|
|
||||||
flagContainer string
|
|
||||||
flagRepo string
|
|
||||||
flagMounts []string
|
|
||||||
flagSourcePath string
|
|
||||||
flagArgs = NewStringList(" ")
|
|
||||||
flagRollback bool
|
|
||||||
flagListen = NewHostPort("127.0.0.1", 0)
|
|
||||||
flagVscode bool
|
|
||||||
flagContinue bool
|
|
||||||
flagJSONLog bool
|
|
||||||
flagDebug bool
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
l *logrus.Entry
|
|
||||||
grapple *gograpple.Grapple
|
|
||||||
|
|
||||||
rootCmd = &cobra.Command{
|
|
||||||
Use: "gograpple",
|
|
||||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
if cmd.Name() == commandNameVersion || cmd.Name() == commandNameInteractive {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
l = newLogger(flagVerbose, flagJSONLog)
|
|
||||||
var err error
|
|
||||||
err = gograpple.ValidatePath(".", &flagDir)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
grapple, err = gograpple.NewGrapple(l, flagNamespace, args[0], flagDebug)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return gograpple.ValidatePath(flagDir, &flagSourcePath)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
patchCmd = &cobra.Command{
|
|
||||||
Use: "patch [DEPLOYMENT] -c {CONTAINER} -n {NAMESPACE} -i {IMAGE} -t {TAG} -m {MOUNT}",
|
|
||||||
Short: "applies a development patch for a deployment",
|
|
||||||
Args: cobra.MinimumNArgs(1),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
if flagRollback {
|
|
||||||
return grapple.Rollback()
|
|
||||||
}
|
|
||||||
mounts, err := gograpple.ValidateMounts(flagDir, flagMounts)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return grapple.Patch(flagImage, flagContainer, mounts)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
shellCmd = &cobra.Command{
|
|
||||||
Use: "shell [DEPLOYMENT] -n {NAMESPACE} -c {CONTAINER}",
|
|
||||||
Short: "shell into the dev patched deployment",
|
|
||||||
Args: cobra.MinimumNArgs(1),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
return grapple.Shell(flagPod)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
delveCmd = &cobra.Command{
|
|
||||||
Use: "delve [DEPLOYMENT] --source {SRC} -n {NAMESPACE} -c {CONTAINER}",
|
|
||||||
Short: "start a headless delve debug server for .go input on a patched deployment",
|
|
||||||
Args: cobra.MinimumNArgs(1),
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
return grapple.Delve(flagPod, flagContainer, flagSourcePath, flagArgs.items, flagListen.Host, flagListen.Port, flagVscode, flagContinue)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func Execute() {
|
|
||||||
if err := rootCmd.Execute(); err != nil {
|
|
||||||
l = logrus.NewEntry(logrus.New())
|
|
||||||
l.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newLogger(verbose, jsonLog bool) *logrus.Entry {
|
|
||||||
logger := logrus.New()
|
|
||||||
if jsonLog {
|
|
||||||
logger.SetFormatter(&logrus.JSONFormatter{
|
|
||||||
DisableTimestamp: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if verbose {
|
|
||||||
logger.SetLevel(logrus.TraceLevel)
|
|
||||||
}
|
|
||||||
return logrus.NewEntry(logger)
|
|
||||||
}
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
package actions
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
)
|
|
||||||
|
|
||||||
var version = "latest"
|
|
||||||
|
|
||||||
const commandNameVersion = "version"
|
|
||||||
|
|
||||||
var (
|
|
||||||
versionCmd = &cobra.Command{
|
|
||||||
Use: commandNameVersion,
|
|
||||||
Short: "prints cli version",
|
|
||||||
Long: "prints the current installed cli version",
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
fmt.Println(version)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import "github.com/foomo/gograpple/cmd/gograpple/actions"
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
actions.Execute()
|
|
||||||
}
|
|
||||||
@ -1,15 +1,19 @@
|
|||||||
package actions
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/foomo/gograpple"
|
"github.com/foomo/gograpple/internal/config"
|
||||||
"github.com/foomo/gograpple/config"
|
"github.com/foomo/gograpple/internal/grapple"
|
||||||
"github.com/foomo/gograpple/kubectl"
|
"github.com/foomo/gograpple/internal/kubectl"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
const commandNameInteractive = "interactive"
|
func init() {
|
||||||
|
interactiveCmd.Flags().BoolVar(&flagAttach, "attach", false, "debug with attach (default will patch)")
|
||||||
|
interactiveCmd.Flags().StringVar(&flagSaveDir, "save", ".", "directory to save interactive configuration")
|
||||||
|
rootCmd.AddCommand(interactiveCmd)
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
flagAttach bool
|
flagAttach bool
|
||||||
@ -37,7 +41,7 @@ func attachDebug(baseDir string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
g, err := gograpple.NewGrapple(newLogger(flagVerbose, flagJSONLog), c.Namespace, c.Deployment, flagDebug)
|
g, err := grapple.NewGrapple(newLogEntry(flagDebug), c.Namespace, c.Deployment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -48,7 +52,7 @@ func attachDebug(baseDir string) error {
|
|||||||
if err := kubectl.SetContext(c.Cluster); err != nil {
|
if err := kubectl.SetContext(c.Cluster); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return g.Attach(c.Namespace, c.Deployment, c.Container, c.AttachTo, c.Arch, host, port)
|
return g.Attach(c.Namespace, c.Deployment, c.Container, c.AttachTo, c.Arch, host, port, flagDebug)
|
||||||
}
|
}
|
||||||
|
|
||||||
func patchDebug(baseDir string) error {
|
func patchDebug(baseDir string) error {
|
||||||
@ -64,7 +68,7 @@ func patchDebug(baseDir string) error {
|
|||||||
if &c == nil {
|
if &c == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
g, err := gograpple.NewGrapple(newLogger(flagVerbose, flagJSONLog), c.Namespace, c.Deployment, flagDebug)
|
g, err := grapple.NewGrapple(newLogEntry(flagDebug), c.Namespace, c.Deployment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
25
cmd/rollback.go
Normal file
25
cmd/rollback.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/foomo/gograpple/internal/grapple"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(rollbackCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
rollbackCmd = &cobra.Command{
|
||||||
|
Use: "rollback [namespace] [deployment]",
|
||||||
|
Short: "rollback the patched deployment",
|
||||||
|
Args: cobra.MinimumNArgs(2),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
g, err := grapple.NewGrapple(newLogEntry(flagDebug), args[0], args[1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return g.Rollback()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
50
cmd/root.go
Normal file
50
cmd/root.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.PersistentFlags().BoolVarP(&flagDebug, "debug", "", false, "debug mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// flagImage string
|
||||||
|
// flagDir string
|
||||||
|
flagDebug bool
|
||||||
|
// flagNamespace string
|
||||||
|
// flagPod string
|
||||||
|
// flagContainer string
|
||||||
|
// flagRepo string
|
||||||
|
// flagMounts []string
|
||||||
|
// flagSourcePath string
|
||||||
|
// flagArgs = NewStringList(" ")
|
||||||
|
// flagRollback bool
|
||||||
|
// flagListen = NewHostPort("127.0.0.1", 0)
|
||||||
|
// flagVscode bool
|
||||||
|
// flagContinue bool
|
||||||
|
// flagJSONLog bool
|
||||||
|
// flagDebug bool
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
rootCmd = &cobra.Command{
|
||||||
|
Use: "gograpple",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func Execute() {
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
le := newLogEntry(flagDebug)
|
||||||
|
le.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLogEntry(debug bool) *logrus.Entry {
|
||||||
|
logger := logrus.New()
|
||||||
|
if debug {
|
||||||
|
logger.SetLevel(logrus.TraceLevel)
|
||||||
|
}
|
||||||
|
return logrus.NewEntry(logger)
|
||||||
|
}
|
||||||
32
cmd/version.go
Normal file
32
cmd/version.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(versionCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// set on build
|
||||||
|
var version = ""
|
||||||
|
|
||||||
|
var (
|
||||||
|
versionCmd = &cobra.Command{
|
||||||
|
Use: "version",
|
||||||
|
Short: "prints cli version",
|
||||||
|
Long: "prints the current installed cli version",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
fmt.Println(getVersion())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func getVersion() string {
|
||||||
|
if version == "" {
|
||||||
|
return "latest"
|
||||||
|
}
|
||||||
|
return version
|
||||||
|
}
|
||||||
@ -8,8 +8,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/c-bata/go-prompt"
|
"github.com/c-bata/go-prompt"
|
||||||
"github.com/foomo/gograpple/kubectl"
|
"github.com/foomo/gograpple/internal/kubectl"
|
||||||
"github.com/foomo/gograpple/suggest"
|
"github.com/foomo/gograpple/internal/suggest"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -8,8 +8,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/c-bata/go-prompt"
|
"github.com/c-bata/go-prompt"
|
||||||
"github.com/foomo/gograpple/kubectl"
|
"github.com/foomo/gograpple/internal/kubectl"
|
||||||
"github.com/foomo/gograpple/suggest"
|
"github.com/foomo/gograpple/internal/suggest"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/foomo/gograpple/exec"
|
"github.com/foomo/gograpple/internal/exec"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package gograpple
|
package grapple
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -7,12 +7,12 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/bitfield/script"
|
"github.com/bitfield/script"
|
||||||
"github.com/foomo/gograpple/kubectl"
|
"github.com/foomo/gograpple/internal/kubectl"
|
||||||
"github.com/foomo/gograpple/log"
|
"github.com/foomo/gograpple/internal/log"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (g Grapple) Attach(namespace, deployment, container, bin, arch, host string, port int) error {
|
func (g Grapple) Attach(namespace, deployment, container, bin, arch, host string, port int, debug bool) error {
|
||||||
pod, err := kubectl.GetMostRecentRunningPodBySelectors(namespace, g.deployment.Spec.Selector.MatchLabels)
|
pod, err := kubectl.GetMostRecentRunningPodBySelectors(namespace, g.deployment.Spec.Selector.MatchLabels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -35,7 +35,7 @@ func (g Grapple) Attach(namespace, deployment, container, bin, arch, host string
|
|||||||
if len(pids) != 1 {
|
if len(pids) != 1 {
|
||||||
return fmt.Errorf("found none or more than one process named %q", bin)
|
return fmt.Errorf("found none or more than one process named %q", bin)
|
||||||
}
|
}
|
||||||
go attachDelveOnPod(namespace, pod, container, dlvDest, pids[0], host, port, g.debug)
|
go attachDelveOnPod(namespace, pod, container, dlvDest, pids[0], host, port, debug)
|
||||||
// launchVSCode(context.Background(), g.l, "./test/app", "", port, 3)
|
// launchVSCode(context.Background(), g.l, "./test/app", "", port, 3)
|
||||||
return kubectl.PortForwardPod(namespace, pod, port)
|
return kubectl.PortForwardPod(namespace, pod, port)
|
||||||
}
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package gograpple
|
package grapple
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -7,8 +7,9 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/foomo/gograpple/delve"
|
"github.com/foomo/gograpple/internal/delve"
|
||||||
"github.com/foomo/gograpple/exec"
|
"github.com/foomo/gograpple/internal/exec"
|
||||||
|
"github.com/foomo/gograpple/util"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ func (g Grapple) Delve(pod, container, sourcePath string, binArgs []string, host
|
|||||||
return fmt.Errorf("couldnt find go.mod path for source %q", sourcePath)
|
return fmt.Errorf("couldnt find go.mod path for source %q", sourcePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
RunWithInterrupt(g.l, func(ctx context.Context) {
|
util.RunWithInterrupt(g.l, func(ctx context.Context) {
|
||||||
g.l.Infof("waiting for deployment to get ready")
|
g.l.Infof("waiting for deployment to get ready")
|
||||||
_, err := g.kubeCmd.WaitForRollout(g.deployment.Name, defaultWaitTimeout).Run(ctx)
|
_, err := g.kubeCmd.WaitForRollout(g.deployment.Name, defaultWaitTimeout).Run(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package gograpple
|
package grapple
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
@ -10,7 +10,7 @@ import (
|
|||||||
const testNamespace = "test"
|
const testNamespace = "test"
|
||||||
|
|
||||||
func testGrapple(t *testing.T, deployment string) *Grapple {
|
func testGrapple(t *testing.T, deployment string) *Grapple {
|
||||||
g, err := NewGrapple(logrus.NewEntry(logrus.StandardLogger()), testNamespace, deployment, false)
|
g, err := NewGrapple(logrus.NewEntry(logrus.StandardLogger()), testNamespace, deployment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -1,9 +1,9 @@
|
|||||||
package gograpple
|
package grapple
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/foomo/gograpple/exec"
|
"github.com/foomo/gograpple/internal/exec"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
v1 "k8s.io/api/apps/v1"
|
v1 "k8s.io/api/apps/v1"
|
||||||
)
|
)
|
||||||
@ -30,11 +30,10 @@ type Grapple struct {
|
|||||||
kubeCmd *exec.KubectlCmd
|
kubeCmd *exec.KubectlCmd
|
||||||
dockerCmd *exec.DockerCmd
|
dockerCmd *exec.DockerCmd
|
||||||
goCmd *exec.GoCmd
|
goCmd *exec.GoCmd
|
||||||
debug bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGrapple(l *logrus.Entry, namespace, deployment string, debug bool) (*Grapple, error) {
|
func NewGrapple(l *logrus.Entry, namespace, deployment string) (*Grapple, error) {
|
||||||
g := &Grapple{l: l, debug: debug}
|
g := &Grapple{l: l}
|
||||||
g.kubeCmd = exec.NewKubectlCommand()
|
g.kubeCmd = exec.NewKubectlCommand()
|
||||||
g.dockerCmd = exec.NewDockerCommand()
|
g.dockerCmd = exec.NewDockerCommand()
|
||||||
g.goCmd = exec.NewGoCommand()
|
g.goCmd = exec.NewGoCommand()
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package gograpple
|
package grapple
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -9,6 +9,7 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/foomo/gograpple/util"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -109,7 +110,7 @@ func (g Grapple) Patch(image, container string, mounts []Mount) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// get repo from deployment image
|
// get repo from deployment image
|
||||||
imageRepo, name, tag, err := ParseImage(deploymentImage)
|
imageRepo, name, tag, err := util.ParseImage(deploymentImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package gograpple
|
package grapple
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package gograpple
|
package grapple
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package gograpple
|
package grapple
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@ -7,13 +7,8 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/foomo/gograpple/exec"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func FindFreePort(host string) (int, error) {
|
func FindFreePort(host string) (int, error) {
|
||||||
@ -37,21 +32,6 @@ func CheckTCPConnection(host string, port int) (*net.TCPAddr, error) {
|
|||||||
return l.Addr().(*net.TCPAddr), nil
|
return l.Addr().(*net.TCPAddr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Open(l *logrus.Entry, ctx context.Context, path string) (string, error) {
|
|
||||||
var cmd *exec.Cmd
|
|
||||||
switch runtime.GOOS {
|
|
||||||
case "linux":
|
|
||||||
cmd = exec.NewCommand("xdg-open").Logger(l).Args(path)
|
|
||||||
case "windows":
|
|
||||||
cmd = exec.NewCommand("rundll32").Logger(l).Args("url.dll,FileProtocolHandler", path)
|
|
||||||
case "darwin":
|
|
||||||
cmd = exec.NewCommand("open").Logger(l).Args(path)
|
|
||||||
default:
|
|
||||||
return "", fmt.Errorf("unsupported platform")
|
|
||||||
}
|
|
||||||
return cmd.Run(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TryCall(tries int, waitBetweenAttempts time.Duration, f func(i int) error) error {
|
func TryCall(tries int, waitBetweenAttempts time.Duration, f func(i int) error) error {
|
||||||
var err error
|
var err error
|
||||||
for i := 1; i < tries+1; i++ {
|
for i := 1; i < tries+1; i++ {
|
||||||
@ -129,24 +109,3 @@ func stringIsInSlice(a string, list []string) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPlatformInfo(platform string) (os, arch string, err error) {
|
|
||||||
pieces := strings.Split(platform, "/")
|
|
||||||
if len(pieces) != 2 {
|
|
||||||
return os, arch, fmt.Errorf("invalid format for platform %q", platform)
|
|
||||||
}
|
|
||||||
return pieces[0], pieces[1], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseImage(s string) (repo, name, tag string, err error) {
|
|
||||||
pieces := strings.Split(s, "/")
|
|
||||||
switch true {
|
|
||||||
case len(pieces) == 1 && pieces[0] == s:
|
|
||||||
imageTag := strings.Split(s, ":")
|
|
||||||
return "", imageTag[0], imageTag[1], nil
|
|
||||||
case len(pieces) > 1:
|
|
||||||
imageTag := strings.Split(pieces[len(pieces)-1], ":")
|
|
||||||
return strings.Join(pieces[:len(pieces)-1], "/"), imageTag[0], imageTag[1], nil
|
|
||||||
}
|
|
||||||
return "", "", "", fmt.Errorf("invalid image value %q provided", s)
|
|
||||||
}
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package gograpple
|
package grapple
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package gograpple
|
package grapple
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -10,7 +10,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/foomo/gograpple/exec"
|
"github.com/foomo/gograpple/internal/exec"
|
||||||
|
"github.com/foomo/gograpple/util"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -79,7 +80,7 @@ func launchVSCode(ctx context.Context, l *logrus.Entry, goModDir, host string, p
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = Open(l, ctx, `vscode://fabiospampinato.vscode-debug-launcher/launch?args=`+url.QueryEscape(la))
|
_, err = util.Open(l, ctx, `vscode://fabiospampinato.vscode-debug-launcher/launch?args=`+url.QueryEscape(la))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -7,8 +7,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/bitfield/script"
|
"github.com/bitfield/script"
|
||||||
"github.com/foomo/gograpple/log"
|
"github.com/foomo/gograpple/internal/log"
|
||||||
"github.com/foomo/gograpple/suggest"
|
"github.com/foomo/gograpple/internal/suggest"
|
||||||
"github.com/life4/genesis/slices"
|
"github.com/life4/genesis/slices"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
apps "k8s.io/api/apps/v1"
|
apps "k8s.io/api/apps/v1"
|
||||||
38
interrupt.go
38
interrupt.go
@ -1,38 +0,0 @@
|
|||||||
package gograpple
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func RunWithInterrupt(l *logrus.Entry, callback func(ctx context.Context)) {
|
|
||||||
signalChan := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(signalChan, os.Interrupt)
|
|
||||||
durReload := 3 * time.Second
|
|
||||||
for {
|
|
||||||
ctx, cancelCtx := context.WithCancel(context.Background())
|
|
||||||
// do stuff
|
|
||||||
go callback(ctx)
|
|
||||||
select {
|
|
||||||
case <-signalChan: // first signal
|
|
||||||
l.Info("-")
|
|
||||||
l.Infof("interrupt received, trigger one more within %v to terminate", durReload)
|
|
||||||
cancelCtx()
|
|
||||||
select {
|
|
||||||
case <-time.After(durReload): // reloads durReload after first signal
|
|
||||||
l.Info("-")
|
|
||||||
l.Info("reloading")
|
|
||||||
case <-signalChan: // second signal, hard exit
|
|
||||||
l.Info("-")
|
|
||||||
l.Info("terminating")
|
|
||||||
signal.Stop(signalChan)
|
|
||||||
// exit loop
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
7
main.go
Normal file
7
main.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/foomo/gograpple/cmd"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cmd.Execute()
|
||||||
|
}
|
||||||
78
util/util.go
Normal file
78
util/util.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/foomo/gograpple/internal/exec"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RunWithInterrupt(l *logrus.Entry, callback func(ctx context.Context)) {
|
||||||
|
signalChan := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(signalChan, os.Interrupt)
|
||||||
|
durReload := 3 * time.Second
|
||||||
|
for {
|
||||||
|
ctx, cancelCtx := context.WithCancel(context.Background())
|
||||||
|
// do stuff
|
||||||
|
go callback(ctx)
|
||||||
|
select {
|
||||||
|
case <-signalChan: // first signal
|
||||||
|
l.Info("-")
|
||||||
|
l.Infof("interrupt received, trigger one more within %v to terminate", durReload)
|
||||||
|
cancelCtx()
|
||||||
|
select {
|
||||||
|
case <-time.After(durReload): // reloads durReload after first signal
|
||||||
|
l.Info("-")
|
||||||
|
l.Info("reloading")
|
||||||
|
case <-signalChan: // second signal, hard exit
|
||||||
|
l.Info("-")
|
||||||
|
l.Info("terminating")
|
||||||
|
signal.Stop(signalChan)
|
||||||
|
// exit loop
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Open(l *logrus.Entry, ctx context.Context, path string) (string, error) {
|
||||||
|
var cmd *exec.Cmd
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "linux":
|
||||||
|
cmd = exec.NewCommand("xdg-open").Logger(l).Args(path)
|
||||||
|
case "windows":
|
||||||
|
cmd = exec.NewCommand("rundll32").Logger(l).Args("url.dll,FileProtocolHandler", path)
|
||||||
|
case "darwin":
|
||||||
|
cmd = exec.NewCommand("open").Logger(l).Args(path)
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unsupported platform")
|
||||||
|
}
|
||||||
|
return cmd.Run(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPlatformInfo(platform string) (os, arch string, err error) {
|
||||||
|
pieces := strings.Split(platform, "/")
|
||||||
|
if len(pieces) != 2 {
|
||||||
|
return os, arch, fmt.Errorf("invalid format for platform %q", platform)
|
||||||
|
}
|
||||||
|
return pieces[0], pieces[1], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseImage(s string) (repo, name, tag string, err error) {
|
||||||
|
pieces := strings.Split(s, "/")
|
||||||
|
switch true {
|
||||||
|
case len(pieces) == 1 && pieces[0] == s:
|
||||||
|
imageTag := strings.Split(s, ":")
|
||||||
|
return "", imageTag[0], imageTag[1], nil
|
||||||
|
case len(pieces) > 1:
|
||||||
|
imageTag := strings.Split(pieces[len(pieces)-1], ":")
|
||||||
|
return strings.Join(pieces[:len(pieces)-1], "/"), imageTag[0], imageTag[1], nil
|
||||||
|
}
|
||||||
|
return "", "", "", fmt.Errorf("invalid image value %q provided", s)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user