Record and Persist API call details in KrakenD for API monetization - api-gateway

We are trying out KrakenD as a primary API gateway for our backend services. The plugins available are only for response manipulation, we want to go a step ahead and start recording all the API calls and persist them in a database. I was checking the example of having a custom HTTPStatusHandler, but that mainly caters to handling the status codes and as a best case we can only return the status codes from our backends as it is. Is there any working example of this scenario that we are trying? We are already using a custom middleware to grant API access based on our role based access rules.
Other commercial solutions offer API monetization out of the box, but we want to stick with KrakenD for performance reasons.

KrakenD plugins are not only for response manipulation, but a much more powerful solution. There are 4 types of plugins in krakend, what you want to do I think that fits best as an HTTP SERVER plugin.
This type of plugin intercepts all incoming traffic and you can record it in a Redis database.
I have improvised a main.go of the idea, but you should check the plugin guide in the documentation for more details.
Hope this helps illustrating the idea
package main
import (
"context"
"errors"
"fmt"
"html"
"net/http"
)
// HandlerRegisterer is the symbol the plugin loader will try to load. It must implement the Registerer interface
var HandlerRegisterer = registerer("krakend-api-monetization")
type registerer string
var logger Logger = nil
func (registerer) RegisterLogger(v interface{}) {
l, ok := v.(Logger)
if !ok {
return
}
logger = l
logger.Debug(fmt.Sprintf("[PLUGIN: %s] Logger loaded", HandlerRegisterer))
}
func (r registerer) RegisterHandlers(f func(
name string,
handler func(context.Context, map[string]interface{}, http.Handler) (http.Handler, error),
)) {
f(string(r), r.registerHandlers)
}
func (r registerer) registerHandlers(_ context.Context, extra map[string]interface{}, h http.Handler) (http.Handler, error) {
// check the passed configuration and initialize the plugin
name, ok := extra["name"].([]interface{})
if !ok {
return nil, errors.New("wrong config")
}
if v, ok := name[0].(string); !ok || v != string(r) {
return nil, fmt.Errorf("unknown register %s", name)
}
// check the cfg. If the modifier requires some configuration,
// it should be under the name of the plugin. E.g.:
/*
"extra_config":{
"plugin/http-server":{
"name":["krakend-api-monetization"],
"krakend-api-monetization":{
"redis_host": "localhost:6379"
}
}
}
*/
// The config variable contains all the keys you hace defined in the configuration:
config, _ := extra["krakend-api-monetization"].(map[string]interface{})
// The plugin will save activity in this host:
redis_host, _ := config["redis_host"].(string)
logger.Debug(fmt.Sprintf("The plugin is now storing on %s", redis_host))
// return the actual handler wrapping or your custom logic so it can be used as a replacement for the default http handler
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
// TODO: Save in Redis the request
rdb := redis.NewClient(&redis.Options{
Addr: redis_host,
Password: "", // no password set
DB: 0, // use default DB
})
// etc...
logger.Debug("Request saved:", html.EscapeString(req.URL.Path))
}), nil
}
func main() {}
type Logger interface {
Debug(v ...interface{})
Info(v ...interface{})
Warning(v ...interface{})
Error(v ...interface{})
Critical(v ...interface{})
Fatal(v ...interface{})
}

Related

Set GRPC Header Response GO

I'm using grpc in golang and want to make my header like this
I already tried 2 methode. First, i'm using this answer and it works, but the header is adding grpc-metadata- to it's key like this
My question is how to remove that word? and how to generate some values like date,content-length, cache-control etc, do i have to hardcoded it in my code like this?
header := metadata.New(map[string]string{"Access-Control-Allow-Headers": "X-Requested-With,content-type, Accept,Authorization", "Server": "val2"})
grpc.SendHeader(ctx, header)
And the other methode is i'm using http.Responsewritter. this is my code like:
main.go
var (
config *envcfg.Envcfg
logger *logrus.Logger
https http.ResponseWriter
)
func main() {
--------------------
g.Go(func() error { return NewGRPCServer(config, logger, https) })
-------------------
}
func NewGRPCServer(config *envcfg.Envcfg, logger *logrus.Logger, https http.ResponseWriter) error {
// create service handlers each grpc service server
signupSvc, err := signup.New(config, logger, https)
if err != nil {
return err
}
-----------------------------
// register grpc service server
grpcServer := grpc.NewServer()
supb.RegisterSignServer(grpcServer, signupSvc)
-------------------------------------------------------
}
api.go :
type Server struct {
config ConfigStore
logger Logger
https http.ResponseWriter
}
func New(config ConfigStore, logger Logger, https http.ResponseWriter) (*Server, error) {
return &Server{
config: config,
logger: logger,
https: https,
}, nil
}
function.go:
func (s *Server) SplashScreen(ctx context.Context,req *supb.RetrieveRequest, w http.ResponseWriter) (*supb.RetrieveResponse,error){
// do something
}
The problem is in main it said that signupSVC doesn't implement signServer like this:
Cannot use 'signupSvc' (type *Server) as type SignServer Type does not implement 'SignServer' need method: SplashScreen(context.Context, *RetrieveRequest) (*RetrieveResponse, error) have method: SplashScreen(ctx context.Context, req *supb.RetrieveRequest, w http.ResponseWriter) (*supb.RetrieveResponse, error)
where to place the http.Responsewriter ?

Go Gin framework to log in JSON

I am setting up a small API using Go Gin, however I am unable to convince the logger to output JSON. By default it's a key/value string, but I need it as json.
How can I achieve that? I feel it should be easily support but I've been struggling with a custom formatting function where I need to account for various parameters myself.
Alternatively, I also log manually using uber/zap logger but I haven't found a way to replace gin's logger with mine.
Any pointers would be appreciated as the documentation on gin's github wasn't too helpful.
Thanks!
EDIT: to clarify, adding a middlware helps with logging requests, but I'm looking for single point of setting JSON logging for Gin (eg: including logs not related to requests, such as debug / info logs of framework internals)
access logger
https://github.com/sbecker/gin-api-demo/blob/master/middleware/json_logger.go
// JSONLogMiddleware logs a gin HTTP request in JSON format, with some additional custom key/values
func JSONLogMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// Start timer
start := time.Now()
// Process Request
c.Next()
// Stop timer
duration := util.GetDurationInMillseconds(start)
entry := log.WithFields(log.Fields{
"client_ip": util.GetClientIP(c),
"duration": duration,
"method": c.Request.Method,
"path": c.Request.RequestURI,
"status": c.Writer.Status(),
"user_id": util.GetUserID(c),
"referrer": c.Request.Referer(),
"request_id": c.Writer.Header().Get("Request-Id"),
// "api_version": util.ApiVersion,
})
if c.Writer.Status() >= 500 {
entry.Error(c.Errors.String())
} else {
entry.Info("")
}
}
}
debug logger
Looking at the gin source code, it is found that the debug log is output to an io.Writer. Rewriting this object redirects the output to json, similar to the method of processing the output of http.Server.Errorlog.
func debugPrint(format string, values ...interface{}) {
if IsDebugging() {
if !strings.HasSuffix(format, "\n") {
format += "\n"
}
fmt.Fprintf(DefaultWriter, "[GIN-debug] "+format, values...)
}
}
set debug write, this code is no test.
// WriteFunc convert func to io.Writer.
type WriteFunc func([]byte) (int, error)
func (fn WriteFunc) Write(data []byte) (int, error) {
return fn(data)
}
func NewLogrusWrite() io.Writer {
return WriteFunc(func(data []byte) (int, error) {
logrus.Debugf("%s", data)
return 0, nil
})
}
// set gin write to logrus debug.
gin.DefaultWriter = NewLogrusWrite()
get all http.Server Error log.
htt.Server
htt.Server outputs logs to a log.Logger, and creates a log.Logger output by the specified io.Writer to receive the Error log from http.Sever There is no detailed write gin to use custom Sever code, please check the gin documentation.
srv := &http.Server{
// log level is bebug, please create a error level io.Writer
ErrorLog: log.New(NewLogrusWrite(), "", 0),
}
In addition to #audore's answer; His method will change the default output which is stdout at default. We still want to use stdout but if we'd like to change the whole output we should do these steps.
1- Change gin initialization from
r := gin.Default()
to
r := gin.New()
r.Use(gin.Recovery()) // to recover gin automatically
r.Use(jsonLoggerMiddleware()) // we'll define it later
because gin.Default() is using the default Logger() middleware. If we keep using it gin will output twice
2- add the middleware to your application
func jsonLoggerMiddleware() gin.HandlerFunc {
return gin.LoggerWithFormatter(
func(params gin.LogFormatterParams) string {
log := make(map[string]interface{})
log["status_code"] = params.StatusCode
log["path"] = params.Path
log["method"] = params.Method
log["start_time"] = params.TimeStamp.Format("2006/01/02 - 15:04:05")
log["remote_addr"] = params.ClientIP
log["response_time"] = params.Latency.String()
s, _ := json.Marshal(log)
return string(s) + "\n"
},
)
}
and your output will be:
{"method":"GET","path":"/v1/somepath","remote_addr":"::1","response_time":"5.719ms","start_time":"2022/10/03 - 17:26:11","status_code":200}
It'll be too much easy to parse from logging listeners like Loki+Grafana

Using a custom http.ResponseWriter to write cookies based on the response from a proxied request?

My original question here was flagged as a duplicate of this question. I had no luck implementing it and suspect my problem is misunderstood, so with my question closed, I'm starting fresh with a more specific question.
I'm trying to set a cookie based on a response header from within middleware in request that is reverse proxied.
Here's the workflow:
User requests http://example.com/foo/bar
Go app uses ReverseProxy to proxy that request to http://baz.com
baz.com sets a response header X-FOO
Go app modifies response by setting a MYAPPFOO cookie with the value of the X-FOO response header
The cookie is written to the user's browser
It was suggested that a custom http.ResponseWriter will work, but after trying and searching for more information, it is not clear how to approach this.
Since I'm failing to grasp the concept of a custom ResponseWriter for my use case, I'll post code that demonstrates more precisely what I was trying to do at the point I got stuck:
package main
import (
"github.com/gorilla/mux"
"log"
"net/http"
"net/http/httputil"
"net/url"
)
func setCookie(w http.ResponseWriter, name string, value string) {
...
http.SetCookie(w, &cookie)
}
func handler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// setCookie() works here
// but I cannot access w.Header().Get("X-FOO")
next.ServeHTTP(w, r)
// I can access w.Header().Get("X-FOO") here
// but setCookie() does not cookie the user's browser
// If I could do it all in one place, this is what I would do:
if r.Method == "POST" && r.URL.String() == "/login" {
foo := w.Header().Get("X-FOO")
setCookie(w, "MYAPPFOO", foo)
}
})
}
func main() {
r := mux.NewRouter()
r.Use(handler)
proxy := httputil.NewSingleHostReverseProxy("https://baz.example.com/")
r.PathPrefix("/").Handler(proxy)
log.Fatal(http.ListenAndServe(":9001", r))
}
As a side note, I was able to make this work with ReverseProxy.ModifyResponse as recommended in the comments of my last question but I'd really like to achieve this with middleware to keep the code that dynamically creates proxies from config clean. (not in the example code)
From the documentation on http.ResponseWriter methods:
(emphasis added)
Header() http.Header:
Changing the header map after a call to WriteHeader (or Write) has no
effect unless the modified headers are trailers.
WriteHeader(statusCode int):
WriteHeader sends an HTTP response header with the provided status
code.
Write([]byte) (int, error):
If WriteHeader has not yet been called, Write calls
WriteHeader(http.StatusOK) before writing the data.
This should highlight the reason why, you can't set a cookie after the next.ServeHTTP(w, r) call, which is that one of the handlers in the middleware chain executed by that call is calling either WriteHeader or Write directly or indirectly.
So to be able set the cookie after the next.ServeHTTP(w, r) call you need to make sure that none of the handlers in the middleware chain calls WriteHeader or Write on the original http.ResponseWriter instance. One way to do this is to wrap the original instance in a custom http.ResponseWriter implementation that will postpone the writing of the response until after you're done with setting the cookie.
For example something like this:
type responsewriter struct {
w http.ResponseWriter
buf bytes.Buffer
code int
}
func (rw *responsewriter) Header() http.Header {
return rw.w.Header()
}
func (rw *responsewriter) WriteHeader(statusCode int) {
rw.code = statusCode
}
func (rw *responsewriter) Write(data []byte) (int, error) {
return rw.buf.Write(data)
}
func (rw *responsewriter) Done() (int64, error) {
if rw.code > 0 {
rw.w.WriteHeader(rw.code)
}
return io.Copy(rw.w, &rw.buf)
}
And you would use it like this in your middleware:
func handler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rw := &responsewriter{w: w}
next.ServeHTTP(rw, r)
if r.Method == "POST" && r.URL.String() == "/login" {
foo := rw.Header().Get("X-FOO")
setCookie(rw, "MYAPPFOO", foo)
}
if _, err := rw.Done(); err != nil {
log.Println(err)
}
})
}

How to pass arguments to router handlers in Golang using Gin web framework?

I'm using Gin, https://gin-gonic.github.io/gin/, to build a simple RESTful JSON API with Golang.
The routes are setup with something like this:
func testRouteHandler(c *gin.Context) {
// do smth
}
func main() {
router := gin.Default()
router.GET("/test", testRouteHandler)
router.Run(":8080")
}
My question is how can I pass down an argument to the testRouteHandler function? For example a common database connection could be something that one wants to reuse among routes.
Is the best way to have this in a global variable? Or is there some way in Go to pass along an extra variable to the testRouteHandler function? Are there optional arguments for functions in Go?
PS. I'm just getting started in learning Go, so could be something obvious that I'm missing :)
I would avoid stuffing 'application scoped' dependencies (e.g. a DB connection pool) into a request context. Your two 'easiest' options are:
Make it a global. This is OK for smaller projects, and *sql.DB is thread-safe.
Pass it explicitly in a closure so that the return type satisfies gin.HandlerFunc
e.g.
// SomeHandler returns a `func(*gin.Context)` to satisfy Gin's router methods
// db could turn into an 'Env' struct that encapsulates all of your
// app dependencies - e.g. DB, logger, env vars, etc.
func SomeHandler(db *sql.DB) gin.HandlerFunc {
fn := func(c *gin.Context) {
// Your handler code goes in here - e.g.
rows, err := db.Query(...)
c.String(200, results)
}
return gin.HandlerFunc(fn)
}
func main() {
db, err := sql.Open(...)
// handle the error
router := gin.Default()
router.GET("/test", SomeHandler(db))
router.Run(":8080")
}
Using the link i posted on comments, I have created a simple example.
package main
import (
"log"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/mattn/go-sqlite3"
)
// ApiMiddleware will add the db connection to the context
func ApiMiddleware(db gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("databaseConn", db)
c.Next()
}
}
func main() {
r := gin.New()
// In this example, I'll open the db connection here...
// In your code you would probably do it somewhere else
db, err := gorm.Open("sqlite3", "./example.db")
if err != nil {
log.Fatal(err)
}
r.Use(ApiMiddleware(db))
r.GET("/api", func(c *gin.Context) {
// Don't forget type assertion when getting the connection from context.
dbConn, ok := c.MustGet("databaseConn").(gorm.DB)
if !ok {
// handle error here...
}
// do your thing here...
})
r.Run(":8080")
}
This is just a simple POC. But i believe it's a start.
Hope it helps.
Late to the party, so far here is my proposal. Encapsulate methods into the object with private/public vars in it:
package main
import (
"log"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/mattn/go-sqlite3"
)
type HandlerA struct {
Db gorm.DB
}
func (this *HandlerA) Get(c *gin.Context) {
log.Info("[%#f]", this.Db)
// do your thing here...
}
func main() {
r := gin.New()
// Init, should be separate, but it's ok for this sample:
db, err := gorm.Open("sqlite3", "./example.db")
if err != nil {
log.Fatal(err)
}
Obj := new(HandlerA)
Obj.Db = db // Or init inside Object
r := gin.New()
Group := r.Group("api/v1/")
{
Group.GET("/storage", Obj.Get)
}
r.Run(":8080")
}
Handler closures are a good option, but that works best when the argument is used in that handler alone.
If you have route groups, or long handler chains, where the same argument is needed in multiple places, you should set values into the Gin context.
You can use function literals, or named functions that return gin.HandlerFunc to do that in a clean way.
Example injecting configs into a router group:
Middleware package:
func Configs(conf APIV1Config) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("configKey", conf) // key could be an unexported struct to ensure uniqueness
}
}
Router:
conf := APIV1Config{/* some api configs */}
// makes conf available to all routes in this group
g := r.Group("/api/v1", middleware.Configs(conf))
{
// ... routes that all need API V1 configs
}
This is also easily unit-testable. Assuming that you test the single handlers, you can set the necessary values into the mock context:
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Set("configKey", /* mock configs */)
apiV1FooHandler(c)
Now in the case of application-scoped dependencies (db connections, remote clients, ...), I agree that setting these directly into the Gin context is a poor solution.
What you should do then, is to inject providers into the Gin context, using the pattern outlined above:
Middleware package:
// provider could be an interface for easy mocking
func DBProvider(provider database.Provider) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("providerKey", provider)
}
}
Router:
dbProvider := /* init provider with db connection */
r.Use(DBProvider(dbProvider)) // global middleware
// or
g := r.Group("/users", DBProvider(dbProvider)) // users group only
Handler (you can greatly reduce the boilerplate code by putting these context getters in some helper function):
// helper function
func GetDB(c *gin.Context) *sql.DB {
provider := c.MustGet("providerKey").(database.Provider)
return provider.GetConn()
}
func createUserHandler(c *gin.Context) {
db := GetDB(c) // same in all other handlers
// ...
}
I like wildneuro's example but would do a one liner to setup the handler
package main
import (
"log"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/mattn/go-sqlite3"
)
type HandlerA struct {
Db gorm.DB
}
func (this *HandlerA) Get(c *gin.Context) {
log.Info("[%#f]", this.Db)
// do your thing here...
}
func main() {
r := gin.New()
// Init, should be separate, but it's ok for this sample:
db, err := gorm.Open("sqlite3", "./example.db")
if err != nil {
log.Fatal(err)
}
r := gin.New()
Group := r.Group("api/v1/")
{
Group.GET("/storage", (&HandlerA{Db: db}).Get)
}
r.Run(":8080")
}
Let me try to explain in detail so that you won't get confused.
Depending on the incoming route, you want to call a controller function. Lets say your incoming route is /books and your controller is BooksController
Your BooksController will try to fetch the books from the database and returns a response.
Now, you want this a handler within your BooksController so that you can access database.
I would do something like this. Let's assume that you are using dynamoDB and the aws sdk provides *dynamodb.DynamoDB. Depending on your db, change this variable.
Create a struct as below.
type serviceConnection struct {
db *dynamoDB.DynamoDB
// You can have all services declared here
// which you want to use it in your controller
}
In your main function, get the db connection information. Let's say you already have a function initDatabaseConnection which returns a handler to db, something like below.
db := initDatabaseConnection() -> returns *dynamodb.DynamoDB
Set db to a struct variable.
conn := new(serviceConnection)
conn.db = db
Call the gin request method with a receiver handler as below.
r := gin.Default()
r.GET("/books", conn.BooksController)
As you see, the gin handler is a controller method which has your struct instance as a receiver.
Now, create a controller method with serviceConnection struct receiver.
func (conn *serviceConnection) BooksController(c *gin.Context) {
books := getBooks(conn.db)
}
As you see here, you have access to all the serviceConnection struct variables and you can use them in your controller.
Alright, I have given you a simple example. It should work. You can extend it as per your need
func main() {
router := gin.Default()
router.GET("/test/:id/:name", testRouteHandler)
router.Run(":8080")
}
func testRouteHandler(c *gin.Context) {
id := c.Params.ByName("id")
name := c.Params.ByName("name")
}
Now you will have to call your handler as below
http://localhost:8080/test/1/myname

runtime error:Invalid memory Address or nil pointer dereference-golang

I am new to golang, am trying develop a login page with sesions. the code is building successfully but when I run in browser its saying 404 page not found.can any one help for me. Thanks in advance.
Here is my code
// main.go
package main
import (
_ "HarishSession/routers"
"github.com/astaxie/beego"
"fmt"
"net/http"
"html/template"
"strings"
"log"
"github.com/astaxie/beego/session"
"sync"
)
var globalSessions *session.Manager
var provides = make(map[string]Provider)
func sayhelloName(w http.ResponseWriter, r *http.Request) {
r.ParseForm() // parse arguments, you have to call this by yourself
fmt.Println("the information of form is",r.Form) // print form information in server side
fmt.Println("path", r.URL.Path)
fmt.Println("scheme", r.URL.Scheme)
fmt.Println(r.Form["url_long"])
for k, v := range r.Form {
fmt.Println("key:", k)
fmt.Println("val:", strings.Join(v, ""))
}
fmt.Fprintf(w, "Hello astaxie!") // send data to client side
}
type Manager struct {
cookieName string //private cookiename
lock sync.Mutex // protects session
provider Provider
maxlifetime int64
}
type Provider interface {
SessionInit(sid string) (Session, error)
SessionRead(sid string) (Session, error)
SessionDestroy(sid string) error
SessionGC(maxLifeTime int64)
}
type Session interface {
Set(key, value interface{}) error //set session value
Get(key interface{}) interface{} //get session value
Delete(key interface{}) error //delete session value
SessionID() string //back current sessionID
}
func NewManager(provideName, cookieName string, maxlifetime int64) (*Manager, error) {
provider, ok := provides[provideName]
if !ok {
return nil, fmt.Errorf("session: unknown provide %q (forgotten import?)", provideName)
}
return &Manager{provider: provider, cookieName: cookieName, maxlifetime: maxlifetime}, nil
}
func login(w http.ResponseWriter, r *http.Request) {
sess := globalSessions.SessionStart(w,r)
r.ParseForm()
fmt.Println("method:", r.Method)
if r.Method == "GET" {
t, _ := template.ParseFiles("login.tpl")
w.Header().Set("Content-Type", "text/html")
t.Execute(w,sess.Get("username"))
} else {
//logic part of log in
fmt.Println("username:",r.Form["username"])
fmt.Println("password:",r.Form["password"])
http.Redirect(w,r,"/",302)
}
}
func main() {
var globalSessions *session.Manager
http.HandleFunc("/", sayhelloName)
http.HandleFunc("/login", login)
err := http.ListenAndServe(":8080", nil) // set listen port
if err != nil {
log.Fatal("ListenAndServe the error is: ", err)
}
fmt.Println("hello")
beego.Run()
fmt.Println(globalSessions)
}
//router.go
package routers
import (
"HarishSession/controllers"
"github.com/astaxie/beego"
)
func init() {
beego.Router("/", &controllers.MainController{})
beego.Router("/login", &controllers.MainController{})
}
//default.go
package controllers
import (
"github.com/astaxie/beego"
)
type MainController struct {
beego.Controller
}
func (this *MainController) Get() {
this.Data["Website"] = "beego.me"
this.Data["Email"] = "astaxie#gmail.com"
this.TplNames = "index.tpl"
this.TplNames="login.tpl"
}
You have two variables at different scopes, each called globalSessions. One is in your definition in main.go, which is defined at global scope, and another is defined in the main function, and is defined as a local variable to main. These are separate variables. Your code is making this mistake of conflating them.
You can see this by paying closer attention to the stack trace entry:
github.com/astaxie/beego/session.(*Manager).SessionStart(0x0, 0x151e78, 0xc08212 0000, 0xc082021ad0, 0x0, 0x0)
as this points to globalSessions being uninitialized due to being nil. After that, troubleshooting is a direct matter of looking at the program to see what touches globalSessions.
Note that you should include the stack trace as part of your question. Don't just add it as a comment. It's critical to include this information: otherwise we would not have been able to easily trace the problem. Please improve the quality of your questions to make it easier for people to help you.
Also, you may want to take a serious look at go vet, which is a tool that helps to catch problems like this.
As this is the one line you used in code :
t, _ := template.ParseFiles("login.tpl")
So what you need to check is whether the file login.tpl is at the correct location, where it must be, or not. If not then correct the reference of it and also check same for the other references.
This helped me.

Resources