diff --git a/cmd/local/local.go b/cmd/local/local.go index 4b96033d6..1facbb9c4 100644 --- a/cmd/local/local.go +++ b/cmd/local/local.go @@ -1,6 +1,7 @@ package local import ( + "context" "fmt" "sync" "time" @@ -40,6 +41,10 @@ var ( adminEmail string templateTag string logLevel string + + // ngrok context that is used to control ngrok context cancellation, and is called at the end of the installation, + // after the user closes Kubefirst installer. + cancelContext context.CancelFunc ) func NewCommand() *cobra.Command { diff --git a/cmd/local/postrun.go b/cmd/local/postrun.go index 602ea27f0..45b92897d 100644 --- a/cmd/local/postrun.go +++ b/cmd/local/postrun.go @@ -1,10 +1,7 @@ package local import ( - "os" - "os/signal" - "sync" - "syscall" + "time" "github.com/rs/zerolog/log" @@ -36,14 +33,21 @@ func runPostLocal(cmd *cobra.Command, args []string) error { log.Info().Msgf("Kubefirst Console available at: %s", pkg.KubefirstConsoleLocalURLTLS) // managing termination signal from the terminal - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) - var wg sync.WaitGroup - wg.Add(1) - go func() { - <-sigs - wg.Done() - }() + // todo: handle user inputs (q, ctrl+c, etc) + //sigs := make(chan os.Signal, 1) + //signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + //var wg sync.WaitGroup + //wg.Add(1) + //go func() { + // <-sigs + // wg.Done() + //}() + //wg.Wait() + + cancelContext() + log.Info().Msg("ngrok context killed") + // force wait context kill + time.Sleep(1 * time.Second) return nil } diff --git a/cmd/local/prerun.go b/cmd/local/prerun.go index a1981aec9..e800d2e6e 100644 --- a/cmd/local/prerun.go +++ b/cmd/local/prerun.go @@ -102,7 +102,11 @@ func validateLocal(cmd *cobra.Command, args []string) error { return err } - go pkg.RunNgrok(context.TODO()) + // creates a new context, and a cancel function that allows canceling the context. The context is passed as an + // argument to the RunNgrok function, which is then started in a new goroutine. + var ctx context.Context + ctx, cancelContext = context.WithCancel(context.Background()) + go pkg.RunNgrok(ctx) viper.Set("github.atlantis.webhook.secret", pkg.Random(20)) viper.Set("github.user", githubUser) diff --git a/go.mod b/go.mod index d037d15f5..750f3c298 100644 --- a/go.mod +++ b/go.mod @@ -33,6 +33,7 @@ require ( github.com/segmentio/analytics-go v3.1.0+incompatible github.com/spf13/cobra v1.4.0 github.com/spf13/viper v1.11.0 + golang.ngrok.com/ngrok v1.0.0 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a @@ -55,7 +56,8 @@ require ( github.com/gobwas/pool v0.2.1 // indirect github.com/gobwas/ws v1.1.0 // indirect github.com/hashicorp/vault/sdk v0.6.0 // indirect - github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac // indirect + github.com/inconshreveable/log15 v3.0.0-testing.3+incompatible // indirect + github.com/inconshreveable/log15/v3 v3.0.0-testing.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect @@ -127,8 +129,8 @@ require ( github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magiconair/properties v1.8.6 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -155,11 +157,11 @@ require ( github.com/subosito/gotenv v1.2.0 // indirect github.com/xanzy/ssh-agent v0.3.0 // indirect go.uber.org/atomic v1.9.0 // indirect - golang.org/x/net v0.0.0-20220812174116-3211cb980234 // indirect + golang.org/x/net v0.2.0 // indirect golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 - golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/sys v0.2.0 // indirect + golang.org/x/term v0.2.0 // indirect + golang.org/x/text v0.4.0 // indirect golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac // indirect diff --git a/go.sum b/go.sum index 989dd9e63..e530cd686 100644 --- a/go.sum +++ b/go.sum @@ -388,8 +388,10 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1: github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac h1:n1DqxAo4oWPMvH1+v+DLYlMCecgumhhgnxAPdqDIFHI= -github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o= +github.com/inconshreveable/log15 v3.0.0-testing.3+incompatible h1:zaX5fYT98jX5j4UhO/WbfY8T1HkgVrydiDMC9PWqGCo= +github.com/inconshreveable/log15 v3.0.0-testing.3+incompatible/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o= +github.com/inconshreveable/log15/v3 v3.0.0-testing.5 h1:h4e0f3kjgg+RJBlKOabrohjHe47D3bbAB9BgMrc3DYA= +github.com/inconshreveable/log15/v3 v3.0.0-testing.5/go.mod h1:3GQg1SVrLoWGfRv/kAZMsdyU5cp8eFc1P3cw+Wwku94= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/itchyny/gojq v0.12.8 h1:Zxcwq8w4IeR8JJYEtoG2MWJZUv0RGY6QqJcO1cqV8+A= @@ -449,14 +451,16 @@ github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlW github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= @@ -620,6 +624,8 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +golang.ngrok.com/ngrok v1.0.0 h1:36xgYK8C05D4V/KslXc+Nm6E+qorNLv8zZiQCHO+FB4= +golang.ngrok.com/ngrok v1.0.0/go.mod h1:h0SmDbrHimeTrjlMgUWh21Ni3e4s5SQZm2nMJZe3XHI= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -710,8 +716,8 @@ golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5o golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= -golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -798,13 +804,15 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI= -golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -813,8 +821,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/pkg/ngrok.go b/pkg/ngrok.go index 3dbdf6cec..5344ba812 100644 --- a/pkg/ngrok.go +++ b/pkg/ngrok.go @@ -2,56 +2,76 @@ package pkg import ( "context" - "fmt" + "github.com/spf13/viper" + "golang.ngrok.com/ngrok" + "golang.ngrok.com/ngrok/config" "io" "net" - "time" "github.com/rs/zerolog/log" - - "github.com/ngrok/ngrok-go" - "github.com/ngrok/ngrok-go/config" - "github.com/spf13/viper" + _ "github.com/spf13/viper" "golang.org/x/sync/errgroup" ) +// RunNgrok creates a ngrok tunnel, listens for incoming connections, and starts a goroutine to handle each connection +// with context passed to it, also it logs the errors and sets the url in viper. RunNgrok is called to run in goroutine +// the caller needs to cancel the context to stop the tunnel. func RunNgrok(ctx context.Context) { - - tunnel, err := ngrok.StartTunnel(ctx, config.HTTPEndpoint(), ngrok.WithAuthtokenFromEnv()) + defer func() { + log.Info().Msg("RunNgrok context was cancelled, conn closed and not accepting new connections, and function exited") + }() + tunnel, err := ngrok.Listen(ctx, config.HTTPEndpoint(), ngrok.WithAuthtokenFromEnv()) if err != nil { log.Error().Err(err).Msg("") } - retry(3, time.Second, "create ngrok tunnel", func() error { - tunnel, err = ngrok.StartTunnel(ctx, config.HTTPEndpoint(), ngrok.WithAuthtokenFromEnv()) - if err != nil { - log.Debug().Err(err).Msg("") - return err + // inform when tunnel is not accepting new connections + go func() { + select { + case <-ctx.Done(): + log.Info().Msg("Ngrok is closed, and not accepting new connections") } - return nil - }) + }() - fmt.Println("tunnel created: ", tunnel.URL()) + log.Info().Msgf("tunnel created: %s", tunnel.URL()) viper.Set("github.atlantis.webhook.url", tunnel.URL()+"/events") viper.Set("ngrok.url", tunnel.URL()) - viper.WriteConfig() + err = viper.WriteConfig() + if err != nil { + log.Error().Err(err).Msg("") + return + } for { + log.Debug().Msgf("current state of the ngrok context is (if nil, its healthy): %s", ctx.Err()) + conn, err := tunnel.Accept() if err != nil { log.Error().Err(err).Msg("") + break } - log.Info().Msgf("accepted connection from %s", conn.RemoteAddr()) - - go func() { - - err := handleConn(ctx, conn) - log.Info().Msgf("connection closed: %v", err) - }() + if ctx.Err() == nil { + log.Info().Msgf("Ngrok is accepting connections: %s", conn.RemoteAddr()) + go func() { + err = handleConn(ctx, conn) + if err == nil { + return + } + log.Info().Err(err).Msg("connection closed: ") + }() + } else { + err := conn.Close() + if err != nil { + println(err) + } + break + } } } +// handleConn handles the connection by copying the data from the connection to the destination address and vice versa +// it also logs the errors and closes the connection when the context is cancelled func handleConn(ctx context.Context, conn net.Conn) error { next, err := net.Dial("tcp", ":80") if err != nil {