Catch stdErr output in golang - go

I have written a go program to monitor some MySQL databases.
In the following example, I simply display "Connection Success!" when the connection is successful or "Connection Error:" followed by the error message in case of failure.
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
func main() {
err := mysqlConnect()
if err != nil {
fmt.Println("Connection Error:", err)
} else {
fmt.Println("Connection Success!")
}
}
func mysqlConnect() error {
db, err := sql.Open("mysql", "myuser:mypwd#tcp(10.2.0.3:3306)/mysql")
if err != nil {
return err
}
defer db.Close()
err = db.Ping()
if err != nil {
return err
}
return nil
}
This works as expected, but in some cases, some error message appears while executing db.Ping() and is displayed to stdErr:
For example:
$ go run main.go
[mysql] 2019/07/22 18:01:59 auth.go:293: unknown auth plugin:dialog
Connection Error: this authentication plugin is not supported
The last line is my expected message, but the previous line is displayed when calling db.Ping().
I would like to catch/hide this message [mysql] 2019/07/22 18:01:59 auth.go:293: unknown auth plugin:dialog, as I already gets a clean error message that I can handle as I wish (displaying it or not).
How can I prevent such an error message from appearing (in code I mean, not at the calling time)?

As the comment by #tkausi suggests, you ought to set a custom logger for the go-sql-driver package with https://godoc.org/github.com/go-sql-driver/mysql#SetLogger
Your custom logger can then do whatever makes sense for your application, including "swallowing" these logging statements and not displaying anything in case of no errors.
To answer the question's title, you can capture os.Stderr with something like this:
func doPrint() {
fmt.Fprintf(os.Stderr, "output")
}
func main() {
old := os.Stderr
r, w, _ := os.Pipe()
os.Stderr = w
doPrint()
w.Close()
var buf bytes.Buffer
io.Copy(&buf, r)
os.Stderr = old
fmt.Println("Captured:", buf.String())
}

Related

`no such file or directory` with `os.Remove` inside go routine

I added a new command to my CLI application using the Cobra framework. This command is supposed to start a TCP server that accepts socket connections. It receives a payload which is an absolute path to a file/directory and tries to delete it. Here is the code for the command
package cmd
import (
"bufio"
"fmt"
"net"
"os"
"github.com/spf13/cobra"
"wpgenius.io/util"
)
var cachePurgerCmd = &cobra.Command{
Use: "cache-purger",
Short: "Listen for request to purge NGINX page cache",
Run: func(cmd *cobra.Command, args []string) {
dstream, err := net.Listen("tcp", ":9876")
if err != nil {
util.HandleError(err, "Can not start listener..")
return
}
fmt.Println("Listening for purge requests...")
defer dstream.Close()
for {
con, err := dstream.Accept()
if err != nil {
util.HandleError(err, "Can not accept connection")
os.Exit(1)
}
go handleRequest(con)
}
},
}
func handleRequest(con net.Conn) {
path, err := bufio.NewReader(con).ReadString('\n')
if err != nil {
return
}
defer con.Close()
err = os.Remove(path)
if err != nil {
con.Write([]byte("ERROR"))
fmt.Println(err)
util.HandleError(err, "Can not delete cache file")
return
}
con.Write([]byte("SUCCESS"))
}
func init() {
rootCmd.AddCommand(cachePurgerCmd)
}
Although the file/directory exists, I still get no such file or directory error.
I did a sanity check by simply adding the os.Remove to the main function to make sure it's not related to the path and I can see it successfully delete the file/directory.
I'm not sure if it has something to do with go routing or with the tcp server!
Any help will be highly appreciated!
I guess the point is \n in the path you input.

execute functions in specific order

I have the following function
func (c *Connection) ConnectVpn() error {
cmd := exec.Command(c.Command, c.Args...)
var password bytes.Buffer
password.WriteString(os.Getenv("PASSWORD"))
cmd.Stdin = &password
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Start()
if err != nil {
return err
}
return err
}
This function call the openconnect binary and connects in a private vpn to be able to reach a specific server (it works fine).
The problem is that when I call cmd.Start() it creates a thread and allows me to execute the another function named checkCertificate() but then this function is called before the vpn connects so it fails.
When I try to let the VPN connects and use cmd.RUN() this process do not run on background so the process never finish and it never tells to cmd.Wait() it finished because it shouldn't finish.
days, err := domain.CheckCertificate()
if err != nil {
log.Fatalln(err)
}
I have tried to use channels to try to sync the results between them but when I do this the checkCertificate() function keeps being executed before the VPN executes and I can't reach the server I need.
Any idea in how I could let the ConnectVPN() function be running on foreground and even so send some signal to my other function to say vpn is connected now, please run?
I have tried to send the openconnect to background with cmd.Process.Signal(syscall.SIGTSTP) but when I bring it back it breaks the main function.
I have implemented the function to check if this is connected as was suggested before.
Now it only triggers the other functions if this VPN is connected.
package main
import (
"fmt"
"gitlabCertCheck/request"
"gitlabCertCheck/teams"
"gitlabCertCheck/vpn"
"net/http"
"os"
"strconv"
)
func checker() {
client := http.Client{}
_, err := client.Get(os.Getenv("HTTPS_HOST"))
if err != nil {
checker()
}
return
}
func main() {
args := []string{"--user", os.Getenv("USERNAME"), "--authgroup", "default", "--background",os.Getenv("VPN_SERVER")}
conn := vpn.NewConnection("openconnect", args)
err := conn.ConnectVpn()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
checker()
client := request.Request{}
domain := client.NewRequest(os.Getenv("HOST"), os.Getenv("PORT"))
days, err := domain.CheckCertificate()
if err != nil {
fmt.Println(err)
}
card := teams.Card{}
card.CustomCard.Title = "Certificate Alert"
card.CustomCard.Text = "Certificate will expire in " + strconv.Itoa(days) + " days"
card.NewCard(card.CustomCard)
err = card.SendMessageCard(os.Getenv("TEAMS_WEBHOOK"))
if err != nil {
fmt.Println(err)
}
}
Thank you everyone

Closing amqp.Channel when consumer is failed is not responding

I use https://github.com/NeowayLabs/wabbit/
When amqp.Channel is closing after wrong try channel.Consume, we have a not listened chan and function is not responding.
My code:
package main
import (
"fmt"
"github.com/NeowayLabs/wabbit"
"github.com/NeowayLabs/wabbit/amqptest"
"github.com/NeowayLabs/wabbit/amqptest/server"
)
func someFunc(amqpURL string) error {
conn, err := amqptest.Dial(amqpURL)
defer conn.Close()
channel, err := conn.Channel()
defer channel.Close()
consumer, err := channel.Consume(
"queue",
"consumer",
wabbit.Option{},
)
if err != nil {
return err // err = "Unknown queue 'queue'", but we never response it
}
fmt.Println(<-consumer)
return nil
}
func main() {
amqpURL := "127.0.0.1:32773"
fakeServer := server.NewServer(amqpURL)
err := fakeServer.Start()
defer fakeServer.Stop()
err = someFunc(amqpURL)
if err != nil {
panic(err)
}
fmt.Println("Happy end")
}
someFunc never responding with error, but I want to handle consumer errors.
someFunc never responds with an error because it gets hung up in the defer code.
When someFunc gets to the return err line, then it tries to run the defer statements that you set up in the beginning of the function. The first one that it tries is defer channel.Close().
The problem seems to be with this block in the wabbit library: https://github.com/NeowayLabs/wabbit/blob/d8bc549279ecd80204a8a83a868a14fdd81d1a1b/amqptest/server/channel.go#L315-L317
I think, although I am not sure, that writing to the consumer.done channel is a blocking operation because the channel is not buffered and does not have a receiver. See this: https://gobyexample.com/non-blocking-channel-operations for more information.
I commented that block of code out when running this locally and found that the rest of the code ran as you expected that it would, finally resulting in a panic: Unknown queue 'queue'

rpc.ServerCodec Still Serving?

I was performing some RPC tests, and stumbled across a problem I can't seem to solve. In my testing I create three separate RPC servers, all of which I try to close and shutdown. However upon performing my last test (TestRpcCodecServerClientComm), it seems my client connection is connecting to the first RPC server I started (I know this because I at some point attached IDs to the RPCHandlers), even though I attempted everything I could to make sure it was shutdown. Though the code is not there I have attempted to inspect every single error I could, but that did not bring about anything.
rpc.go
package rbot
import (
"io"
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
func RpcCodecClientWithPort(port string) (rpc.ClientCodec, error) {
conn, err := net.Dial("tcp", "localhost:"+port)
if err != nil {
return nil, err
}
return jsonrpc.NewClientCodec(conn), nil
}
func RpcCodecServer(conn io.ReadWriteCloser) rpc.ServerCodec {
return jsonrpc.NewServerCodec(conn)
}
rpc_test.go
package rbot
import (
"errors"
"fmt"
"net"
"net/rpc"
"testing"
)
type RPCHandler struct {
RPCServer net.Listener
conn rpc.ServerCodec
done chan bool
TestPort string
stop bool
GotRPC bool
}
func (r *RPCHandler) SetupTest() {
r.stop = false
r.GotRPC = false
r.done = make(chan bool)
r.TestPort = "5556"
}
// TODO: Create separate function to handle erroring
func (r *RPCHandler) CreateRPCServer() error {
rpc.RegisterName("TestMaster", TestAPI{r})
var err error
r.RPCServer, err = net.Listen("tcp", ":"+r.TestPort)
if err != nil {
return err
}
go func() {
for {
conn, err := r.RPCServer.Accept()
if err != nil || r.stop {
r.done <- true
return
}
r.conn = RpcCodecServer(conn)
rpc.ServeCodec(r.conn)
}
}()
return nil
}
func (r *RPCHandler) CloseRPCServer() error {
r.stop = true
if r.conn != nil {
err := r.conn.Close()
if err != nil {
fmt.Println(err)
}
}
err := r.RPCServer.Close()
<-r.done
return err
}
type TestAPI struct {
t *RPCHandler
}
func (tapi TestAPI) Send(msg string, result *string) error {
if msg == "Got RPC?" {
tapi.t.GotRPC = true
return nil
}
return errors.New("Didn't receive right message")
}
// Check if we can create and close an RPC server successfully using the RPC server codec.
func TestRpcCodecServer(t *testing.T) {
r := RPCHandler{}
r.SetupTest()
err := r.CreateRPCServer()
if err != nil {
t.Fatalf("Could not create rpc server! %s:", err.Error())
}
err = r.CloseRPCServer()
if err != nil {
t.Fatalf("Could not close RPC server! %s:", err.Error())
}
}
// Check if we can create a client without erroring.
func TestRpcCodecClientWithPortt(t *testing.T) {
r := RPCHandler{}
r.SetupTest()
r.CreateRPCServer()
defer r.CloseRPCServer()
RPCClient, err := RpcCodecClientWithPort(r.TestPort)
defer RPCClient.Close()
if err != nil {
t.Fatalf("Could not create an RPC client! %s:", err.Error())
}
}
// Let's double check and make sure our server and client can speak to each other
func TestRpcCodecServerClientComm(t *testing.T) {
r := RPCHandler{}
r.SetupTest()
r.CreateRPCServer()
defer r.CloseRPCServer()
RPCCodec, _ := RpcCodecClientWithPort(r.TestPort)
RPCClient := rpc.NewClientWithCodec(RPCCodec)
defer RPCClient.Close()
var result string
err := RPCClient.Call("TestMaster.Send", "Got RPC?", &result)
if err != nil {
t.Fatalf("Error while trying to send RPC message: %s", err.Error())
}
if !r.GotRPC {
t.Fatalf("Could not send correct message over RPC")
}
}
Not sure if I'm just mishandling the connection or something of the like, any help would be much appreciated.
For the Record The RPC api does receive the correct string message
While not the source of your problems, your test configuration has a few race conditions which you should take care of before they cause problems. Always check for issues with the -race option. You should also let the OS allocate the port so you don't run into conflicts. See for example how httptest.Server works.
Your failure here is that you're not creating a new rpc.Server for each test, you're reusing the rpc.DefaultServer. The first call to CreateRPCServer registers a TestAPI under the name TestMaster. Each subsequent call uses the already registered instance.
If you create a new rpc.Server each time you setup the test and register a new TestAPI, the final test will pass.
srv := rpc.NewServer()
srv.RegisterName("TestMaster", testAPI)
...
// and then use srv to handle the new connection
srv.ServeCodec(RpcCodecServer(conn))

How to make a websocket client wait util the server is running?

I want to create a websocket client that waits until the server is running. If the connection is closed by the server it should reconnect.
What I tried does not work and my code exits with a runtime error:
panic: runtime error: invalid memory address or nil pointer dereference
func run() {
origin := "http://localhost:8080/"
url := "ws://localhost:8080/ws"
ws, err := websocket.Dial(url, "", origin)
if err != nil {
fmt.Println("Connection fails, is being re-connection")
main()
}
if _, err := ws.Write([]byte("something")); err != nil {
log.Fatal(err)
}
}
Your example looks like a code snippet. It's difficult to say why you're getting that error without seeing all the code. As were pointed out in the comments to your post, you can't call main() again from your code and including the line numbers from the panic report would be helpful as well.
Usually minimizing your program to a minimal case that anyone can run and reproduce the error is the fastest way to get help. I've reconstructed yours for you in such fashion. Hopefully you can use it to fix your own code.
package main
import (
"websocket"
"fmt"
"log"
"time"
)
func main() {
origin := "http://localhost:8080/"
url := "ws://localhost:8080/ws"
var err error
var ws *websocket.Conn
for {
ws, err = websocket.Dial(url, "", origin)
if err != nil {
fmt.Println("Connection fails, is being re-connection")
time.Sleep(1*time.Second)
continue
}
break
}
if _, err := ws.Write([]byte("something")); err != nil {
log.Fatal(err)
}
}
To run this, just copy it into a file called main.go on your system and then run:
go run main.go

Resources