Find location of Golang application - go

I have a Golang application which is being deployed into a container via Kubernetes. Within the service I would like to read a file which is nested within the application.
I'm struggling to find out how to set the current working directory to the location of the application.
Currently the application panics with an error due to the relative paths not being aligned:
open /workspace/source/package/file.csv: no such file or directory
I've tried using the following code and appending the file name:
var (
_, b, _, _ = runtime.Caller(0)
basepath = filepath.Dir(b)
)
However this still doesn't work. Is there a way to find the applications current location and then append the filename?
Thanks for help!

Use os.Executable to find the path to the executable.
b, err := os.Executable()
if err != nil {
log.Fatal(err)
}
basepath := filepath.Dir(b)

Can't you just cd before executing the application? Then you can use relative paths.

Related

How to get all files recursively by extension using filepath.Glob()?

I am trying to parse all the files with an extension with the text/template parser, which uses the filepath.Glob() function under the hood to match files.
package main
//go:embed templates
var templates embed.FS
func main() {
folder, _ := fs.Sub(t, "templates")
tpl, err := template.ParseFS(folder, "**/*.gohtml")
// ...
}
As far as I can tell, there isn't a specification defining the behaviour of glob patterns, however this pattern in Node.js will match all files in a directory with the .gohtml extension.
It doesn't seem to match all files in Go, however manually adding folders selects the correct files.
tpl, err := template.ParseFS(folder, "*.gohtml", "partials/*.gohtml")
I tried looking at the documentation on the Go website but struggled to find resources on the pattern matching rules.
I'm hoping it's as simple as getting the right variation of *s, /s and I just haven't found it

Golang os.Rename(<fromDir>,<toDir>) not working in Windows

Using Go - lang, according to the documentation, the os.Rename should be able to rename either a file or directory on any operating system.
On Linux it works as it should, pass either a file or directory into it and the file or directory are moved.
On windows i recieve an 'Access is denied' Error when trying to pass a folder.
It works 100% for files.
example:
source = c:\sourcefolder
destination = c:\destinationfolder
source contains:
C:\sourcefolder\file1.xml
C:\sourcefolder\file2.xml
C:\sourcefolder\foldername1
C:\sourcefolder\foldername1\file3.xml
C:\sourcefolder\foldername2
C:\sourcefolder\foldername2\file4.xml
both file1.xml and file2.xml will successfully copy to c:\destination.
But the folders and files within the folders crash out with access denied
The script is pretty simple:
source := "C:\\sourcefolder"
destination := "C:\\destinationfolder"
pathSeperator := "\\"
files, err := ioutil.ReadDir(source)
if err != nil {
fmt.Println("Move command execution error: ", err)
}
for _, f := range files {
fmt.Println(f.Name())
fmt.Println(f.Mode())
err := os.Rename(source+pathSeperator+f.Name(), destination+pathSeperator+f.Name())
if err != nil {
fmt.Println("Move command execution error: ", err)
panic(err)
}
}
Having searched stackoverflow and golang's resources, i found the issue listed in 2016 that reported this fault and according to the issue it was fixed, but i am unable to get this to work. Nowhere else that i can find lists this issue go golang.
checking the f.Mode for access, i get drwxrwxrwx and have complete access to all the files and directories.
Any help with this would be great, racking my mind. Thank you.
Quoted from comment. solved my issue.
Found the cause of the fault to be, if a windows explorer window is
open and has ANY visibility of the folders being moved (i.e. in the
tree on the left or right-pane) then access is denied as it can not
move the folders. If i minimize all the tree's so that the
source\destination folders are not visible and select a different sub
folder in windows explorer then the os.Rename works as it should,
moving all content from A to B really quick (as per linux)
I had the same issue with copying files within the same folder. The following solution works just fine (without closing or minimizing windows):
// read original file
origFile, _:= os.ReadFile(filePath)
// create new file with a different name
newFile, _ := os.Create(filePath + ".new")
// print data from original file to new file.
fmt.Fprintf(newFile, "%s", string(origFile))

Read file from relative path with different callers

I'm trying to read from a file in my project's directory.
My problem is, that depending on the caller, the path changes. The caller changes, because I want to unit test this code and the caller is not Main.go anymore.
This is what my project structure looks like:
The code where I try to access specialChars.txt from looks like this:
func RemoveSpecialChars(word string) string {
file, err := ioutil.ReadFile("wordlists/specialChars.txt")
[...]
}
This code works for the start from Main.go but not for the start from CleanupUtil_test.go. To get it working from the test I would need file, err := ioutil.ReadFile("../wordlists/specialChars.txt")
I found answers like this one: https://stackoverflow.com/a/32163888/2837489
_, filename, _, ok := runtime.Caller(0) which is obviously also dependent on the caller.
Is it possible to get the projects root path independent of the calling function?
Or is my code design wrong? Should I pass the file path into the function?
Starting from Go 1.16, you can use the embed package. This allows you to embed the files in the running go program. It comes with the caveat that the referenced directory needs to exist at or below the embedding file. In your case, the structure would look as follows:
-- main.go
-- cleanup
-- wordlist
\- specialChars.txt
CleanupUtil.go
CleanupUtil_test.go
You can reference the file using a go directive
// CleanupUtil.go
package cleanup
import (
"embed"
)
//go:embed wordlists/specialChars.txt
var content embed.FS
func RemoveSpecialChars(word string) string {
file, err := content.ReadFile("wordlists/specialChars.txt")
[...]
}
This program will run successfully regardless of where the program is executed. You should be able to reference this code in both your main.go file and your CleanupUtil_test.go file.
Pass in the filepath as a parameter to the function (as indicated in your last question).
More details:
The relative path "wordlists/specialChars.txt" is in fact not dependent on where the source file is located (such as Main.go or CleanupUtil_test.go), but where you execute it from. So you could run your tests from your root directory and then it would actually work. In short, the current working directory is relevant.
Still, I'd recommend specifying the path, because that makes your function more reusable and universal.
Maybe you don't even need to put this information into a file, but can simply have a string containing those chars. In this case you could also check if https://golang.org/pkg/regexp/#Regexp.ReplaceAll already covers your use case.

Binary and additional files

I'm trying to package my go application binary which is accessible by a web interface running on localhost:8080 so that when downloaded it's able to find the JS(front-end) files in the folder where the file is ran but i can't seem to make it work.
I've been doing something like this :
pwd, err := os.Getwd()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
Then trying to use the working directory of the binary to access the files inside it but that doesn't seem to work.
The binary is located at :
/Users/admin/Desktop/testappfolder
but when i run the program with just :
pwd, err := os.Getwd()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println(pwd)
I end up getting /Users/admin as the working directory instead.
I wondering :
Where i'm going wrong ?
Is this has something to do with the Gopath ?
Am i going at it the right way regarding distributing the app as a "zip" and having file path setup directly inside my program relative to the working directory ? or is it that logic that's wrong ?
os.Getwd is going to correspond to where you start your binary from not where the binary is located.
To make for a more robust solution I would pass in the location of the files directory using a flag or using a config value.

File path in golang

I have a project with next structure:
|_main.go
|_config
|_config.go
|_config_test.go
|_config.json
I'm having next code line in config.go:
file, _ := os.Open("config/config.json")
When I'm executing method contained this code line from main.go all is working. But when I'm trying to execute this method from config_test.go it produces error:
open config/config.json: no such file or directory
As I understood it is a working directory issue because I'm launching same code with relative path from different directories. How can I fix this problem without using full path in config.go?
Relative paths are always resolved basis your current directory. Hence it's better to avoid relative paths.
Use command line flags or a configuration management tool (better approach) like Viper
Also according to The Twelve-Factor App your config files should be outside your project.
Eg usage with Viper:
import "github.com/spf13/viper"
func init() {
viper.SetConfigName("config")
// Config files are stored here; multiple locations can be added
viper.AddConfigPath("$HOME/configs")
errViper := viper.ReadInConfig()
if errViper != nil {
panic(errViper)
}
// Get values from config.json
val := viper.GetString("some_key")
// Use the value
}

Resources