Getting a use of package without selector error - go

I'm using this config library called Viper
In my main I have this:
viper.SetConfigName("development")
viper.AddConfigPath("config/settings/")
err := viper.ReadInConfig()
if err != nil {
fmt.Println("viper config read error %v", err)
}
I then have a struct that takes a viper as parameter:
type MyConfig struct {
v *viper.Viper
}
In my main I have a function that returns this MyConfig like:
func NewMyConfig(v *viper.Viper) *MyConfig {
return &MyConfig{v: v}
}
I am getting this error:
./main.go:55: use of package viper without selector
Not sure what I should be doing?

When you import a package like
import "github.com/spf13/viper"
the package name (which is viper in this case) will be available to you as a new identifier. You may use this identifier to construct qualified identifiers to refer to exported identifiers of the package (identifiers that start wtih an uppercase letter).
The package name itself cannot be used by itself. The line that gives you error:
myConfig = NewMyConfig(&viper)
You used package name viper without specifying what exported identifier you want to refer to from the package.
You want to use your NewMyConfig() function to obtain a pointer to a new value of your MyConfig struct. Your NewMyConfig() function expects a value of *viper.Viper. Since the viper.Viper struct contains unexported fields, you can just create it like &viper.Viper{}, but the viper package exports a function viper.New() which can be used to obtain a pointer to a new, initialized viper.Viper value. You may use it like:
vp := viper.New()
myConfig = NewMyConfig(vp)
Note that the viper package declares an internal, global, unexported viper.Viper "instance". There are many exported functions that match methods of the viper.Viper type. These "matching" functions work on the global, unexported viper.Viper instance. So you may choose to use all the exported global functions of the viper package, or create your own Viper instance and then keep using its methods afterwards.

Related

Go package Global Variable usage within imports

I am creating a package that will be called from other services that I have in Go. If a service imports this package, and this package has global variables, will the component store the global variables in its' own memory?
So if my package looks something like this,
var global1 = ""
var global2 = ""
var global3 = ""
var global4 = ""
func setAllGlobalValues() error {
// some logic that checks if globals are nil
// if not setting it to a value after some computation.
// returns nil or an actual error.
}
func DoesSomethingUsingGlobalVars() (bool, error) {
// sets and uses the global vars.
// Does some sort of computation and returns a bool, nil or nil,error
}
Then in the service I would import this package and use the doesSomethingUsingGlobalVars function.
Will the component using this package store the global variables in its own memory? I can't really test it now with my services with the way things are set up so I'm just curious if anyone knows.
Essentially, will this work or will the global vars be nil each and every-time anything is called from a service that imports this package?
Thanks in advance all!
When your program imports this package, the globals will be set when you call SetAllGlobalValues(), and later, when you call DoesSomethingUsingGlobalVars(), those values will already be set. Note that the first letter of those function names must be capitalized so that they are exported and available for use by other packages. If the variables are not exported, as shown in your code snippet, you will not be able to access them directly from the caller.
It seems as if you are trying to reinvent objects. Instead of your code, do
something like this:
package some
import "fmt"
type Thing struct {
One string
Two string
Three string
Four string
}
func NewThing() Thing {
return Thing{"One", "Two", "Three", "Four"}
}
func (t Thing) Print() {
fmt.Println(t.One, t.Two, t.Three, t.Four)
}
Then, the "global variables" are only calculated once, when you call NewThing.

Using return value of type from unimported package

Given the following three go packages (I abbreviated the import paths for imp and Tdef for privacy reasons)
package main
import (
"imp"
"fmt"
)
func main() {
T := imp.NewT()
fmt.Printf("T.X = %d\n", T.X)
fmt.Printf("T has type %T\n", T)
}
package imp
import (
"Tdef"
)
func NewT() Tdef.T {
return Tdef.T{0,0}
}
package Tdef
type T struct {
X int
Y int
}
func (T T) GetX() int {
return T.X
}
main() produces the output
T.X = 0
T has type Tdef.T
This means that I can use a variable of type T in a package where the definition of T is not visible by defining it using a return value, and I can use its exported fields (and methods, not shown here).
I find that a bit surprising and did not find any information on this in the spec. Then again, I don't have much programming experience -- is this to be expected, and is it bad practice to make use of it (I don't have a real-life example right now, mind you)?
You are allowed to use values originating from anywhere, but without the import declaration you can't refer to its type for example. Yes, because referring to identifiers requires a qualified identifier which has the form of:
QualifiedIdent = PackageName "." identifier .
You haven't imported Tdef in your main package, so you can't refer to the type Tdef.T identifier (type), but you didn't attempt it so all is good.
Spec: Import declarations:
An import declaration states that the source file containing the declaration depends on functionality of the imported package (§Program initialization and execution) and enables access to exported identifiers of that package. The import names an identifier (PackageName) to be used for access and an ImportPath that specifies the package to be imported.
You used a short variable declaration and so you did not specify the variable's type (which is perfectly OK), so you didn't need to refer to its type:
T := imp.NewT()
T's type will be inferred from the right-hand side expression, which will be Tdef.T of course, but your source code contains no reference to this type. Obviously the following line would give a compile-time error without importing Tdef:
var T Tdef.T = imp.NewT()
You can even return values of unexported types from an exported function, and the package importing it can as well use it, e.g. it can print it, call exported methods and access exported fields of the value of the unexported type (although returning values of unexported types is considered bad practice, golint gives a warning like: "exported func XXX returns unexported type pkgname.typename, which can be annoying to use").

Creating empty Kubernetes resource struct when knowing the resource kind/type only (in golang)

How can I create an empty struct of a certain Kubernetes resource when the resource kind/type is given as string ?
I.e.
var object *runtime.Object
object = factory.NewResourceFromKind("pod")
and object should then contain &apiv1.Pod{} ?
Knowing the Kind is not enough, however a combination of API Group + API Version + object Kind (GroupVersionKind) would allow you to use the information contained in the default scheme.Scheme variable to generate a new object.
package main
import (
"fmt"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes/scheme"
)
func main() {
// apiVersion's syntax is "group/version" for non-core API groups
// e.g. "apps/v1"
podGvk := schema.FromAPIVersionAndKind("v1", "Pod")
obj, err := scheme.Scheme.New(podGvk) // error handling omitted
fmt.Printf("%T", obj)
/* prints '*v1.Pod' */
}
See godoc for func (*Scheme) New
There is no pretty way to do this.
This would require two steps:
Building a type map: map[string]reflect.Type
You could either write your own code to go through each k8s api type and insert into a map, as in this answer: is there a way to create an instance of a struct from a string?
Or, you could use an extremely non-portable method to extract types from binaries, which allows you to avoid knowing which types to add to the map:
How to discover all package types at runtime?
Use the map to instantiate. Lookup myType from a string using the map, then:
reflect.New(myType).Elem()
where myType is the instance of reflect.Type

How to correctly import method from another package

I have the following simple project:
myapp/
|- mainlib/main.go
|- tables/sometable.go
In sometable.go I have:
package tables
import (
"fmt"
)
type TableName string
func (table TableName) GetDataFromDataSource() string {
return "getdatafromdatasource"
}
Those are methods, and now I want to use them in main.go:
package main
import t myapp/tables
type tableName t.TableName // and such "attempts"
I've tried:
t.GetDataFromDataSource()
t.tableName("string").GetDataFromDataSource()
tableName("string").GetDataFromDataSource()
and similar combinations and compiler says "undefined reference" still...
So, my question is: how to correctly use method from another package in such context? Of course my methods starts with captial letters and are exported. If I try to use simple function with same name (not method), then it works correctly...
This has nothing to do with packages, but with alias declaration vs type definition.
Let's take a simple example:
type A string
func (a A) Print() {
fmt.Println(a)
}
type B A
type C = A
func main() {
A("A").Print()
B("B").Print()
C("C").Print()
}
This fails to compile on line B("B").Print() with:
B("B").Print undefined (type B has no field or method Print)
The spec clearly spells out the difference in the type declarations section
The line type B A is a type definition:
A type definition creates a new, distinct type with the same
underlying type and operations as the given type, and binds an
identifier to it.
While the line type C = A is an alias declaration:
An alias declaration binds an identifier to the given type.
The former is a new type, so the receiver type A for method Print() will not match the type B.
The latter is just a name pointing to type A.
You'll want to change your code to the following, using type aliases:
import t myapp/tables
type tableName = t.TableName
tableName("string").GetDataFromDataSource()
And a few notes:
t.GetDataFromDataSource() can't work, it's a method and needs a receiver
t.tableName("string").GetDataFromDataSource() won't work, t.tableName is not a thing, or if it is, it's not exported
you can't talk about importing methods, only packages are imported. A method is either exported or not, but it's the package you import

Viper, cannot get the value initialized outside the functions

I have a problem using a viper, I have assigned variable by viper method, so when I try to get value inside any function I have a null value. Does anybody have any idea why does it happen so? Any other variables initialization works fine, but not viper GetString method.
Structure:
main.go
package main
import (
"project/Model"
"github.com/spf13/viper"
...
)
func main() {
//Config handling
viper.SetConfigName("main")
viper.AddConfigPath("/config/")
err = viper.ReadInConfig()
...
}
Package Model
package Model
import ("github.com/spf13/viper"
...
)
var sqlhost = viper.GetString("db.host")
func foo() {
log.Println(sqlhost)
}
I suspect that doing package variable initialization as you show will lead to the viper configuration not being complete before performing the GetString(), and therefore the zero (empty) string value is what is returned. Without seeing how you initialize your viper config, I'm guessing that it's not in an init() function, but is in a main() function that hasn't run yet when the package variable is assigned.
You should probably retrieve the viper.GetString("db.host") inside the highest level function that needs it rather than during package initialization.

Resources