I'm trying to connect to MongoDB Atlas free cluster using mgo.
Golang Code -
package main
import (
"fmt"
"gopkg.in/mgo.v2"
"time"
"log"
)
const (
AuthDatabase = "mydatabase"
AuthUserName = "databaseadmin"
AuthPassword = "databasepassword"
ReplicaSetName = "myproject-shard-0"
)
func main(){
MongoDBHosts := []string{
"myproject-shard-00-00-w4vds.mongodb.net:27017",
"myproject-shard-00-01-w4vds.mongodb.net:27017",
"myproject-shard-00-02-w4vds.mongodb.net:27017",
}
mongoDBDialInfo := &mgo.DialInfo{
Addrs: MongoDBHosts,
Timeout: 60 * time.Second,
Database: AuthDatabase,
Username: AuthUserName,
Password: AuthPassword,
ReplicaSetName: ReplicaSetName,
}
mongoSession, err := mgo.DialWithInfo(mongoDBDialInfo)
if err != nil {
log.Fatalf("CreateSession: %s\n", err)
}
defer mongoSession.Close()
fmt.Printf("Connected to replica set %v!\n", mongoSession.LiveServers())
}
Error Message -
CreateSession: no reachable servers
Environment
I'm using mongodb free cluster with Google App Engine GO SDK
To connect to MongoDB Atlas, you need SSL
Prerequisites
TLS/SSL
Clients must have support for TLS/SSL to connect to an Atlas cluster.
Clients must have support for the SNI TLS extension to connect to an Atlas M0 Free Tier cluster.
Whitelist
To access a cluster, you must connect from an IP address on the Atlas group’s IP whitelist. If you need to add an IP address to the whitelist, you can do so in the Connect dialog. You can also add the IP address from the Security tab.
Thus I had to change the following piece of code
mongoDBDialInfo := &mgo.DialInfo{
Addrs: MongoDBHosts,
Timeout: 60 * time.Second,
Database: AuthDatabase,
Username: AuthUserName,
Password: AuthPassword,
ReplicaSetName: ReplicaSetName,
}
mongoDBDialInfo.DialServer = func(addr *mgo.ServerAddr) (net.Conn, error) {
conn, err := tls.Dial("tcp", addr.String(), tlsConfig)
return conn, err
}
I had to import the following too
"crypto/tls"
"net"
Related
I am getting a server selection timeout while connecting to mongodb.Any help is appreciated.
selection error: server selection timeout, current topology: { Type:
ReplicaSetNoPrimary, Servers: [{ Addr:
ac-pjyudwq-shard-00-01.1bnb2bm.mongodb.net:27017, Type: Unknown, Last
error: dial tcp 3.6.207.111:27017: i/o timeout }, { Addr:
ac-pjyudwq-shard-00-00.1bnb2bm.mongodb.net:27017, Type: Unknown, Last
error: dial tcp 3.7.150.83:27017: i/o timeout }, { Addr:
ac-pjyudwq-shard-00-02.1bnb2bm.mongodb.net:27017, Type: Unknown, Last
error: dial tcp 3.7.137.42:27017: i/o timeout }, ] } exit status 1
Code that I used for the connection
const MONGOURL = "mongodb+srv://sai:sai#cluster0.1bnb2bm.mongodb.net/?retryWrites=true&w=majority"
var collection *mongo.Collection
func init() {
fmt.Println("in the init function") var databasename string = "GOAPI"
var collectionname string = "Movies"
client, err: = mongo.Connect(context.TODO(), options.Client().ApplyURI(MONGOURL)) if err != nil {
fmt.Println("unable to get mongo connection ") log.Fatal(err)
}
collection = ( * mongo.Collection)(client.Database(databasename).Collection(collectionname)) fmt.Println("sucessfully collection is created ") doc: = Movie {
Name: "rrr",
Watched: false,
Rating: 9,
Id: "12121"
}
result, err: = collection.InsertOne(context.Background(), doc) if err != nil {
log.Fatal("hey unable to insert one ", err)
}
fmt.Println("sucessfully added : ", result.InsertedID)
// mongo.Connect()`your text`
}
I am hosting my test Atlas cluster on AWS so I wanted to have similar credential management to the AWS process. From the AWS credentials page:
The default provider chain looks for credentials in the following order:
Environment variables.
Shared credentials file.
If your application is running on an Amazon EC2 instance, IAM role for Amazon EC2.
Therefore, I wanted to implement the environment veriable for my simple login to Atlas example. Code below assumes that the following line has been issued at the command line
export MONGO_PW='<your Atlas admin user password>'
Then the following program will verify your connection
package main
import (
"context"
"fmt"
"os"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
var username = "<username>"
var host1 = "<atlas host>" // of the form foo.mongodb.net
func main() {
ctx := context.TODO()
pw, ok := os.LookupEnv("MONGO_PW")
if !ok {
fmt.Println("error: unable to find MONGO_PW in the environment")
os.Exit(1)
}
mongoURI := fmt.Sprintf("mongodb+srv://%s:%s#%s", username, pw, host1)
fmt.Println("connection string is:", mongoURI)
// Set client options and connect
clientOptions := options.Client().ApplyURI(mongoURI)
client, err := mongo.Connect(ctx, clientOptions)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
err = client.Ping(ctx, nil)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println("Connected to MongoDB!")
}
From here the rest of the tutorial linked in my original question goes smoothly.
I'm using mongodb community version 4.2.13 and go driver version 1.5.
My go application is running on the same host as db, but getting the following error when trying to make a connection:
connection() error occured during connection handshake: auth error:
sasl conversation error: unable to authenticate using mechanism
"SCRAM-SHA-256": (AuthenticationFailed) Authentication failed.
Here is how I created the admin account:
use admin
db.createUser({
user: "admin1",
pwd: "passwd12#$",
roles: ["root"],
mechanisms: ["SCRAM-SHA-256"]
})
db.system.users.update(
{ _id: "admin.admin1", "db": "admin" },
{
$addToSet: {
authenticationRestrictions: { clientSource: ["127.0.0.1"] }
}
}
)
Go app code snippet
package main
import (
"context"
"fmt"
"time"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func main() {
uri := fmt.Sprintf(
"mongodb://%s:%s#%s:%d/admin?authSource=admin&authMechanism=SCRAM-SHA-256",
"admin1",
"passwd12#$",
"127.0.0.1",
27017,
)
// Prints "mongodb://admin1:passwd12#$#127.0.0.1:27017/admin?authSource=admin&authMechanism=SCRAM-SHA-256"
fmt.Println(uri)
ctx, cancel := context.WithTimeout(context.Background(), 10 * time.Second)
defer cancel()
client, err := mongo.Connect(
ctx,
options.Client().ApplyURI(uri),
)
if err != nil {
panic(err)
}
defer func() {
err = client.Disconnect(ctx)
if err != nil {
panic(err)
}
}()
err = client.Ping(ctx, nil)
if err != nil {
panic(err)
}
fmt.Println("pinged")
}
I tried the following, but all of them didn't work:
Encoding username and password using url.QueryEscape
Trying "localhost" instead of "127.0.0.1"
Removing "authMechanism=SCRAM-SHA-256" in uri
As a side note, connecting to the Mongo shell with the exact same uri, and that worked.
Add ssl=false to your uri. Worked for me
Based on MongoDB documentation for the authentication process, there is a parameter to identify which database is used for authentication besides the target database on the URI.
While in mongoshell you can use this line
mongo "mongodb://Admin:${DBPASSWORD}#<host>:<port>/admin?authSource=admin"
I used that information to add ?authSource=admin to my CONNECTION_URL
CONNECTION_URL=mongodb://root:example#mongo:27017/my_database?retryWrites=true&w=majority&authSource=admin
That worked for me. Hope it does for you too.
For detailed information please review https://www.mongodb.com/features/mongodb-authentication
You could try using 'options.Credential' to pass the authentication settings.
Seems like a cleaner way than formatting an URI that needs to be parsed later on.
https://docs.mongodb.com/drivers/go/current/fundamentals/auth/
clientOpts := options.Client().SetHosts(
[]string{"localhost:27017"},
).SetAuth(
options.Credential{
AuthSource: "<authenticationDb>",
AuthMechanism: "SCRAM-SHA-256",
Username: "<username>",
Password: "<password>",
}
)
client, err := mongo.Connect(context.TODO(), clientOpts)
I am unable to connect to AWS RDS PostgeSQL when running Helm Chart for a Go App (GORM). All credentials are stored in a kubernetes secret, and the secret is being used in the helm chart.
Few Points:
Able to connect locally just fine.
The PostgreSQL database is already created in RDS, and made sure that the kubernetes secret as matches with the same creds.
Docker image is pushed and pulled from Gitlab without any errors.
Command "helm ls" displays the deployment status as "DEPLOYED"
When taking "kubectl get pod", I get STATUS as "CrashLoopBackoff"
When taking "kubectl describe pod", I get back MESSAGE "Back-off restarting failed container"
I then take "kubectl logs pod_name" to track the error, and get back the following:
failed to connect to the database
dial tcp 127.0.0.1:5432: connect: connection refused
(Not sure why it is still specifying "127.0.0.1" when i have the secret mounted)
Unable to exec into the pod because it is not running.
I have tried:
Secure a connection in the same cluster from a different pod using psql to ensure that the creds in the secret are in sync with what was set up in RDS PostgreSQL
Changing the api from DB_HOST, to host=%s
Tried connecting using fmt.Sprintf, as well as os.Getenv
"Versions"
GO version:
go1.11.1 darwin/amd64
DOCKER version:
Client:
Version: 18.06.1-ce
API version: 1.38
API.GO (file)
package controllers
import (
"fmt"
"log"
"net/http"
"os"
"time"
"github.com/gorilla/mux"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres"
_ "gitlab.torq.trans.apps.ge.com/503081542/torq-auth-api/models"
)
var err error
type API struct {
Database *gorm.DB
Router *mux.Router
}
func (api *API) Initialize(opts string) {
// Initialize DB
dbinfo := os.Getenv("DB_HOST, DB_USER, DB_PASSWORD, DB_NAME,
DB_PORT sslmode=disable")
// dbinfo := os.Getenv("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable")
api.Database, err = gorm.Open("postgres", dbinfo)
if err != nil {
log.Print("failed to connect to the database")
log.Fatal(err)
}
// Bind to a port and pass our router in
// log.Fatal(http.ListenAndServe(":8000", handlers.CORS() .
(api.Router)))
fmt.Println("Connection established")
log.Printf("Postgres started at %s PORT", config.DB_PORT)
// MODELS
type application struct {
ID string `json:"id" gorm:"primary_key"`
CreatedAt time.Time `json:"-"`
UpdatedAt time.Time `json:"-"`
Name string `json:"name"`
Ci string `json:"ci"`
}
type Cloud struct {
ID string `json:"id" gorm:"primary_key"`
Name string `json:"name"`
}
fmt.Println("Tables are created")
// Enable this line for DB logging
api.Database.LogMode(true)}
// Initialize Router
api.Router = mux.NewRouter()
api.Router.HandleFunc("/api/v1/applications",
api.HandleApplications)
api.Router.HandleFunc("/api/v1/application/{id}",
api.HandleApplication)
api.Router.HandleFunc("/api/v1/clusters", api.handleClusters)
}
I am not exactly sure where the issue could be here, this is a learning experience for myself. Any ideas would be appreciated.
Thanks in advance!
I think your database initialization code is not doing what you want. Try something like this
var driver = "postgres"
var name = os.Getenv("DB_NAME")
var host = os.Getenv("DB_HOST")
var user = os.Getenv("DB_USER")
var pass = os.Getenv("DB_PASSWORD")
var conn = fmt.Sprintf("host=%v user=%v password=%v dbname=%v sslmode=disable", host, user, pass, name)
api.Database, err := gorm.Open(driver, conn)
I am trying to connect to remote mongodb servers using port forwarding. In mgo.DialInfo, the dial server will return a connection (of type *net.Conn) to the intermediary host and the Addrs would be a string of remote mongodb servers' addresses (a set of seed servers). My question is, if I give the remote IP addresses of these servers, the program works fine and I am able to connect to the remote mongodb servers but since, the IP addresses can change I want to give the domain names of the servers, say mongodbserver1 and mongodbserver2 and when I give that, the program hangs. As far as I understood, the program is trying to resolve the domain names on my local machine which won't work and they need to be resolved on the intermediary host. I want to know how I can do that.
In terms of code, the following works fine,
dialinfo := mgo.DialInfo{
Addrs: {"1.2.3.45","1.2.3.56"}
Database: Mongo1,
Username: User,
Password: Pwd,
Timeout: 60 * time.Second,
DialServer: func(addr *mgo.ServerAddr) (net.Conn, error) {
conn, err := connectToCluster("172.12.13.145")
if err != nil {
fmt.Println("couldn't connect to the cluster, trying again..")
return nil, err
}
remote, err := conn.Dial("tcp", addr.String())
if err != nil {
fmt.Println("couldn't connect to the mongodb server:", addr.String())
}
return remote, err
},
}
session, err = mgo.DialWithInfo(dialinfo)
but the one below doesn't work
dialinfo := mgo.DialInfo{
Addrs: {"mongodbserver1","mongodbserver2"}
Database: Mongo1,
Username: User,
Password: Pwd,
Timeout: 60 * time.Second,
DialServer: func(addr *mgo.ServerAddr) (net.Conn, error) {
conn, err := connectToCluster("172.12.13.145")
if err != nil {
fmt.Println("couldn't connect to the cluster, trying again..")
return nil, err
}
remote, err := conn.Dial("tcp", addr.String())
if err != nil {
fmt.Println("couldn't connect to the mongodb server:", addr.String())
}
return remote, err
},
}
session, err = mgo.DialWithInfo(dialinfo)
Configure name resolution to work on your machine. This could mean adding test entries to /etc/hosts (if you're using *nix). Or you could add an entry for your production DNS server to /etc/resolv.conf (again, assuming *nix).
I am trying to connect to Heroku's Postgres using a Go. All is working fine locally.
The error I am receiving on Heroku is dial tcp 127.0.0.1:5432: connection refused.
I've confirmed my ability to connect to the database via psql on heroku's command line, and have confirmed that the database url config is correct. The code is clear enough, so I wonder if there is a lower-level problem.
The code is straightforward enough:
import (
"database/sql"
"github.com/coopernurse/gorp"
_ "github.com/lib/pq"
"os"
)
func openDb() *sql.DB {
connection := os.Getenv("DATABASE_URL")
db, err := sql.Open("postgres", connection)
if err != nil {
log.Println(err)
}
return db
}
...and am importing github.com/lib/pq. Go version is 1.1.2.
Heroku + Go is pretty particular about the connection strings. The URL-style doesn't seem to allow specification of sslmode=require, which Heroku insists upon.
The modified version uses pq to parse the URL into a traditional Postgres connection string, and appends the parameter:
import (
"database/sql"
"github.com/lib/pq"
"os"
)
func openDb() *sql.DB {
url := os.Getenv("DATABASE_URL")
connection, _ := pq.ParseURL(url)
connection += " sslmode=require"
db, err := sql.Open("postgres", connection)
if err != nil {
log.Println(err)
}
return db
}