Golang executable refresh - go

I have a function which writes/updates a json. But I need to stop the executable, run go build again and re-run the executable to get the json updated in url.
For example, I have a Handler.go file which takes argument from URL as key and runs an if condition and updates the json. So If json value before building the executable is {"Name":"Sneha"} and i pass parameter "Nair" in the url, the json gets updated in the server as {"Name":"Nair"}, but doesnt get updated in the URL. So I have to stop the executable, run go build again and run the executable again to reflect the new json value {"Name":"Nair"} in the URL.
1. Can somebody please suggest an alternative idea ?
2. Can we run go build or go update inside a function?
Help much appreciated.
PS: I have got URL for goagain.go. But am not sure if that matches my requirement.
Handler.go
func handler(w http.ResponseWriter, r *http.Request) {
keys, ok := r.URL.Query()["key"]
if !ok || len(keys) < 1 {
log.Println("Url Param 'key' is missing")
return
}
key := keys[0]
log.Println("Url Param 'key' is: " + string(key))
if key == "java" {
commands := []string{
"Version=`java -version`",
"sed -i -e 's/^\\( *Name: *\\) .*$/ Name:\"Java\",/' Handler.go",
"sed -i -e 's/^\\( *Version: *\\) .*$/ Version:\" '$Version'\",/' Handler.go",
}
exe_cmd(commands)
}
if key == "go" {
commands := []string{
"Version=`go version`",
"sed -i -e 's/^\\( *Name: *\\) .*$/ Name:\"Go\",/' Handler.go",
"sed -i -e 's/^\\( *Version: *\\) .*$/ Version:\" '$Version'\",/' Handler.go",
}
exe_cmd(commands)
}
GetHands := GetHand{
Name:"java",
Version:" 1.7.0_71",
}
if err := json.NewEncoder(w).Encode(GetHands); err != nil {
panic(err)
}
So on running this package, the url shows json value : {"name":"java","version":" 1.7.0_71"}
If I call url : http://localhost:8080/?key=go this Handler.go gets updated to,
GetHands := GetHand{
Name:"go",
Version:" 1.9",
}
If I stop the executable,
run go build again and run executable again the url gets returned as :{"name":"go","version":" 1.9"}
So basically I need dynamic url which on hitting the http:/localhost/?key=go would return go's corresponding value annd htpp://localhost/?key=java would return java's corresponding value. This should be attained without restarting the executable or re-running the go build

Its quite difficult to understand exactly what you want. But I suspect that is essence you simply want to extract the output from a shell command and write it to JSON.
For this there is no need to modify the Handler.go file or do go build. You can simply write the output directly into the GetHand structure.
A basic example is as follows :
package main
import (
"fmt"
"os/exec"
)
var cmds = map[string][]string{
"go": []string{"/usr/local/go/bin/go", "version"},
"java": []string{"java", "-version"},
}
type GetHand struct {
Name string
Version string
}
func handleKey(key string) (*GetHand, error) {
cmd := cmds[key]
if cmd == nil {
return nil, fmt.Errorf("No such key : %v", key)
}
b, err := exec.Command("/usr/local/go/bin/go", "version").Output()
if err != nil {
return nil, err
}
return &GetHand{
Name: key,
Version: string(b),
}, nil
}
func main() {
h, err := handleKey("go")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(h)
h, err = handleKey("java")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(h)
}

Your current code with all those sed tried to modify yor source code. It does not work that way. Some ancient practices (usually using bash) auctually did that kind of staff but it is outdated and problemtic.
As for go, you sure know go is a compiled language, which compiles source code into binaries. In that way, any modification to the source code will not affect binaries, and resulting the process running unaware of that. In that sense, modifying the source code by the process it self is wrong and uneffective. Even if it is possible to use compiler to rebuild binaries, it is going to be both dangerous and inefficient. Do not do it that way.
In your specified case, there is a much better and a much common practice: variable. You sure use it everyday. It would be the simplest to just set the fields of GetHands to needed value. By doing that, your response to each request (what you call URL, but it is wrong) will change because the fields of GetHands change as variable.
I write the code below:
import (
"os/exec"
"regex"
"runtime"
)
var pattern = regexp.MustCompile(`version \"(.+)\"`)
func GetJavaVersion() string {
cmd := exec.Command("Java", "-version")
out, err := cmd.Output()
if err != nil {
panic(err)
}
return string(pattern.FindSubmatch(out)[1])
}
func GetGoVersion() string {
return runtime.Version()[2:]
}
func handler(w http.ResponseWriter, r *http.Request) {
keys, ok := r.URL.Query()["key"]
if !ok || len(keys) < 1 {
log.Println("Url Param 'key' is missing")
return
}
key := keys[0]
log.Println("Url Param 'key' is: " + string(key))
var GetHands GetHand
if key == "java" {
GetHands = GetHand{Name: "java", Version: GetJavaVersion()}
}
if key == "go" {
GetHands = GetHand{Name: "go", Version: GetGoVersion()}
}
if err := json.NewEncoder(w).Encode(GetHands); err != nil {
panic(err)
}
}
So, for go, you can auctually get the version info from package runtime, though the info started with "go". You can simply slice it. And for java, you would have to run a command: The same as your orignal code: java -version. But instead of passing it to sed, you get the output from it by using Command.Output and then use a regexp to find the version information. regexp is a very useful tool to get info from strings. You can read about it here

Related

what's the best practice for testing deleting a directory?

I have a cli that I am making that is more for learning purposes and creating my own cli, that does stuff. Anyways, I am testing the delete function and it works fine and gives me the right answer. However, I don't believe that it is the best practice and was wondering if you could let me know if it's ok, or not.
Test file
func TestDeleteConfig(t *testing.T) {
err := cm.DeleteConfig()
if err != nil {
t.Errorf("error when deleting the folder: %s", err)
}
usr, err := user.Current()
if err != nil {
t.Errorf("error when getting user current: %s", err)
}
fp := filepath.Join(usr.HomeDir, ".config", "godot", "config.json")
fmt.Println("the path of the config file", fp)
if _, e := os.Stat(fp); !os.IsNotExist(e) {
t.Errorf("error path still exists: %v", e)
}
}
function being tested
func DeleteConfig() error {
usr, err := user.Current()
if err != nil {
return err
}
err = os.RemoveAll(filepath.Join(usr.HomeDir, ".config", "godot"))
if err != nil {
return err
}
return nil
}
The problem is that I don't want the DeleteConfig() to take any arguments as this is a hard path. I have a separate function for deleting a single file in which the help with this will solve that as well func DeleteFile(p string) error {}.
So for testing purposes should I just create a foo directory at a separate path (within the test), delete said directory, and assume that if it works on foo path then it should work with the godot directory?
It's a bit philosophical.
If you absolutely do not want to stub/mock any part of your code to replace accessing a real file system to do testing, then we're talking about what you'd call system or integration testing. That's fine in itself: for instance, you could run such tests in a throw-away container as part of a CI pipeline.
But with this approach you can't sensibly do what is called unit-testing.
To unit-test your function you need to replace something it uses with something "virtualized". Exactly how to do that is an open question.
Approach: make is possible to override os.RemoveAll
For instance, you can have a private global variable containing the "remove whole directory" function—like this:
var removeAll = os.RemoveAll
func DeleteConfig() error {
usr, err := user.Current()
if err != nil {
return err
}
err = removeAll(filepath.Join(usr.HomeDir, ".config", "godot"))
if err != nil {
return err
}
return nil
}
Then to test it you'd just monkey-patch the function with your own implementation which would have the same signature as os.RemoveAll, like this:
func TestDeleteConfig(t *testing.T) {
var actualPath string
removeAll = func(path string) {
actualPath = path
return nil
}
defer func() { removeAll = os.RemoveAll }()
DeleteConfig()
if actualPath != expectedPath {
t.Errorf("unexpected path: want %s, got %s", expectedPath, actualPath)
}
}
This approach also allows to test how your function handles errors: just replace it with something which generates an error and then afterwards check that your function under test returned that error (or wrapped it in an expected way, whatever).
Still, it's a bit low-tech for a number of reasons:
A global variable is involved, so you have to be sure no two tests which monkey-patch it run concurrently, or that all patching is done before running those tests.
If different tests need to set it to different value, they must be serialized.
Approach: do not hard-code the concept of "the user" or "the config"
Another approach is to notice that basically the problem with testing stems from the fact you hard-coded getting of the user.
Leaving aside the flawed approach you've taken to getting the place of configuration (you should be using something which implements the XDG spec), if you could easily override getting of the "root" directory (which is the user's home directory in your code), you could easily target your function to operate on the result of calling io/ioutil.TempDir.
So may be a way to go is to have an interface type like
type ConfigStore interface {
Dir() string
}
of which the Dir() method is supposed to return the path to the configuration store's root directory.
Your DeleteConfig() would then start to accept a single argument of type ConfigStore, and in your program you'd have a concrete implementation of it, and in your testing code — a stub implementing the same interface and managing a temporary directory.
Approach: go full-on virtualized
Right now, a work is being done on bringing filesystem virtualization right into the Go standard library, but while it's not there yet, 3rd-party packages which do that exist for ages, — for instance, github.com/spf13/afero.
Basically, they allow you to not use os directly but write all your code in a way so that instead of the os package it calls methods on an instance of a type implementing particular interface: in the production code that object is a thin shim for the os package, and in the testing code it's replaced by whatever you wish; afero has a readily-available in-memory FS backend to do this.
Writing a unit test for filesystem checking is not trivial. You should NOT create a real file on the system, because then your test will depend on the I/O of the file system itself. The last resort is mocking the filesystem. There are quite a few powerful libraries like spf13/afero for this purpose (mocking of a filesystem). These packages will create temporary files in the background and clean up afterward.
main.go
package main
import (
"log"
"os/user"
"path/filepath"
iowrap "github.com/spf13/afero"
)
var (
// FS is simulated filesystem interface
FS iowrap.Fs
// FSUtil is the struct of the simulated interface
FSUtil *iowrap.Afero
)
func init() {
FS = iowrap.NewOsFs()
FSUtil = &iowrap.Afero{Fs: FS}
}
// DeleteConfig removes ~/.config/godot if exists
func DeleteConfig() error {
usr, err := user.Current()
if err != nil {
return err
}
path := filepath.Join(usr.HomeDir, ".config", "godot")
log.Println(path)
err = FSUtil.RemoveAll(path)
return err
}
func main() {
err := DeleteConfig()
if err != nil {
log.Fatal(err)
}
}
main_test.go
package main
import (
"os/user"
"path/filepath"
"testing"
iowrap "github.com/spf13/afero"
)
func init() {
FS = iowrap.NewMemMapFs()
FSUtil = &iowrap.Afero{Fs: FS}
usr, _ := user.Current()
pathDir := filepath.Join(usr.HomeDir, ".config")
filePath := filepath.Join(pathDir, "godot")
FS.MkdirAll(pathDir, 0755)
iowrap.WriteFile(FS, filePath, []byte("0-7\n"), 0644)
}
const (
succeed = "\u2713"
failed = "\u2717"
)
func TestDeleteConfig(t *testing.T) {
t.Log("Given the need to test downloading a webpage content")
{
usr, _ := user.Current()
pathDir := filepath.Join(usr.HomeDir, ".config")
filePath := filepath.Join(pathDir, "godot")
t.Logf("\tTest 0:\tWhen deleting the %v with 0644 permissions", filePath)
{
err := DeleteConfig()
if err != nil {
t.Fatalf("\t%s\tThe file couldn't be deleted: %v", failed, err)
}
t.Logf("\t%s\tThe file has been successfully deleted.", succeed)
}
}
}
Functional test:
touch C:/Users/drpan/.config/godot
ls -l C:/Users/drpan/.config/godot Output: -rw-r--r-- 1 drpan 197609 0 Nov 2 19:38 C:/Users/drpan/.config/godot
./deletedirectory.exe
ls -l C:/Users/drpan/.config/godot Output: ls: cannot access 'C:/Users/drpan/.config/godot': No such file or directory
Unit Test:
$ touch C:/Users/drpan/.config/godot
$ go test
2020/11/02 19:55:35 C:\Users\drpan\.config\godot
PASS
ok github.com/drpaneas/deletedirectory 0.162s
$ ls -l C:/Users/drpan/.config/godot Output: -rw-r--r-- 1 drpan 197609 0 Nov 2 19:55 C:/Users/drpan/.config/godot

How to automate two os.Stdin input using Bash

I need to automate the input of a code segment like bellow where the inputs of the ReadString are distinct.
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
buf := bufio.NewReader(os.Stdin)
value, err := buf.ReadString('\n')
if err != nil {
fmt.Println(err)
} else {
fmt.Println(value)
}
buf = bufio.NewReader(os.Stdin)
value, err = buf.ReadString('\n')
if err != nil {
fmt.Println(err)
} else {
fmt.Println(value)
}
}
I have tried several formats like the bellow one following answers of this question, unfortunately, none worked.
>> echo "data1
data2" | go run main.go
output: data1
EOF
Here data1 and data2 and input of the separate ReadString methods. I don't have control over the source code. So, I can only try changing the bash input. How to resolve this issue?
This is happening because the second string does not end with a newline. Looking at the documentation for ReadString:
If ReadString encounters an error before finding a delimiter, it returns the data read before the error and the error itself (often io.EOF).
So, even though error is non-nil, you have the data. The following change should work for this specific case:
buf = bufio.NewReader(os.Stdin)
value, err = buf.ReadString('\n')
if value!="" {
fmt.Println(value)
}
if err != nil {
fmt.Println(err)
}
In general, even if you get an error from ReadString, you may still have nonempty data returned from the function.

Readline Failed in Go Test Case

I'm experimenting getting keyboard input in a test case, basically I followed this example,
https://www.socketloop.com/tutorials/golang-read-input-from-console-line
and in my unit test case, it always result in error of "EOF" without giving me a chance to type in from keyboard.
Is there any special in Go unit test environment? Or I should look into another better option?
My code looks like,
func (o *Player) consoleReadLn() (line string) {
consoleReader := bufio.NewReader(os.Stdin)
line, err := consoleReader.ReadString('\n')
if err != nil {
panic(err.Error()) // it just panic: EOF
}
return
}
Firstly, your code should be corrected to:
import "testing/iotest"
func (o *Player) consoleReadLn() string {
consoleReader := bufio.NewReader(os.Stdin)
s := ""
for {
s1, err := consoleReader.ReadString('\n')
if err == io.EOF {
break
}
if err != nil && err != iotest.ErrTimeout {
panic("GetLines: " + err.Error())
}
s += s1
}
return s
}
Because you expect using \n as delimiter of a line of string, so it will return the data with EOF which is \n in Unix OS, see godoc bufio#Reader.ReadString:
ReadString reads until the first occurrence of delim in the input, returning a string containing the data up to and including the delimiter. If ReadString encounters an error before finding a delimiter, it returns the data read before the error and the error itself (often io.EOF). ReadString returns err != nil if and only if the returned data does not end in delim. For simple uses, a Scanner may be more convenient.
However, I suggest reading this answer Read from initial stdin in GO?
Secondly, it is hard to test STDIN in unit test context like go test. I found this mail saying:
the new go test runs tests with standard input connected
to /dev/null.
So I think it is hard to test os.Stdin via go test directly, for example, the following code confirmed it doesn't read /dev/stdin at all when running command echo this is stdin | go test ./:
import "io/ioutil"
import "testing"
import "fmt"
func TestSTDIN(t *testing.T) {
bytes, err := ioutil.ReadAll(os.Stdin)
if err != nil {
t.Fatal(err)
}
fmt.Println(string(bytes))
}

In Go, is it possible to write to the console and read back?

I'm writing a wizard for a CLI in Go. What I'd like to do is ask the user what he wants to do, prepare the appropriate CLI command, and write it to the console. The user then would submit the command to the CLI by pressing Enter, possibly after editing it first. In other words, I want to write output to stdout that becomes input to stdin when the user presses Enter. Is there a way to do this in Go?
For getting input directly from user:
var s string
_, err := fmt.Scanf("%s", &s)
For curses-like application, look here:
https://github.com/rthornton128/goncurses/blob/master/ncurses.go
It has C bindings.
I've found a Go command line editor package, https://github.com/peterh/liner, that incorporates the capability requested and a good deal more. It allows you to write a CLI with history and command completion, with the command completion feature providing exactly what I asked for in the question: it presents text that the user can edit and submit to the CLI. It supports Windows, Linux, and Mac. Here's a slightly modified version of the example in its README that runs a simple CLI with command completion. For example, the user can type "j" and press Tab to cycle through a list of names, edit one to taste, and press Enter to submit.
package main
import (
"fmt"
"log"
"os"
"strings"
"github.com/peterh/liner"
)
var (
history_fn = ".liner_history"
names = []string{"jack", "john", "james", "mary", "mike", "nancy"}
)
func main() {
line := liner.NewLiner()
defer line.Close()
line.SetCompleter(func(line string) (c []string) {
for _, n := range names {
if strings.HasPrefix(n, strings.ToLower(line)) {
c = append(c, n)
}
}
return
})
if f, err := os.Open(history_fn); err == nil {
line.ReadHistory(f)
f.Close()
}
line.SetCtrlCAborts(true)
for true {
if name, err := line.Prompt("What is your name? "); err != nil {
if err.Error() == "EOF" || err == liner.ErrPromptAborted {
break
}
log.Print("Error reading line: ", err)
} else {
log.Print("Got: ", name)
line.AppendHistory(name)
}
}
fmt.Printf("End of test\n")
if f, err := os.Create(history_fn); err != nil {
log.Print("Error writing history file: ", err)
} else {
line.WriteHistory(f)
f.Close()
}
}

Finding imports and dependencies of a go program

The go list -json command run from the command line will tell you the imports and dependencies of a go program ( in json format). Is there a way to get this information from within a go program I.e at runtime, either by running the 'go list' command somehow or another way?
The following code uses the go/build to get the imports for the application in the current working directory.
p, err := build.Default.Import(".", ".", 0)
if err != nil {
// handle error
}
for _, i := range p.Imports {
fmt.Println(i)
}
You can build a list of all dependencies using a simple recursive function.
To get the imports for a specific path, use:
p, err := build.Default.Import(path, ".", 0)
if err != nil {
// handle error
}
for _, i := range p.Imports {
fmt.Println(i)
}
I don't think you can do it without using the go binary since go needs to analyze your source code.
It's pretty easy to do but it must have access to go and your source code at run time. Heres a quick example:
package main
import (
"encoding/json"
"fmt"
"os/exec"
)
func main() {
cmd := exec.Command("go", "list", "-json")
stdout, err := cmd.Output()
if err != nil {
println(err.Error())
return
}
var list GoList
err = json.Unmarshal(stdout, &list)
for _, d := range list.Deps {
fmt.Printf(" - %s\n", d)
}
}
type GoList struct {
Dir string
ImportPath string
Name string
Target string
Stale bool
Root string
GoFiles []string
Imports []string
Deps []string
}
No, I don't think this is possible without the source in a reliable way. Go binaries on different platforms, compiled with different compilers may or may not have (or may not have in the future) these informations compiled in.
But as Go programs are compiled anyway: Why not record this information while you do have access to the source code?

Resources