plugin and package scoped global variables - go

I have a Go package to manage configuration. The package has a non-exported variable initialized in an init function that holds the configuration data. The user interacts with the configuration through exported functions that internally access the global variable. Something like this
pakage config
var gConfig ...
func init() {
gConfig = ...
}
func Value(name string) (string, error) {
return gConfig.value(name)
}
I’m considering the use of plugins and explore the impact on my config package.
If the plugin imports the config package and calls some of its exported functions, what gConfig variable will be used? Will the plugin have its own internal instance of config with its own gConfig variable initialized when the plugin is loaded, or will the plugin be linked dynamically at load time to use the main program gConfig variable initialized at program startup?

as per documentation
Package plugin implements loading and symbol resolution of Go plugins.
When a plugin is first opened, the init functions of all packages not
already part of the program are called. The main function is not run.
A plugin is only initialized once, and cannot be closed.
Also, you cannot import the same plugin twice.
Will the plugin have its own internal instance of config with its own gConfig variable initialized when the plugin is loaded
the plugin will have its own variable within its scope.
If the plugin imports the config package and calls some of its exported functions, what gConfig variable will be used ?
the variable defined within the package, as you demonstrated.
To check that out, write a small demonstration. Go is very straightforward and efficient, doing it takes very little time, see.
$ tree .
.
├── main.go
├── plug
│   └── plugin.go
└── plugin.so
1 directory, 3 files
// $ cat plug/plugin.go
package main
var pkgGlobal = map[string]string{}
func Set(k, v string) {
pkgGlobal[k] = v
}
func Get(k string) string {
return pkgGlobal[k]
}
// $ cat main.go
package main
import (
"fmt"
"plugin"
)
func main() {
p, err := plugin.Open("plugin.so")
if err != nil {
panic(err)
}
var get func(string) string
{
x, err := p.Lookup("Get")
if err != nil {
panic(err)
}
get = x.(func(string) string)
}
var set func(string, string)
{
x, err := p.Lookup("Set")
if err != nil {
panic(err)
}
set = x.(func(string, string))
}
set("tomate", "rouge")
fmt.Println(get("tomate"))
fmt.Println(get("notomate"))
}
build and run
$ go build -buildmode=plugin -o plugin.so plug/plugin.go
$ go run main.go
rouge

Related

passing map between packages in golang

In golang, I know that map is passed between functions in the form of reference, but I encountered a strange situation today. The running result of the code is not what I imagined. I simplified it into the following lines of code.
.
├── go.mod
├── main.go
├── packageA
│   └── a.go
└── packageB
└── b.go
main.go file
package main
import (
"gostudy/packageA"
"gostudy/packageB"
)
func main() {
packageB.UseMap(packageA.M, packageA.InitMap)
}
a.go
package packageA
var M map[string]string
func InitMap() {
M = make(map[string]string)
M["hello"] = "go"
}
b.go
package packageB
import "fmt"
func UseMap(m map[string]string, callback func()) {
callback()
fmt.Println(m)
}
As you can see, there is only one variable globally declared in the a.go file. I thought the above program should output map[hello:go], but it actually outputs an empty map[]. I'm very confused about this and hope to get an answer.
You're passing the old value of the map as a parameter, before you invoke the function to replace it with a new version of the map.
Let's say packageA.M contains the value map[string]string{"foo": "bar"}. The main() function reads the variable and gets a reference to this map, and passes it and the function to packageB.UseMap().
Inside packageB.UseMap(), your code calls packageA.InitMap() via the callback. This does not modify the existing map; instead, it creates a new map, assigns it to the global variable, and populates it. Anything that had a copy of the old map is unaffected, and the code you show doesn't re-read the value of packageA.M.
I'd recommend dispensing with the global variable entirely: it can make the code hard to test and there are potential problems once you start using goroutines. Just have your setup function return the new map.
package packageA
func InitMap() map[string]string {
return map[string]string{"hello": "go"}
}
package packageB
func UseMap(callback func() map[string]string) {
m := callback()
fmt.Println(m)
}
package main
import "packageA"
import "packageB"
func main() {
packageB.UseMap(packageA.InitMap)
}
Just as a side note to the accepted anwer, if you take a look at this:
// ...
import (
"reflect"
"fmt"
)
// ... other functions
// I defined all of the functions in a single paackage, so I can access them both here
func UseMap(m map[string]string, callback func()) {
fmt.Println(reflect.ValueOf(m).Pointer() == reflect.ValueOf(M).Pointer()) // prints true, they have the same reference
callback()
// inside callback, M global variable takes a whole new reference
// when "M = make(...)"
// and then =>
fmt.Println(reflect.ValueOf(m).Pointer() == reflect.ValueOf(M).Pointer()) // prints false
}
If you want to avoid this without changing your apis, you can do this in your package A:
package packageA
var M map[string]string = make(map[string]string)
func InitMap() {
M["hello"] = "go"
}

How to interact with Telegraf using Go external plugin?

I have a dummy GO plugin, using that, i want send data to telegraf. But, i'm not able to find any way to send data from the plugin to telegraf.
this external Go plugin looks like below
package main
import (
"fmt"
"time"
)
type greeting string
type n int
func (g greeting) Greet() {
for i := 0; i < 10; i++ {
timer1 := time.NewTimer(2 * time.Second)
<-timer1.C
fmt.Println("value ", i)
sum := 0
sum += i * 100
fmt.Println(sum)
}
}
// exported
var Greeter greeting
And the main file looks like
import (
"fmt"
"os"
"plugin"
)
type Greeter interface {
Greet()
}
func main() {
var mod string
mod = "./eng/eng.so"
// load module
// 1. open the so file to load the symbols
plug, err := plugin.Open(mod)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// 2. look up a symbol (an exported function or variable)
// in this case, variable Greeter
symGreeter, err := plug.Lookup("Greeter")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// 3. Assert that loaded symbol is of a desired type
// in this case interface type Greeter (defined above)
var greeter Greeter
greeter, ok := symGreeter.(Greeter)
if !ok {
fmt.Println("unexpected type from module symbol")
os.Exit(1)
}
// 4. use the module
greeter.Greet()
}
Can anyone help me find a way or a direction on how to make interaction between GO plugin and telegraf work. Any heads up is appreciated.
Currently, Telegraf does not support the use of external plugins.
An issue was made and closed regarding this requested feature.
If you were planning on writing code in Golang I would suggest creating a custom Telegraf plugin. Telegraf plugins do not use go external plugin package instead they are built with Telegraf itself. Your plugin must satisfy the specific interface based on the type of plugin you want to create. Based on your question I will assume that you desire to create an input plugin, Telegraf has examples for creating each type of plugin in their source code.

Importing local package in another folder

I keep getting the following error and I have been trying out some of the other suggestions in other Stack Overflow threads.
./main.go:15:13: undefined: checkEnv
Folder Structure:
├── README.md
├── main.go
└── validate
└── args.go
$GOPATH
/Users/myusername/go
main.go
package main
import (
"fmt"
"log"
"os"
"projectName/validate"
"gopkg.in/urfave/cli.v1"
)
func main() {
app := cli.NewApp()
app.Name = "thirsty"
app.Action = func(c *cli.Context) error {
result := checkEnv(c.Args().Get(0))
fmt.Println(result)
return nil
}
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
}
validate/args.go
package validate
import "strings"
func checkEnv(environment string) bool {
env := strings.ToLower(environment)
return env != "qa" && env != "dev"
}
My project is in the src directory of my $GOPATH. If this is not the proper way to do code splitting, is there a generally used convention to follow?
Any feedback appreciated.
There are two problems here.
The checkEnv method is not exported [in validate/args.go]. ie it is usable only inside the same package and not other packages To export just capitalize the first letter of the method CheckEnv. Now CheckEnv can be used from other packages
While calling a method from other package, the syntax should be packagename.ExportedMethod(params...). so in your code [in main.go] it should be result := validate.CheckEnv(c.Args().Get(0))

How to get flag arguments in other package

I write program in go, and I have a problem with get variable form command line in other own package using package flag .
flag.Parse()
Main problem is in Configuration package, because I use her many times in many places, so I want to avoid pass pionter to her, and I decided create as an independent module. Now I have problem with get flag to pathFile with configuration. My code looks like below
I run my program with parameter
program -config=/my/path/config.cfg
and main function
func main() {
flag.Parse()
// some next operation but not with configuration Type
// the type of configuration is use in other object
//and in other place in my program
// here I only want to parse plag from
// cmd lines
}
package configuration and file configuration.go so configuration/configuration.go
var (
configPath string
C *configuration
)
func init() {
flag.StringVar(&configPath,
"config",
"/defaultpath/config.gcfg",
"Path to configuration file")
C = &configuration{}
if err := gcfg.ReadFileInto(C, configPath); err != nil {
fmt.Printf("Cannot read configuration file. Program was Terminated error:%s", err)
os.Exit(1)
}
}
type configuration struct {
Queue struct {
User string
Host string
Port string
}
After compilation I get error: Cannot open log file error:open
/defaultpath/config.gcfg: no such file or directory. Program was
terminated.
As look above Parsed flags in main are not visible in package configuration, because configuration module try start with default path nor form arguments cmd line. that's why this must be finish error, because this path dosen't exist in my local machine
Question on end is:
How export flag variable from main package to other own package ?
The init() in configuration is run before anything in main is executed, so there's no way the effect of flag.Parse() could be seen. You also need to declare the flag variables before you call flag.Parse()
You can't export anything from main, both because main can't be imported, and because you can't have a circular dependency between the packages. Have the main package call a function from the configuration package to get the behavior you want.
You could add an exported Init() function in configuration to handle the flag options:
func Init() {
flag.StringVar(&configPath,
"config",
"/defaultpath/config.gcfg",
"Path to configuration file")
flag.Parse()
C = &configuration{}
if err := gcfg.ReadFileInto(C, configPath); err != nil {
fmt.Printf("Cannot read configuration file. Program was Terminated error:%s", err)
os.Exit(1)
}
}
and have the main function start with
func main() {
configuration.Init()
}

exported methods not available in same package

I have a small go lang project which in the main.go file has a few handlers that refer to session related methods in a session.go file. Both have package main at the top of the file. The functions in the session.go file all begin with an uppercase letter (i.e. they are public/exported methods). Yet when I run the main.go file, it says the methods located in session.go and called from main.go are undefined. Why is that, how to fix it.
I am running the project like go run main.go
main.go
func logout(w http.ResponseWriter, r *http.Request) {
ClearSession(w, r)
....
}
session.go
func ClearSession(w http.ResponseWriter, r *http.Request) {
}
As #ptd said, the command needs all the files named.
I prefer use another package:
/ main.go
|_session/
|_session.go
|_validations.go
|_errors.go
So, you can organize your code and simplify your named files.
e.g.:
file: main.go
package main
import "session"
func main() {
var validator session.Validator
var session session.Session
...
if session.IsValid() == false {
// return session.InvalidSession
fmt.Printf("ERROR: %v", session.InvalidSession)
}
}
file: errors.go
import "errors"
var (
InvalidSession = errors.New("[Your error message]"
)
Then you can use:
go run main.go

Resources