value to be passed to the fileName of appengine/file.Delete () is? - google-app-engine

I want to know is fileName to pass when you delete a file of gcs from gae/go.
Although passed "/gs/{bucketname}/{filename}", error message "RPC error UNKNOWN_ERROR:" is returned
package main
import (
"appengine"
"appengine/file"
"net/http"
)
func handle(w http.ResponseWriter, r *http.Request) {
c := appengine.NewContext(r)
file.Delete(c, "/gs/{bucketname}/{filename}")
}

I'm not an expert on Go but to work with Cloud Storage you need the Google Cloud Storage Go Client Library as indicated here [1].
Take a look at this sample code [2], more specifically to the deleteFiles() fuction. You can see that the function to the delete files is DeleteObject [3].
[1] https://cloud.google.com/appengine/docs/go/storage#google_cloud_storage
[2] https://cloud.google.com/appengine/docs/go/googlecloudstorageclient/getstarted
[3] http://godoc.org/google.golang.org/cloud/storage#DeleteObject

Related

Parse multipart form on Google App Engine

A project I'm working on depends on having a service hosted on Google App Engine parse from SendGrid. The following code is an example of what we're doing:
package sendgrid_failure
import (
"net/http"
"fmt"
"google.golang.org/appengine"
"google.golang.org/appengine/log"
)
func init() {
http.HandleFunc("/sendgrid/parse", sendGridHandler)
}
func sendGridHandler(w http.ResponseWriter, r *http.Request) {
ctx := appengine.NewContext(r)
err := r.ParseMultipartForm(-1)
if err != nil {
log.Errorf(ctx, "Unable to parse form: %v", err)
}
fmt.Fprint(w, "Test.")
}
When SendGrid POSTs its multipart form, the console shows similar to the following:
2018/01/04 23:44:08 ERROR: Unable to parse form: open /tmp/multipart-445139883: no file writes permitted on App Engine
App Engine doesn't allow you to read/write files, but Golang appears to need it to parse. Is there an App Engine specific library to parse multipart forms, or should we be using a different method from the standard net/http library entirely? We're using the standard go runtime.
The documentation for ParseMultipartForm says:
The whole request body is parsed and up to a total of maxMemory bytes of its file parts are stored in memory, with the remainder stored on disk in temporary files.
The server attempts to write all files to disk because the application passed -1 as maxMemory. Use a value larger than the size of the files you expect to upload.

Cross platform go code for appengine

What is the GO appropriate way to create a FetchUrl/GetURL function that works from the command line and works from google app engine with its custom way to fetch a url.
I have basic code that fetches and processes some data on a URL. I want to be able to call it from code I use on my desktop, and code deployed to app engine.
Hopefully thats clear, if not please let me know and Ill clarify.
If you have some code which works both on local machine and on AppEngine environment, you have nothing to do.
If you need to do something which should or must be done differently on AppEngine, then you need to detect the environment and write different code for the different environments.
This detection and code selection is easiest done using build constraints. You can put a special comment line in the beginning of your .go file, and it may or may not be compiled and run depending on the environment.
Quoting from The Go Blog: The App Engine SDK and workspaces (GOPATH):
The App Engine SDK introduces a new build constraint term: "appengine". Files that specify
// +build appengine
will be built by the App Engine SDK and ignored by the go tool. Conversely, files that specify
// +build !appengine
are ignored by the App Engine SDK, while the go tool will happily build them.
So for example you can have 2 separate .go files, one for AppEngine and one for local (non-AppEngine) environment. Define the same function in both (with same parameter list), so no matter in which environment the code is built, the function will have one declaration. We will use this signature:
func GetURL(url string, r *http.Request) ([]byte, error)
Note that the 2nd parameter (*http.Request) is only required for the AppEngine (in order to be able to create a Context), so in the implementation for local env it is not used (can even be nil).
An elegant solution can take advantage of the http.Client type which is available in both the standard environment and in AppEngine, and which can be used to do an HTTP GET request. An http.Client value can be acquired differently on AppEngine, but once we have an http.Client value, we can proceed the same way. So we will have a common code that receives an http.Client and can do the rest.
Example implementation can look like this:
url_local.go:
// +build !appengine
package mypackage
import (
"net/http"
)
func GetURL(url string, r *http.Request) ([]byte, error) {
// Local GetURL implementation
return GetClient(url, &http.Client{})
}
url_gae.go:
// +build appengine
package mypackage
import (
"google.golang.org/appengine"
"google.golang.org/appengine/urlfetch"
"net/http"
)
func GetURL(url string, r *http.Request) ([]byte, error) {
// Appengine GetURL implementation
ctx := appengine.NewContext(r)
c := urlfetch.Client(ctx)
return GetClient(url, c)
}
url_common.go:
// No build constraint: this is common code
package mypackage
import (
"net/http"
)
func GetClient(url string, c *http.Client) ([]byte, error) {
// Implementation for both local and AppEngine
resp, err := c.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
return body, nil
}
You could get some clues in the golang/appengine project itself.
For instance, its remote_api/client.go provides the client for connecting remotely to a user's production application.

c.Infof undefined (type context.Context has no field or method Infof) google.golang.org/appengine/log error

In the Go Runtime i used the method c.Infof to log messages , but it fails to compile with the following error
c.Infof undefined (type context.Context has no field or method Infof) .
The Error clearly tells that the app engine context returned from c := appengine.NewContext(r) is of type context.Context and it doesnt have a method c.Infof on it. But contrary to this the documentation in https://godoc.org/google.golang.org/appengine/log suggests that this method exists . Another point to note , The method existed on the context returned by "appengine" (import "appengine" ) package , and this doesnt seem to exist on the context returned by the new package google.golang.org/appengine , what is c.Infof equivalent on the new Context of type context.Context returned by package "google.golang.org/appengine" ?
The example in the package documentation is not correct.
Use the log package functions to write to the App Engine log. Here's the corrected example:
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)
}
The example in the package documentation was copied from the App Engine Classic package, but not updated to use the new functions. I suggest reporting this to the App Engine Team.

How to send 204 No Content with Go http package?

I built a tiny sample app with Go on Google App Engine that sends string responses when different URLs are invoked. But how can I use Go's http package to send a 204 No Content response to clients?
package hello
import (
"fmt"
"net/http"
"appengine"
"appengine/memcache"
)
func init() {
http.HandleFunc("/", hello)
http.HandleFunc("/hits", showHits)
}
func hello(w http.ResponseWriter, r *http.Request) {
name := r.Header.Get("name")
fmt.Fprintf(w, "Hello %s!", name)
}
func showHits(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%d", hits(r))
}
func hits(r *http.Request) uint64 {
c := appengine.NewContext(r)
newValue, _ := memcache.Increment(c, "hits", 1, 0)
return newValue
}
According to the package docs:
func NoContent(w http.ResponseWriter, r *http.Request) {
// Set up any headers you want here.
w.WriteHeader(http.StatusNoContent) // send the headers with a 204 response code.
}
will send a 204 status to the client.
Sending 204 response from your script means your instance still need to run and cost you money. If you are looking for a caching solution. Google got it and it's called Edge Cache.
You only need to response with the following headers and Google will automatically cache your response in multiple servers nearest to the users (that is, replying with 204). This greatly enhance your site's speed and reduce instance cost.
w.Header().Set("Cache-Control", "public, max-age=86400")
w.Header().Set("Pragma", "Public")
You can adjust the max-age, but do it wisely.
By the way, it seems billing must be enabled in order to use Edge Cache

Google GO: routing request handling mystical declarations?

I'm dorking around with Google GO for the first time. I've extended the "hello world" application to try to have paths defined in the init section. Here's what I've done so far:
package hello
import (
"fmt"
"net/http"
)
func init() {
http.HandleFunc("/service", serviceHandler)
http.HandleFunc("/site", siteHandler)
http.HandleFunc("/", handler)
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello, there")
}
func serviceHandler( w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "this is Services")
}
func siteHandler( w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "this is Sites")
}
Only the handler() callback is ever executed -- the others are ignored. E.g.: http://myserver/service/foo prints Hello, there. I had hoped that it would be this is Services.
Is there a better way to do service routing? Ideally, I would expect these to be separate scripts anyway, but it looks like Go has only one script, based on the fact that the app.yaml requires a special string _go_app in the script declaration.
Thanks!
According to the documentation at: http://golang.org/pkg/net/http/#ServeMux
path specs that do not have a trailing slash only match that path exactly. Add a slash to the end like so: http.HandleFunc("/service/", serviceHandler) and it will work as you expect.

Resources