I have created a go application that sends an email using net/smtp and PlainAuth. And everything is working fine. But when I deploy this same code on the google app engine it is not sending. If I serve using goapp serve it work locally. Below is the code:
func sendMail(body string, subject string) {
var err error
var doc bytes.Buffer
auth := smtp.PlainAuth("",
"from#gmail.com",
"********",
"smtp.gmail.com")
parameters := &SmtpTemplateData{
"from#gmail.com",
"to#gmail.com",
"Subject",
body,
}
buffer := new(bytes.Buffer)
template := template.Must(template.New("emailTemplate").Parse(emailScript()))
template.Execute(buffer, parameters)
t := template.New("emailTemplate1")
t, err = t.Parse(emailTemplate)
if err != nil {
fmt.Println("error trying to parse mail template")
}
err = t.Execute(&doc, parameters)
smtp.SendMail(
"smtp.gmail.com:587",
auth,
"from#gmail.com",
[]string{"to#gmail.com"},
doc.Bytes(),
)
}
Related
I am currently trying to deploy another app on GAE using a Google Cloud SQL instance. I already have 2 services running on there with the same setup also using a Cloudsql instance. However, when I am trying to connect to Cloudsql from my newest service, I am getting a "driver: bad connection" error and I can not figure out why. I am using the _ "github.com/go-sql-driver/mysql" driver as follows:
var (
DB_INSTANCE *sql.DB
)
...
configFile := GetConfig()
user := configFile.DatabaseReaderHost.DatabaseUser
password := configFile.DatabaseReaderHost.DatabasePassword
dbName := configFile.DatabaseReaderHost.DatabaseName
connectionName := "project-id:region:instance-name"
if appengine.IsDevAppServer() {
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s#tcp(%s:%s)/%s?parseTime=true",
user,
password,
configFile.DatabaseReaderHost.DatabaseHost,
configFile.DatabaseReaderHost.DatabasePort,
dbName))
if err != nil {
panic(err)
}
DB_INSTANCE = db
return nil
} else {
dbn, err := sql.Open("mysql", fmt.Sprintf("%s:%s#cloudsql(%s)/%s", user, password, connectionName, dbName))
if err != nil {
panic(err)
}
DB_INSTANCE = dbn
return nil
}
What I noticed is that the connection to the Cloudsql instance seems to be established since the above code is not what produces the error, but my test query agains the database.
func TestDatabaseHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
var name string
err := DB_INSTANCE.QueryRow("SELECT name FROM names WHERE id = ?", 1).Scan(&name)
if err != nil {
if err == sql.ErrNoRows {
PrintSingleResponseJson(w, "error", fmt.Sprintf("no rows found"))
} else {
Log(r, fmt.Sprintf("Could not connect to database: %v", err))
PrintSingleResponseJson(w, "error", fmt.Sprintf("could not connect to database: %s", err))
}
return
}
PrintSingleResponseJson(w, "success", fmt.Sprintf("%s", name))
}
I appreciate all help.
I've created a Go API and deploy but when i see the logs there is no Println and no response is send to the client
the main:
func main() {
http.HandleFunc("/v1/", handler)
appengine.Main()
}
func handler(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
http.Error(w, "not allowed", http.StatusMethodNotAllowed)
return
}
...
}
the yaml file:
runtime: go
api_version: go1.8
handlers:
- url: /.*
script: _go_app
the url that i'm accessing:
myapp.appspot.com/v1/param
(what is this _go_app?)
the problem is that if i pass a "POST" request the error works just fine, but if i make a valid request the response take forever.
full code: https://goplay.space/#yiIp42FKzzj
In AppEngine you must use the AppEngine logger, passing in the context from the request.
Example from the godoc:
c := appengine.NewContext(r)
query := &log.Query{
AppLogs: true,
Versions: []string{"1"},
}
for results := query.Run(c); ; {
record, err := results.Next()
if err == log.Done {
log.Infof(c, "Done processing results")
break
}
if err != nil {
log.Errorf(c, "Failed to retrieve next log: %v", err)
break
}
log.Infof(c, "Saw record %v", record)
}
Hey im trying to implement a resumable upload to cloud storage .
But im getting a Status:"401 Unauthorized", StatusCode:401
And im assuming that it`s something with the bearer but i can't figure out another way to send the bearer Token.
I've been able to delete files with the GetClinet method.
func GetClinet(c endpoints.Context) *http.Client {
cli := &http.Client{
Transport: &oauth2.Transport{
Source: google.AppEngineTokenSource(c, storage.ScopeReadWrite),
Base: &urlfetch.Transport{Context: c},
},
}
return cli
}
client := GetClinet(con)
url := "https://storage.googleapis.com/bucketName/file.txt"
b := r.Header.Get("Authorization") //parse the bearer from user request
r, err = http.NewRequest("POST", url, nil)
r.Header.Add("Content-Type", "text/plain")
r.Header.Add("Content-Length", "0")
r.Header.Add("x-goog-resumable", "start")
r.Header.Add("Authorization", b)
resp, err := client.Do(r)
I'm having similar difficulties, not sure if your situation is the same as mine. My goal is to
User client uploads meta-data to my server
Server authenticates and stores in DB
Server asks Google Cloud Storage for resumabale upload url
Server returns url to client
Client uses url to upload media to GCS
I'm getting 401 unauthorized returns too. Maybe I'm not setting the headers correctly? or the storage.SignedURL doesn't handle resumable uploads properly...?
I've been able to upload files using a Service Account PEM file
func setGcsFile(c context.Context, fileId string, content []byte) error {
expiration := time.Now().Add(time.Second * 30) //expire in 30 seconds
data, err = ioutil.ReadFile(serviceAccountPEMFilename)
if err != nil {
fmt.Printf("error: %v", err)
panic(err)
}
log.Debugf(c, "Getting upload url")
opts := &storage.SignedURLOptions{
GoogleAccessID: googleAccessID,
PrivateKey: data,
Method: "PUT",
Expires: expiration,
}
putURL, err := storage.SignedURL(bucket, fileId, opts)
if err != nil {
log.Debugf(c, "%v", err)
return err
}
log.Debugf(c, "PUT URL : %v\n", putURL)
client := urlfetch.Client(c)
req, err := http.NewRequest("PUT", putURL, bytes.NewBuffer(content))
res, err := client.Do(req)
if err != nil {
log.Debugf(c, "%v", err)
return err
}
res.Body.Close()
log.Debugf(c, "Response Code: %s\n", res.Status)
return nil
}
I'm thinking about pulling and modifying the storage.SignedURL function next.
I tried to use the example of sessionauth of martini on google app engine and want to save login list in Datastore, but did not know how to deal with appengine.Context. Does anyone has the experience?
Thank you.
Update:
// Auth example is an example application which requires a login
// to view a private link. The username is "testuser" and the password
// is "password". This will require GORP and an SQLite3 database.
package ahl
import (
//"fmt"
"github.com/go-martini/martini"
"github.com/hnakamur/gaesessions"
"github.com/martini-contrib/binding"
"github.com/martini-contrib/render"
"github.com/martini-contrib/sessionauth"
"github.com/martini-contrib/sessions"
"net/http"
)
//var namespace string = "ahl"
func init() {
//store := sessions.NewCookieStore([]byte("secret123"))
store := gaesessions.NewDatastoreStore("", gaesessions.DefaultNonPersistentSessionDuration)
m := martini.Classic()
m.Use(render.Renderer())
// Default our store to use Session cookies, so we don't leave logged in
// users roaming around
//store.Options(sessions.Options{
// MaxAge: 0,
//})
m.Use(sessions.Sessions("my_session", store))
m.Use(sessionauth.SessionUser(GenerateAnonymousUser))
sessionauth.RedirectUrl = "/new-login"
sessionauth.RedirectParam = "new-next"
m.Get("/", func(r render.Render) {
r.HTML(200, "index", nil)
})
m.Get("/new-login", func(r render.Render) {
r.HTML(200, "login", nil)
})
m.Post("/new-login", binding.Bind(MyUserModel{}), func(session sessions.Session, postedUser MyUserModel, r render.Render, req *http.Request) {
// You should verify credentials against a database or some other mechanism at this point.
// Then you can authenticate this session.
//user := MyUserModel{}
user := MyUserModel{1, "testuser", "password", false}
//err := dbmap.SelectOne(&user, "SELECT * FROM users WHERE username = $1 and password = $2", postedUser.Username, postedUser.Password)
//if err != nil {
// r.Redirect(sessionauth.RedirectUrl)
// return
//} else {
err := sessionauth.AuthenticateSession(session, &user)
if err != nil {
r.JSON(500, err)
}
params := req.URL.Query()
redirect := params.Get(sessionauth.RedirectParam)
r.Redirect(redirect)
return
//}
})
m.Get("/private", sessionauth.LoginRequired, func(r render.Render, user sessionauth.User) {
r.HTML(200, "private", user.(*MyUserModel))
})
m.Get("/logout", sessionauth.LoginRequired, func(session sessions.Session, user sessionauth.User, r render.Render) {
sessionauth.Logout(session, user)
r.Redirect("/")
})
http.Handle("/", m)
}
Yes, it should be possible. The sessionauth package requires you to pass it a *sessions.Store, and there is a gaesessions package that can replace the default cookie/file stores: https://github.com/hnakamur/gaesessions
The sessionauth package has a full example (https://github.com/martini-contrib/sessionauth/blob/master/example/auth_example.go) - just replace sessions.NewCookieStore with gaesessions.NewDatastoreStore.
I'm new to Google AppEngine & to Go & to Web stacks - so it is very likely something I'm doing wrong here, but I can't figure it out :)
I'm trying to write a simple clock app in Go where the the server pushes to the client & a JS handler updates an HTML element. I'm using the TaskQueue api to reschedule the server side update, and I've changed the default queue frequency to be once per second. The server sends a payload string that contains the number of times the task URL has been hit & the current time. The hit count updates at a rate of 1 per second, as expected - but the time stamp barely changes. The log output I'm seeing in the dev_appserver console looks correct
INFO 2012-07-05 03:04:31,955 dev_appserver.py:2952] "POST /update HTTP/1.1" 200 -
INFO 2012-07-05 03:04:31,985 dev_appserver.py:2952] "POST /update HTTP/1.1" 200 -
INFO 2012-07-05 03:04:32,015 dev_appserver.py:2952] "POST /update HTTP/1.1" 200 -
INFO 2012-07-05 03:04:32,043 dev_appserver.py:2952] "POST /update HTTP/1.1" 200 -
I've pasted my code below (is a pastebin better?).
Cheers!
Brad
package clock
import (
"fmt"
"appengine"
"appengine/channel"
"appengine/datastore"
"appengine/taskqueue"
"html/template"
"net/http"
"time"
// "appengine/user"
)
type Greeting struct {
Author string
Content string
Date time.Time
}
func init() {
http.HandleFunc("/", root)
http.HandleFunc("/update", update)
}
type TemplateFiller struct {
Time string
Token string
}
var clientId string = "clockclient"
func root(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
q := datastore.NewQuery("Greeting").Order("-Date").Limit(10)
greetings := make([]Greeting, 0, 10)
if _, err := q.GetAll(c, &greetings); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
token, _ := channel.Create(c, clientId);
tf := TemplateFiller{time.Now().String(), token}
if err := guestbookTemplate.Execute(w, tf); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
reschedule(c)
}
func reschedule(c appengine.Context) {
t := taskqueue.NewPOSTTask("/update", map[string][]string{"token": {clientId}})
if _, err := taskqueue.Add(c, t, ""); err != nil {
return
}
}
var hits int = 0
func update(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
channel.Send(c, clientId, fmt.Sprintln(hits) + time.Now().Format(time.RFC3339))
hits = hits + 1
//channel.Send(c, clientId, time.Now().Format(time.RFC3339))
reschedule(c)
}
var guestbookTemplate = template.Must(template.New("").Parse(guestbookTemplateHTML))
const guestbookTemplateHTML = `
<html>
<script type="text/javascript" src="/_ah/channel/jsapi"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<body>
<div id="timediv">No time</div>
<script type="text/javascript">
$(document).ready(function(){
onMessage = function(msg) {
$("#timediv").html(msg.data);
}
channel = new goog.appengine.Channel('{{.Token}}');
socket = channel.open();
//socket.onopen = onOpened;
socket.onmessage = onMessage;
//socket.onerror = onError;
//socket.onclose = onClose;
});
</script>
</body>
</html>
`
The dev_server queue and timing behaves nothing like how the production server behaves (timing wise).
It is a very bad idea to depend on the queue to give you a constat and stable timer interup.