Type conflicts between projects - go

I want to move code used by a specific subdomain its own project, which will be imported by the main code base which is currently resides in. I am able to import code from the subdomain into the main project successfully, until I add the Gorilla Mux code. For example, this works:
// imports and non-relevant routes removed for simplicity
r := mux.NewRouter()
// Primary site routes here...
s := r.Host("subdomain-regex-here").Subrouter()
s.HandleFunc("/", people.Index)
http.ListenAndServe("localhost:8080", r)
But when I move the subdomain to its own project and import it, then call the LoadRoutes() function which passes in the mux.Router object from the primary site, I receive an error. Here's the code:
// Primary Project
r := mux.NewRouter()
// Primary site routes here...
// function located in the subdomain go project, which is imported
func LoadRoutes(host string, r *m.Router) {
s := r.Host(host).Subrouter()
s.HandleFunc("/", people.Index)
s.HandleFunc("/people", people.Index)
s.HandleFunc("/person/new", people.New)
}
# command-line-arguments
./main.go:25: cannot use r (type *"primary_site/vendor/github.com/gorilla/mux".Router) as type *"subdomain_site/vendor/github.com/gorilla/mux".Router in argument to routers.LoadRoutes
It looks like I have two instances of the Gorilla Mux, from two separate projects, that are conflicting. I only import packages from the subdomain site to the primary site, not the other way around. This exact code works perfectly as long as I have it in a single project, but when I try to separate the projects, it breaks.
Since I pass in the instance of mux.NewRouter(), why am I having a conflict?

You have 2 vendor directories in your project. You need to flatten them into a single vendor directory at the top level in order to share vendored types between packages. Remove the subdomain_site/vendor and only use the vendor directory in your main pacakage.

Related

How to call a Go Cloud Function from another Go Cloud Function in GCP

Goal: I want to reuse many Go functions from two Go functions with HTTP triggers.
What I have tried and steps to reproduce the problem:
In GCP, create a new Go 1.11 Cloud Function, HTTP Trigger
Name it: MyReusableHelloWorld
In function.go, paste this:
package Potatoes
import (
"net/http"
)
// Potatoes return potatoes
func Potatoes(http.ResponseWriter, *http.Request) {
}
In go.mod, paste this: module example.com/foo
In function to execute, paste this: Potatoes
Click on deploy. It works.
Create another Go serverless function in GCP
In function. go, paste this:
// Package p contains an HTTP Cloud Function.
package p
import (
"encoding/json"
"fmt"
"html"
"net/http"
"example.com/foo/Potatoes"
)
// HelloWorld prints the JSON encoded "message" field in the body
// of the request or "Hello, World!" if there isn't one.
func HelloWorld(w http.ResponseWriter, r *http.Request) {
var d struct {
Message string `json:"message"`
}
if err := json.NewDecoder(r.Body).Decode(&d); err != nil {
fmt.Fprint(w, "error here!")
return
}
if d.Message == "" {
fmt.Fprint(w, "oh boy Hello World!")
return
}
fmt.Fprint(w, html.EscapeString(d.Message))
}
In go.mod, paste this: module example.com/foo
In function to execute, paste this: HelloWorld
Click on deploy. It doesn't work. You have the error: unknown import path "example.com/foo/Potatoes": cannot find module providing package example.com/foo/Potatoes
I have also tried all kinds of combinations for the module/packages to import.
I have tried without the example.com/ part.
Other smaller issue:
The functions I want to reuse could all be in the same file and don't really need any trigger, but it doesn't seem that having no trigger is possible.
Related questions and documentation with which I could not achieve my goal:
How can I use a sub-packages with Go on Google Cloud Functions?
https://github.com/golang/go/wiki/Modules , section go.mod
You can’t invoke a cloud function from another one, because each function is in its own container independently.
So If you want to deploy the function with a dependency that can't be downloaded from a package manager you need to put the code together like here and deploy using the CLI
It is likely each cloud function defined in the console is independent of the other. If you want code reuse, it's best to structure it as per the following document and deploy it using gcloud command.
https://cloud.google.com/functions/docs/writing/#structuring_source_code
You are mixing things: package management and function deployment.
When you deploy a Cloud Function, if you want to (re)use it, you have to call if with http package.
If you build a package that you want to include in your source code, you have to rely on package manager. With Go, Git repository, like Github, is the best way to achieve this (Don't forget to perform a release and to name it as expected by Go mod: vX.Y.Z)
Here your code can't work without more engineering and package publication/management.
I achieve the same things but with a Dockerfile and I deplored my code in Cloud Run (that I recommend you if you aren't event oriented and only HTTP oriented. I wrote a comparison on Medium)
Root
go.mod
pkg/foo.go
pkg/go.mod
service/Helloworld.go
service/go.mod
In my helloworld.go, I can reuse the package foo. For this I perform this in my service/go.mod file
module service/helloworld
go 1.12
require pkg/foo v0.0.0
replace pkg/foo v0.0.0 => ../pkg
Then when you build your container, you run your go build service/Helloworld.go from the root directory.

Golang import cycle not allowed

I am creating a restful api in GO and every method essentially interacts with the database. The specific statement that I use to open a database connection is
db,err := sql.Open("postgres", "user=postgres password=password dbname=dbname sslmode=disable")
if err != nil {
log.Fatal(err)
println(err)
}
It is very simple but the issue is that once I want to change something inside that statement then I have to change it for all other methods that have that statement . I am trying to do a dependency injection or something of that nature so that I can have that statement or value in 1 place and just reference it. I am getting an import cycle not allowed error though like Import cycle not allowed . This is my project structure
What I have done is that in the Config.go I have written this
package Config
const Connect = "user=postgres password=password dbname=dbname sslmode=disable"
Then in the Listings.go I put this
package Controllers
import (
"net/http"
"database/sql"
"../Config"
)
func Listing_Expiration(w http.ResponseWriter, r *http.Request) {
db,err := sql.Open("postgres",Config.Connect)
if err != nil {
log.Fatal(err)
println(err)
}
notice I have the import ../Config and the Config.Connect but when I compile that I get import cycle not allowed . I have been trying to solve this issue but haven't been able to.
Yes, Go doesn't allow to have cycled imports. In your example you have 2 packages Config and Controllers. When you build a code, Controllers package requires Config package, then Config requires Controllers and it's endless. You should refactor your code to make Config package separated from Controllers, and only used by it. Also, you can make some common package, imported to Controllers and Config.
I got the same error. But in my case, I imported the package itself inside the file. so you can check if you did the same mistake.
Go does NOT allow import cycles. So you need to fix it. In your case, Controller and Config are importing each other, you are not allowed to do that.
I wrote a detailed blog about how you can deal with it. Refer https://jogendra.dev/import-cycles-in-golang-and-how-to-deal-with-them
Use interfaces.
Making use of go:linkname.
Though, I can't see any import cycle from your code.
Go don't support the import cycle. Based on your question, you must import the controller from the Config Package. You need to remove one of them and refactor your code.
Fore more: https://jogendra.dev/import-cycles-in-golang-and-how-to-deal-with-them

Using an External Dependency in a Library

I am using wgo for dependency management in Golang (although I think wgo has little to do with this), wgo has a folder structure like this
project/
.gocfg/
gopaths
vendor.json
vendor/
src/
github.com_or_whatever/
I have a library I coded myself which uses an nsq-go type in one of the exported methods:
func AddNsqSubscription(
topic, channel string,
handler nsq.Handler,
config *nsq.Config) error { }
The library is called messi and I import the nsq-go like so "messi/vendor/src/github.com/bitly/go-nsq"
The problem comes when I try to use this library in another project. For instance, in a project called scribe I have the following code (notice the imports):
import (
"scribe/vendor/src/github.com/bitly/go-nsq"
"scribe/vendor/src/messi"
)
//...
nsqHandler := nsq.HandlerFunc(func(message *nsq.Message) error {
msgHandler(MessiMessage{message})
return nil
})
return messi.AddNsqSubscription(destination, subdestination, nsqHandler, nsq.NewConfig())
When I go build the following error is returned:
cannot use nsqHandler (type "scribe/vendor/src/github.com/bitly/go-nsq".HandlerFunc) as type "messi/vendor/src/github.com/bitly/go-nsq".Handler in argument to messi.AddNsqSubscription:
"scribe/vendor/src/github.com/bitly/go-nsq".HandlerFunc does not implement "messi/vendor/src/github.com/bitly/go-nsq".Handler (wrong type for HandleMessage method)
have HandleMessage("scribe/vendor/src/github.com/bitly/go-nsq".Message) error
want HandleMessage("messi/vendor/src/github.com/bitly/go-nsq".Message) error
Why? I do not really know what is going on. The code go-nsq imported is exactly the same, yet golang wants that this code comes from the same folder?
What am I doing wrong?
Packages in Go are identified by full import path, not by name.
For example in the standard library there are two different packages with the same name template but different import paths: text/template and html/template.
You should make sure that go-nsq package is imported using the same path.

Gorilla mux routes in separate files in subfolder?

I'm trying to build a very simple Go web application, and the golang "a folder per package" structure is making things difficult for me.
I'm using github.com/gorilla/mux as the router and github.com/unrolled/render for template rendering. This means that I need to create a new router and a new renderer when the app launches, and I need all my routes to access the renderer.
This is super easy to do in a single file:
func main() {
...
r := render.New(render.Options{
// a lot of app specific setup
})
mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
r.HTML(w, http.StatusOK, "myTemplate", nil)
})
...
}
However, this is where I don't understand Go. Because I want the routes in separate files in a subfolder (my project will grow), that forces them to be in a routes package. Of course that makes the renderer variable inaccesssible. I can't just create the renderer in the routes package, because the render.New() call relies on a me passing in a ton of app specific stuff like the template folder, and helpers for asset paths.
I went down the route of making my handler functions work on a struct with an already initialized renderer...
func (app *App) Hello2(w http.ResponseWriter, r *http.Request) {
app.Renderer.HTML(w, http.StatusOK, "myTemplate", nil)
}
But I'm still confused as to how I'm going to access this app *App in the routes package when it's initialized in main. Everything in Go seems super easy if you have a flat list of files, but as soon as you want a bit of folder structure, the package setup becomes problematic.
There's probably something I'm missing here, so any help is appreciated.
Here's general info on dealing with dependencies in Go. A key trick is, you just have to declare the Render variable in a package that your views can import. You could create a myapp/render package with a var Render that's either inited in the package itself (func init()) or set from main.
But the context thing you found sounds totally sane, though it might be more than this app needs. The neat thing about it is that because the context is set in per-request code, later you could extend it to do sneaky things like use the Host: header to provide a different Layout for people loading the app via different domains. If the Layout is baked into a global, you can't. This can be a real advantage--I've tried to retrofit per-request changes onto big codebases whose config was sprayed around various global variables, and it's a pain.

Package bound resource use for multiple go packages

For a contrived example:
I have 2 packages, repo.com/alpha/A & repo.net/beta/B. package A uses package B, both structured as example.
A:
main.go
B:
b.go
templates \
1.tmpl
2.tmpl
In main.go of package A, I'd need to access the templates directory of package B.
b.go
var templates string
templates = templatepath
func init(){
templatepath, _ = filepath.Abs("./templates")
}
main.go
import(
repo.net/beta/B
)
func main(){
fmt.Printf("%s", B.templates)
}
So the problem being in my more complex use case & the contrived example here is that B.templates will be in the directory for package A, where I need to establish and reference the directory of the imported path. This is part of learning and navigating the Go way of doing things, and my understanding is probably basic, so I need to understand how to do this in a Go context.
My use case is a package that uses other packages that do things for the base package, and these external packages may use standard web resources files(css, html, js) the problem being I'm having immediate trouble packaging and referencing them abstractly enough for what I want to do.
You can't, you have to either use something like go-bindata or so, or simply embed the templates in your B package as consts.
tmpl1.go:
const tmpl1 = `........`

Resources