Golang sharing configurations between packages

MrSSS16 picture MrSSS16 · Apr 10, 2016 · Viewed 8.9k times · Source

So I just started learning the Go programming language and have spent hours on end looking at examples, references and so forth. As most of you would agree there is no better way to learn a language than to dive in and make something, which is what I am attempting to do at the moment. I am building a Restful web service. I have managed to get the basics running as well as inserting into db, registering routes etc. However for the past two days I have been struggling to implement application configurations/properties. It could just be that since I'm newbie my Go project architecture is all wrong hence why I am having such difficulty with this. Without further a due here is my project structure

src
   server
      database
         dbaccess.go
         dbcomm.go
      handling
         handler.go
         handlercomm.go
      models
         config.go
         response.go
         user.go
      routing
         routes.go
      main.go

Here is my config.go

package models

import (
   "io/ioutil"
   "encoding/json"
)

type Config struct  {
   Db map[string]string `json:"db"`
   Server map[string]string `json:"server"`
}


func NewConfig(fname string) *Config{
   data,err := ioutil.ReadFile(fname)
   if err != nil{
      panic(err)
   }
   config := Config{}
   err = json.Unmarshal(data,&config)
   if err != nil {
   panic(err)
}
return config

This is my main

func main(){
    args := os.Args[1:]
    if len(args) == 0{
       fmt.Println("********************\nMust specify a config file   in args\n********************")
    os.Exit(1)
   }

   config := models.NewConfig(args[0])
   port := config.Server["PORT"]

   router := routing.NewRouter()
   fmt.Printf(  "-------------------------------------------------\n"+
        "Listening and Serving on Port %s\n"+
        "-------------------------------------------------",port)

   log.Fatal(http.ListenAndServe(":"+port,router))
 }

And finally this is where my routes get mapped

type Route struct {
   Name string
   Method string
   Pattern string
   HandlerFunc http.HandlerFunc
}

var routes = []Route{
   Route{
    "signup",
    "POST",
    "/signup",
    handling.PostSignUpUser,
   },

   Route{
    "authenticate",
    "POST",
    "/login",
    handling.PostLogin,
   },
}

func NewRouter() *mux.Router{
 router :=  mux.NewRouter().StrictSlash(true)
 for _,route := range routes{       
    router.Methods(route.Method)
          .Path(route.Pattern)
          .Name(route.Name)
          .Handler(route.HandlerFunc)
}

return router
}

So as you can see in my Main I initialise the relevant configurations from a file which is fine. But the issue is how would I go about using that same config object from main in the database package,since I will need to set Host,Ports etc ? I could parse the file again but I would prefer if I could share that one object from the start. Please point me in the right direction

Answer

Anfernee picture Anfernee · Apr 18, 2016

What I would suggest is declaring a global variable in config.go and initialize it using the init() function. That way, you know that the variable will always be initialized when any package imports it. Here's some code:

package models

import (
   "io/ioutil"
   "encoding/json"
)


var (

    Configuration Config 
)


init() {

    args := os.Args[1:]
    if len(args) == 0{
       fmt.Println("********************\nMust specify a config file   in args\n********************")
       os.Exit(1)
   }

   Configuration = NewConfig(args[0]) // configuration initialized here
}

type Config struct  {
   Db map[string]string `json:"db"`
   Server map[string]string `json:"server"`
}


func NewConfig(fname string) *Config{
   data,err := ioutil.ReadFile(fname)
   if err != nil{
      panic(err)
   }
   config := Config{}
   err = json.Unmarshal(data,&config)
   if err != nil {
      panic(err)
   }
   return config
}

var() is going to run before init(), but init() will run before the code in the package importing it. So if main.go imports the models package, then init() in models will run before any code inside main.go and thus the variable Configuration will be initialized before it is used.

Effective Go explanation of init()