I need to use config in my go code and I want to load config path from command-line.
I try:
if len(os.Args) > 1 {
configpath := os.Args[1]
fmt.Println("1") // For debug
} else {
configpath := "/etc/buildozer/config"
fmt.Println("2")
}
Then I use config:
configuration := config.ConfigParser(configpath)
When I launch my go file with parameter (or without) I receive similar error
# command-line-arguments
src/2rl/buildozer/buildozer.go:21: undefined: configpath
How should I correctly use os.Args?
Define configPath outside the scope of your if.
configPath := ""
if len(os.Args) > 1 {
configPath = os.Args[1]
fmt.Println("1") // For debugging purposes
} else {
configPath = "/etc/buildozer/config"
fmt.Println("2")
}
Note the 'configPath =' (instead of :=) inside the if.
That way configPath is defined before and is still visible after the if.
See more at "Declarations and scope" / "Variable declarations".
For another approach, you can wrap the logic in a function:
package main
import "os"
func configPath(a []string) string {
switch len(a) {
case 1: return "/etc/buildozer/config"
default: return os.Args[1]
}
}
func main() {
config := configPath(os.Args)
println(config)
}
this avoids the scoping issue.
Related
I'm creating a scheduler using Golang's crontab and resty.v2 to call a POST api(Dropbox file upload api).
When I'm invoking the file upload method manually its working fine. But when the same method is invoked via the crontab scheduler its not making the rest call.
Interesting fact is that, it's neither throwing any error nor providing any rest call response.
PFB the scheduler code to invoke the upload method:
func StartJob() {
ctab := crontab.New()
for _, v := range strings.Split(os.Getenv("schedules"), commaSeparator) {
match, _ := regexp.MatchString(regex, v)
if match == true {
hhmm := strings.Split(v, colonSeparator)
expresion := fmt.Sprintf(cronExpression, hhmm[1], hhmm[0])
ctab.MustAddJob(expresion, func() {
go service.Upload(dropboxEndpoint, dropboxAccessToken, os.Getenv("hkpath"))
})
} else {
fmt.Printf("Not acceptable time-format : %s\n", v)
}
}}
And here is the upload method code:
func Upload(dropboxEndpoint string, dropboxAccessToken string, path string) {
_, fileName := filepath.Split(path)
fileBytes, fileErr := ioutil.ReadFile(path)
if fileErr == nil {
fmt.Println(path)
resp, restErr := client.R().
SetBody(fileBytes).
SetContentLength(true).
SetHeader("Authorization", fmt.Sprintf("Bearer %s", dropboxAccessToken)).
SetHeader("Dropbox-API-Arg", fmt.Sprintf("{\"path\": \"/home/f1/%s/%s\",\"mode\": \"add\",\"autorename\": true,\"mute\": false,\"strict_conflict\": false}", os.Getenv("name"), fileName)).
SetHeader("Content-Type", "application/octet-stream").
Post(dropboxEndpoint)
if restErr != nil {
log.Fatal(restErr)
} else {
fmt.Println(resp)
}
} else {
log.Fatal(fileErr)
}}
Any idea what's the wrong I'm doing?
i'm trying to convert string of objectid to bson ObjectId format with mgo,
errCheck := d.C("col").FindId(bson.ObjectIdHex(obid[0])).One(&Result)
idk why, but if i give a wrong / invalid input string, my application got runtime panic
how i can prevent that ? thank you
bson.ObjectIdHex() documents that it will panic if you pass an invalid object id:
ObjectIdHex returns an ObjectId from the provided hex representation. Calling this function with an invalid hex representation will cause a runtime panic. See the IsObjectIdHex function.
If you want to avoid this, first check your input string using bson.IsObjectIdHex(), and only proceed to call bson.ObjectIdHex() if your input is valid:
if bson.IsObjectIdHex(obid[0]) {
// It's valid, calling bson.ObjectIdHex() will not panic...
}
As #icza said in the last answer. you should check validity if ObjectId.
And you can use panic recover defer to handle any kind of error in future
package main
import (
"fmt"
"gopkg.in/mgo.v2/bson"
"path/filepath"
"runtime"
"strings"
)
func main() {
r := Result{}
getData(&r)
}
func IdentifyPanic() string {
var name, file string
var line int
var pc [16]uintptr
n := runtime.Callers(3, pc[:])
for _, pc := range pc[:n] {
fn := runtime.FuncForPC(pc)
if fn == nil {
continue
}
file, line = fn.FileLine(pc)
name = fn.Name()
if !strings.HasPrefix(name, "runtime.") {
break
}
}
file = filepath.Base(file)
switch {
case name != "":
return fmt.Sprintf("%v:%v", file, line)
case file != "":
return fmt.Sprintf("%v:%v", file, line)
}
return fmt.Sprintf("pc:%x", pc)
}
type Result struct {
success int
data string
}
func getData(result *Result){
defer func() {
if err := recover(); err != nil {
ip := IdentifyPanic()
errorMessage := fmt.Sprintf("%s Error: %s", ip, err)
fmt.Println(errorMessage)
result.success = 0
}
}()
if bson.IsObjectIdHex(obid[0]) { // this line copied from #icza answer
// It's valid, calling bson.ObjectIdHex() will not panic... // this line copied from #icza answer
errCheck := d.C("col").FindId(bson.ObjectIdHex(obid[0])).One(&res)
result.success = 1
result.data = "your result (res). this is just the exam"
}else{
result.success = 0
}
}
How do I run a Gulp task from a Go program?
This is the command I run from a typical terminal:
gulp serv.dev
How could I run this simple line of code from golang:
package main
import (
"net/http"
"github.com/julienschmidt/httprouter"
"fmt"
)
func main() {
// What do I put here to open terminal in background and run `gulp serv.dev`?
}
What you're looking for is exec.Command
You'll pretty much want to spawn off a process that will run your gulp task.
This can be done like so:
package main
import (
"os/exec"
)
func main() {
cmd := exec.Command("gulp", "serv.dev")
if err := cmd.Run(); err != nil {
log.Fatal(err)
}
}
Take a look at exec. For your use case:
package main
import (
"net/http"
"github.com/julienschmidt/httprouter"
"fmt"
"os/exec"
"log"
)
func main() {
out, err := exec.Command("gulp", "serv.dev").Output()
if err != nil {
log.Fatal(err)
}
fmt.Printf("The date is %s\n", out)
}
Most probably you need exec package
cmd := exec.Command("gulp", "serv.dev")
err := cmd.Run()
Take a look at example at exec.Command. They explained how to pass parameters and read the output.
More generic, better output.
Use the exec.Command, along with buffers to record output and display it only when useful.
You can even make the function work with any command by using variadic arguments, aka arguments of an arbitrary quantity of elements.
Label unhanded errors appropriately, so if a command fails you are told which one and why.
Lastly note that Go, although expressive, is a quite raw language. It holds your hand for nothing. You will have to program plenty by yourself.
Example code:
package main
import (
"bytes"
"fmt"
"os"
"os/exec"
"runtime"
"strings"
)
func main() {
runCommand(currentFunction(), "ping", "-c1", "google.commm")
}
func commandErrorMessage(stderr bytes.Buffer, program string) string {
message := string(stderr.Bytes())
if len(message) == 0 {
message = "the command doesn't exist: " + program + "\n"
}
return message
}
func currentFunction() string {
counter, _, _, success := runtime.Caller(1)
if !success {
println("functionName: runtime.Caller: failed")
os.Exit(1)
}
return runtime.FuncForPC(counter).Name()
}
func printCommandError(stderr bytes.Buffer, callerFunc string, program string, args ...string) {
printCommandErrorUbication(callerFunc, program, args...)
fmt.Fprintf(os.Stderr, "%s", commandErrorMessage(stderr, program))
}
func printCommandErrorUbication(callerFunc string, program string, args ...string) {
format := "error at: %s: %s %s\n"
argsJoined := strings.Join(args, " ")
fmt.Fprintf(os.Stderr, format, callerFunc, program, argsJoined)
}
func runCommand(callerFunc string, program string, args ...string) {
command := exec.Command(program, args...)
var stderr bytes.Buffer
command.Stderr = &stderr
fail := command.Run()
if fail != nil {
printCommandError(stderr, callerFunc, program, args...)
os.Exit(1)
}
}
Example run:
$ go run test.go
error at: main.main: ping -c1 google.commm
ping: google.commm: Name or service not known
exit status 1
I have a global variable that I am trying to use across two different functions, and unable to figure out why the following code is not working...
package main
import (
"github.com/ant0ine/go-json-rest/rest"
"log"
"net"
"net/http"
)
type Message struct {
Body string
}
var api rest.Api
func hostLookup(w rest.ResponseWriter, req *rest.Request) {
ip, err := net.LookupIP(req.PathParam("host"))
if err != nil {
rest.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.WriteJson(&ip)
}
func foo() {
api := rest.NewApi()
api.Use(rest.DefaultDevStack...)
router, err := rest.MakeRouter(
&rest.Route{"GET", "/lookup/#host", hostLookup},
)
if err != nil {
log.Fatal(err)
}
api.SetApp(router)
}
func bar() {
log.Fatal(http.ListenAndServe(":8080", api.MakeHandler()))
}
func main() {
foo()
bar()
}
The above code does not work... the HTTP server does not route the request to the hostLookup function.
However - if I move the following line from bar()
log.Fatal(http.ListenAndServe(":8080", api.MakeHandler()))
to the end of function foo(), then it works correctly
What am I doing wrong?
Your problem is two fold...
For one, you declare
var api rest.Api
but the rest.New() returns a *rest.Api
func NewApi() *Api {
Secondly, in your foo() function, you are creating a local variable called api instead of using your package variable.
Instead of
api := rest.NewApi()
It should be
api = rest.NewApi()
So, the fix is to add a * before rest.Api as in var api *rest.Api and remove a colon from the setting of api as in api = rest.NewApi()
I tried the Go Tour exercise #71
If it is run like go run 71_hang.go ok, it works fine.
However, if you use go run 71_hang.go nogood, it will run forever.
The only difference is the extra fmt.Print("") in the default in the select statement.
I'm not sure, but I suspect some sort of infinite loop and race-condition? And here is my solution.
Note: It's not deadlock as Go didn't throw: all goroutines are asleep - deadlock!
package main
import (
"fmt"
"os"
)
type Fetcher interface {
// Fetch returns the body of URL and
// a slice of URLs found on that page.
Fetch(url string) (body string, urls []string, err error)
}
func crawl(todo Todo, fetcher Fetcher,
todoList chan Todo, done chan bool) {
body, urls, err := fetcher.Fetch(todo.url)
if err != nil {
fmt.Println(err)
} else {
fmt.Printf("found: %s %q\n", todo.url, body)
for _, u := range urls {
todoList <- Todo{u, todo.depth - 1}
}
}
done <- true
return
}
type Todo struct {
url string
depth int
}
// Crawl uses fetcher to recursively crawl
// pages starting with url, to a maximum of depth.
func Crawl(url string, depth int, fetcher Fetcher) {
visited := make(map[string]bool)
doneCrawling := make(chan bool, 100)
toDoList := make(chan Todo, 100)
toDoList <- Todo{url, depth}
crawling := 0
for {
select {
case todo := <-toDoList:
if todo.depth > 0 && !visited[todo.url] {
crawling++
visited[todo.url] = true
go crawl(todo, fetcher, toDoList, doneCrawling)
}
case <-doneCrawling:
crawling--
default:
if os.Args[1]=="ok" { // *
fmt.Print("")
}
if crawling == 0 {
goto END
}
}
}
END:
return
}
func main() {
Crawl("http://golang.org/", 4, fetcher)
}
// fakeFetcher is Fetcher that returns canned results.
type fakeFetcher map[string]*fakeResult
type fakeResult struct {
body string
urls []string
}
func (f *fakeFetcher) Fetch(url string) (string, []string, error) {
if res, ok := (*f)[url]; ok {
return res.body, res.urls, nil
}
return "", nil, fmt.Errorf("not found: %s", url)
}
// fetcher is a populated fakeFetcher.
var fetcher = &fakeFetcher{
"http://golang.org/": &fakeResult{
"The Go Programming Language",
[]string{
"http://golang.org/pkg/",
"http://golang.org/cmd/",
},
},
"http://golang.org/pkg/": &fakeResult{
"Packages",
[]string{
"http://golang.org/",
"http://golang.org/cmd/",
"http://golang.org/pkg/fmt/",
"http://golang.org/pkg/os/",
},
},
"http://golang.org/pkg/fmt/": &fakeResult{
"Package fmt",
[]string{
"http://golang.org/",
"http://golang.org/pkg/",
},
},
"http://golang.org/pkg/os/": &fakeResult{
"Package os",
[]string{
"http://golang.org/",
"http://golang.org/pkg/",
},
},
}
Putting a default statement in your select changes the way select works. Without a default statement select will block waiting for any messages on the channels. With a default statement select will run the default statement every time there is nothing to read from the channels. In your code I think this makes an infinite loop. Putting the fmt.Print statement in is allowing the scheduler to schedule other goroutines.
If you change your code like this then it works properly, using select in a non blocking way which allows the other goroutines to run properly.
for {
select {
case todo := <-toDoList:
if todo.depth > 0 && !visited[todo.url] {
crawling++
visited[todo.url] = true
go crawl(todo, fetcher, toDoList, doneCrawling)
}
case <-doneCrawling:
crawling--
}
if crawling == 0 {
break
}
}
You can make your original code work if you use GOMAXPROCS=2 which is another hint that the scheduler is busy in an infinite loop.
Note that goroutines are co-operatively scheduled. What I don't fully understand about your problem is that select is a point where the goroutine should yield - I hope someone else can explain why it isn't in your example.
You have 100% CPU load because almost all times the default case will be executed, resulting effectively in an infinite loop because it's executed over and over again. In this situation the Go scheduler does not hand control to another goroutine, by design. So any other goroutine will never have the opportunity to set crawling != 0 and you have your infinite loop.
In my opinion you should remove the default case and instead create another channel if you want to play with the select statement.
Otherwise the runtime package helps you to go the dirty way:
runtime.GOMAXPROCS(2) will work (or export GOMAXPROCS=2), this way you will have more than one OS thread of execution
call runtime.Gosched() inside Crawl from time to time. Eventhough CPU load is 100%, this will explicitely pass control to another Goroutine.
Edit: Yes, and the reason why fmt.Printf makes a difference: because it explicitely passes control to some syscall stuff... ;)