Instantiating an object in a file VS in a struct - go

I was reading this blog recently and I saw something interesting. The object instance is initialized in the file itself and then accessed everywhere. I found it pretty convenient and was wondering if it's the best practice.
https://dev.to/hackmamba/build-a-rest-api-with-golang-and-mongodb-gin-gonic-version-269m#:~:text=setup.go%20file%20and%20add%20the-,snippet%20below,-%3A
I'm more used to a pattern where we first create a struct like so:
type Server struct {
config util.Config
store db.Store
tokenMaker token.Maker
router *gin.Engine
}
and then set eveything in main:
func NewServer(config util.Config, store db.Store) (*Server, error) {
tokenMaker, err := token.NewPasetoMaker(config.TokenSymmetricKey)
if err != nil {
return nil, fmt.Errorf("cannot create token maker: %w", err)
}
server := &Server{
config: config,
store: store,
tokenMaker: tokenMaker,
}
server.setupRouter()
return server, nil
}
and then the server object is passed every where.
What's best? Is it okay to use the pattern mentioned in that blog?
Thank you.
I tried to implement both patterns, The pattern mentioned in the blog seems very convenient to use as I'm not passing around objects and can easily access object I'm interested in.

You can follow any one of those patterns. But, I think it's better to pass the object pointer everywhere necessary. It saves lots of work and ensures that the object is always updated.

Related

How to structure my Go app for transactions via pgx

I have the following models
type UsersModel struct {
db *pgx.Conn
}
func (u *UsersModel) SignupUser(ctx context.Context, payload SignupRequest) (SignupQueryResult, error) {
err := u.db.Exec("...")
return SignupQueryResult{}, err
}
type SessionsModel struct {
db *pgx.Conn
}
func (s *SessionsModel) CreateSession(ctx context.Context, payload CreateSessionRequest) error {
_, err := s.db.Exec("...")
return err
}
and my service calls UsersModel.SignupUser as follows
type SignupService struct {
userModel signupServiceUserModel
}
func (ss *SignupService) Signup(ctx context.Context, request SignupRequest) (SignupQueryResult, error) {
return ss.userModel.SignupUser(ctx, request)
}
Now, I need to tie SignupUser and CreateSession in a transaction instead of isolated operations, not sure what the best way to structure this is, and how to pass transaction around while maintaining that abstraction of DB specific stuff from services. Or should I just call the sessions table insert query(which I'm putting in *SessionsModel.CreateSession directly in *UsersModel.SignupUser?
For reference, transactions in pgx happen by calling *pgx.Conn.Begin() which returns a concrete pgx.Tx , on which you execute the same functions as you would on *px.Conn , followed by *pgx.Tx.Commit() or *pgx.Tx.Rollback()
Questions I have are:
Where to start transaction - model or service?
If in service, how do I do that while abstracting that there's an underlying DB from service?
How do I pass transaction between models?
There is no right or wrong answer for this since there are multiple ways to do it. However, I share how I'd do it and why.
make sure to keep the service layer clean of any concrete DB implementation, so if you switch to a completely new DB you do not need to change other pieces.
about the solution, I would create a completely new method called SignupUserAndCreateSession that encloses all the logic you need. I wouldn't worry because you have the two original methods in one, as per my understanding in this scenario both of them are tightly coupled by design, so this would not be an anti-pattern.
I would avoid moving around the *pgx.Tx between methods since anyway you would depend on another level that makes sure to commit or roll back, and this might cause errors in future implementations.

List custom resources from caching client with custom fieldSelector

I'm using the Operator SDK to build a custom Kubernetes operator. I have created a custom resource definition and a controller using the respective Operator SDK commands:
operator-sdk add api --api-version example.com/v1alpha1 --kind=Example
operator-sdk add controller --api-version example.com/v1alpha1 --kind=Example
Within the main reconciliation loop (for the example above, the auto-generated ReconcileExample.Reconcile method) I have some custom business logic that requires me to query the Kubernetes API for other objects of the same kind that have a certain field value. It's occurred to me that I might be able to use the default API client (that is provided by the controller) with a custom field selector:
func (r *ReconcileExample) Reconcile(request reconcile.Request) (reconcile.Result, error) {
ctx := context.TODO()
listOptions := client.ListOptions{
FieldSelector: fields.SelectorFromSet(fields.Set{"spec.someField": "someValue"}),
Namespace: request.Namespace,
}
otherExamples := v1alpha1.ExampleList{}
if err := r.client.List(ctx, &listOptions, &otherExamples); err != nil {
return reconcile.Result{}, err
}
// do stuff...
return reconcile.Result{}, nil
}
When I run the operator and create a new Example resource, the operator fails with the following error message:
{"level":"info","ts":1563388786.825384,"logger":"controller_example","msg":"Reconciling Example","Request.Namespace":"default","Request.Name":"example-test"}
{"level":"error","ts":1563388786.8255732,"logger":"kubebuilder.controller","msg":"Reconciler error","controller":"example-controller","request":"default/example-test","error":"Index with name field:spec.someField does not exist","stacktrace":"..."}
The most important part being
Index with name field:spec.someField does not exist
I've already searched the Operator SDK's documentation on the default API client and learned a bit about the inner workings of the client, but no detailed explanation on this error or how to fix it.
What does this error message mean, and how can I create this missing index to efficiently list objects by this field value?
The default API client that is provided by the controller is a split client -- it serves Get and List requests from a locally-held cache and forwards other methods like Create and Update directly to the Kubernetes API server. This is also explained in the respective documentation:
The SDK will generate code to create a Manager, which holds a Cache and a Client to be used in CRUD operations and communicate with the API server. By default a Controller's Reconciler will be populated with the Manager's Client which is a split-client. [...] A split client reads (Get and List) from the Cache and writes (Create, Update, Delete) to the API server. Reading from the Cache significantly reduces request load on the API server; as long as the Cache is updated by the API server, read operations are eventually consistent.
To query values from the cache using a custom field selector, the cache needs to have a search index for this field. This indexer can be defined right after the cache has been set up.
To register a custom indexer, add the following code into the bootstrapping logic of the operator (in the auto-generated code, this is done directly in main). This needs to be done after the controller manager has been instantiated (manager.New) and also after the custom API types have been added to the runtime.Scheme:
package main
import (
k8sruntime "k8s.io/apimachinery/pkg/runtime"
"example.com/example-operator/pkg/apis/example/v1alpha1"
// ...
)
function main() {
// ...
cache := mgr.GetCache()
indexFunc := func(obj k8sruntime.Object) []string {
return []string{obj.(*v1alpha1.Example).Spec.SomeField}
}
if err := cache.IndexField(&v1alpha1.Example{}, "spec.someField", indexFunc); err != nil {
panic(err)
}
// ...
}
When a respective indexer function is defined, field selectors on spec.someField will work from the local cache as expected.

Dependency Injection & Testing

I'm working on a small Go application that's basically a wrapper for various password stores (Ansible Vault, Hashicorp Vault, Chef Vault, etc). The idea is: In my various provisioning scripts, I can use my Go wrapper to grab secrets and if we decide to switch password stores behind the scenes, all of the interfaces don't need to be updated across my projects.
I'm trying to setup proper tests for this application, and in doing so, am trying to figure out the best way to to inject my dependencies.
For example, lets say the project is called secrets. And one of my implementations is ansible. And the ansible implementation needs its own parser and needs to open its own connection to the ansible vault, to retrieve the data.
So I might have the following:
package secrets
type PasswordStore interface {
GetKey(key string) (string, error)
}
func New(backend string, config map[string]interface{}) (PasswordStore, error) {
switch backend {
case "ansible":
return ansible.New(config)
default:
return nil, fmt.Errorf("Password store '%s' not supported.", backend)
}
}
package ansible
type Connection interface {
open() (string, error)
}
type Ansible struct {
connection Connection
contents map[string]string
}
func New(c map[string]interface{}) (*Ansible, error) {
conn, err := NewConnection(c["ansible_path"].(string))
if err != nil {
return nil, err
}
// open connection, parse, etc...
a := &Ansible{
connection: conn,
contents: parsedData,
}
return a, nil
}
So this seems nice because the secrets package doesn't need knowledge of the ansible package dependencies (connection), and the factory just new's up the instance with some config data. However, if I need to mock the connection that Ansible receives, there doesn't seem to be a good way to do this (unless that config map had a connection option called mock)
The other option is to abandon the factory, and just assemble all the dependencies from the secrets package, like:
package secrets
type PasswordStore interface {
GetKey(key string) (string, error)
}
func New(backend string, config map[string]interface{}) (PasswordStore, error) {
switch backend {
case "ansible":
return ansible.New(AnsibleConnection{}, config)
default:
return nil, fmt.Errorf("Password store '%s' not supported.", backend)
}
}
package ansible
// same as before in this file, but with injected dependency ...
func New(connect Connection, c map[string]interface{}) (*Ansible, error) {
conn, err := connect.NewConnection(c["ansible_path"].(string))
if err != nil {
return nil, err
}
// open connection, parse, etc...
a := &Ansible{
connection: conn,
contents: parsedData,
}
return a, nil
}
Now the dependency is injected, but it seems like secrets needs to have knowledge of every dependency for every implementation.
Is there a more logical way to structure this so that secrets knows less? Or is it typical for the top level package to be orchestrating everything?
What decides what the backend is? That should help guide you. I've done something similar with support for multiple databases on a project, and what I did was basically:
config package reads in config file, which determines what backend is being used
store package offers the generic interface and has a function that takes a config, and returns an implementation
server package references only the interface
main package reads the config, passes it to the factory function in store, then injects the result into the server on creation
So when I create my server (which actually uses the data store), I pass the config to the factory function in store, which returns an interface, and then inject that into the server. The only thing that has to know about the different concrete implementations is the same package that exposes the interface and factory; the server, config, and main packages see it as a black box.

sensulib package interface as function param

I am trying to make use of this golang package: https://github.com/jefflaplante/sensulib
I want to get all the events from the sensu API. I've followed the example code and modified it slightly so it works:
config := sensu.DefaultConfig()
config.Address = "sensu-url:port"
onfig.Username = "admin"
config.Password = "password"
// Create a new API Client
sensuAPI, err := sensu.NewAPIClient(config)
if err != nil {
// do some stuff
}
Now I want to grab all the events from the API, and there's a neat function do to that, GetEvents
However, the function expects a parameter, out, which is an interface. Here's the function itself:
func (c *API) GetEvents(out interface{}) (*http.Response, error) {
resp, err := c.get(EventsURI, out)
return resp, err
}
What exactly is it expecting me to pass here? I guess the function wants to write the results to something, but I have no idea what I'm supposed to call the function with
I've read a bunch of stuff about interfaces, but it's not getting any clearer. Any help would be appreciated!
The empty interface interface{} is just a placeholder for anything. It's roughly the equivalent of object in Java or C# for instance. It means the library doesn't care about the type of the parameter you are going to pass. For hints about what the library does with that parameter, I suggest you look at the source code.

Revel - Storing an object in session

I'm using the oauth package "code.google.com/p/goauth2/oauth" with revel and the it creates a few structures with quite a bit of information in it. I need this information to be persistent throughout the session but sessions can only be type string. Is there a better way of doing this than the following?
c.Session["AccessToken"] = t.Token.AccessToken
c.Session["RefreshToken"] = t.Token.RefreshToken
...
If not how do I reassign the strings to create another structure to call Client.Get() ?
You can use the json package to "convert" structs to string and vice versa. Just know that only exported fields are serialized this way.
Since oauth.Token has only exported fields, this will work:
if data, err := json.Marshal(t.Token); err == nil {
c.Session["Token"] = string(data)
} else {
panic(err)
}
And this is how you can reconstruct the token from the session:
if err := json.Unmarshal([]byte(c.Session["Token"]), &t.Token); err != nil {
panic(err)
}
Instead of that you can try to save some string ID to Session and the object you need to Cache:
c.Session["id"] = id
go cache.Set("token_"+id, t.Token, 30*time.Minute)
And then use it as follows:
var token oauth.Token
id := c.Session["id"]
if err := cache.Get("token_"+id, &token); err != nil {
// TODO: token not found, do something
}
// TODO: use your token here...
The advantage of this approach is you do not have to work with json package explicitly and cache has a few different back-ends out of the box: memory, redis, memcached. Moreover, you do not have a limitation of 4K as in case of Cookie based Session.
https://revel.github.io/manual/cache.html

Resources