Go
Contents:
Platform.sh supports building and deploying applications written in Go using Go modules. They are compiled during the Build hook phase, and support both committed dependencies and download-on-demand.
Supported versions
Grid | Dedicated |
---|---|
|
None available |
To specify a Go container, use the type
property in your .platform.app.yaml
.
type: 'golang:1.15'
Deprecated versions
The following container versions are also available. However, due to their lack of Go module support and the difficulties in supporting the GOPATH during the Platform.sh build they are not recommended.
- 1.10
- 1.8
- 1.9
Go modules
The recommended way to handle Go dependencies on Platform.sh is using Go module support in Go 1.11 and later. That allows the build process to use go build
directly without any extra steps, and you can specify an output executable file of your choice. (See the examples below.)
Platform.sh variables
Platform.sh exposes relationships and other configuration as environment variables. To make them easier to access you should use the provided Config Reader library. Most notably, it allows a program to determine at runtime what HTTP port it should listen on and what the credentials are to access other services.
package main
import (
_ "github.com/go-sql-driver/mysql"
psh "github.com/platformsh/gohelper"
"net/http"
)
func main() {
p, err := psh.NewPlatformInfo()
if err != nil {
panic("Not in a Platform.sh Environment.")
}
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
// ...
})
http.ListenAndServe(":"+p.Port, nil)
}
Building and running the application
Assuming your go.mod
and go.sum
files are present in your repository, the application may be built with a simple go build
command that will produce a working executable. You can then start it from the web.commands.start
directive. Note that the start command must run in the foreground. Should the program terminate for any reason it will be automatically restarted.
The following basic .platform.app.yaml
file is sufficient to run most Go applications.
name: app
type: golang:1.14
hooks:
build: |
# Modify this line if you want to build differently or use an alternate name for your executable.
go build -o bin/app
web:
upstream:
socket_family: tcp
protocol: http
commands:
# If you change the build output in the build hook above, update this line as well.
start: ./bin/app
locations:
/:
# Route all requests to the Go app, unconditionally.
# If you want some files served directly by the web server without hitting Go, see
# https://docs.platform.sh/configuration/app/web.html
allow: false
passthru: true
disk: 1024
Note that there will still be an Nginx proxy server sitting in front of your application. If desired, certain paths may be served directly by Nginx without hitting your application (for static files, primarily) or you may route all requests to the Go application unconditionally, as in the example above.
Accessing services
To access various services with Go, see the following examples. The individual service pages have more information on configuring each service.
package examples
import (
"fmt"
"github.com/bradfitz/gomemcache/memcache"
psh "github.com/platformsh/config-reader-go/v2"
gomemcache "github.com/platformsh/config-reader-go/v2/gomemcache"
)
func UsageExampleMemcached() string {
// Create a NewRuntimeConfig object to ease reading the Platform.sh environment variables.
// You can alternatively use os.Getenv() yourself.
config, err := psh.NewRuntimeConfig()
checkErr(err)
// Get the credentials to connect to the Solr service.
credentials, err := config.Credentials("memcached")
checkErr(err)
// Retrieve formatted credentials for gomemcache.
formatted, err := gomemcache.FormattedCredentials(credentials)
checkErr(err)
// Connect to Memcached.
mc := memcache.New(formatted)
// Set a value.
key := "Deploy_day"
value := "Friday"
err = mc.Set(&memcache.Item{Key: key, Value: []byte(value)})
// Read it back.
test, err := mc.Get(key)
return fmt.Sprintf("Found value <strong>%s</strong> for key <strong>%s</strong>.", test.Value, key)
}
package examples
import (
"context"
"fmt"
psh "github.com/platformsh/config-reader-go/v2"
mongoPsh "github.com/platformsh/config-reader-go/v2/mongo"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"time"
)
func UsageExampleMongoDB() string {
// Create a NewRuntimeConfig object to ease reading the Platform.sh environment variables.
// You can alternatively use os.Getenv() yourself.
config, err := psh.NewRuntimeConfig()
checkErr(err)
// Get the credentials to connect to the Solr service.
credentials, err := config.Credentials("mongodb")
checkErr(err)
// Retrieve the formatted credentials for mongo-driver.
formatted, err := mongoPsh.FormattedCredentials(credentials)
checkErr(err)
// Connect to MongoDB using the formatted credentials.
ctx, _ := context.WithTimeout(context.Background(), 10*time.Second)
client, err := mongo.Connect(ctx, options.Client().ApplyURI(formatted))
checkErr(err)
// Create a new collection.
collection := client.Database("main").Collection("starwars")
// Clean up after ourselves.
err = collection.Drop(context.Background())
checkErr(err)
// Create an entry.
res, err := collection.InsertOne(ctx, bson.M{"name": "Rey", "occupation": "Jedi"})
checkErr(err)
id := res.InsertedID
// Read it back.
cursor, err := collection.Find(context.Background(), bson.M{"_id": id})
checkErr(err)
var name string
var occupation string
for cursor.Next(context.Background()) {
document := struct {
Name string
Occupation string
}{}
err := cursor.Decode(&document)
checkErr(err)
name = document.Name
occupation = document.Occupation
}
return fmt.Sprintf("Found %s (%s)", name, occupation)
}
package examples
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
psh "github.com/platformsh/config-reader-go/v2"
sqldsn "github.com/platformsh/config-reader-go/v2/sqldsn"
)
func UsageExampleMySQL() string {
// Create a NewRuntimeConfig object to ease reading the Platform.sh environment variables.
// You can alternatively use os.Getenv() yourself.
config, err := psh.NewRuntimeConfig()
checkErr(err)
// The 'database' relationship is generally the name of the primary SQL database of an application.
// That's not required, but much of our default automation code assumes it.
credentials, err := config.Credentials("database")
checkErr(err)
// Using the sqldsn formatted credentials package.
formatted, err := sqldsn.FormattedCredentials(credentials)
checkErr(err)
db, err := sql.Open("mysql", formatted)
checkErr(err)
defer db.Close()
// Force MySQL into modern mode.
db.Exec("SET NAMES=utf8")
db.Exec(\x60SET sql_mode = 'ANSI,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,
NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,
NO_AUTO_CREATE_USER,ONLY_FULL_GROUP_BY'\x60)
// Creating a table.
sqlCreate := \x60
CREATE TABLE IF NOT EXISTS PeopleGo (
id SERIAL PRIMARY KEY,
name VARCHAR(30) NOT NULL,
city VARCHAR(30) NOT NULL)\x60
_, err = db.Exec(sqlCreate)
checkErr(err)
// Insert data.
sqlInsert := \x60
INSERT INTO PeopleGo (name, city) VALUES
('Neil Armstrong', 'Moon'),
('Buzz Aldrin', 'Glen Ridge'),
('Sally Ride', 'La Jolla');\x60
_, err = db.Exec(sqlInsert)
checkErr(err)
table := \x60<table>
<thead>
<tr><th>Name</th><th>City</th></tr>
</thead>
<tbody>\x60
var id int
var name string
var city string
rows, err := db.Query("SELECT * FROM PeopleGo")
if err != nil {
panic(err)
} else {
for rows.Next() {
err = rows.Scan(&id, &name, &city)
checkErr(err)
table += fmt.Sprintf("<tr><td>%s</td><td>%s</td><tr>\n", name, city)
}
table += "</tbody>\n</table>\n"
}
_, err = db.Exec("DROP TABLE PeopleGo;")
checkErr(err)
return table
}
package examples
import (
"database/sql"
"fmt"
_ "github.com/lib/pq"
psh "github.com/platformsh/config-reader-go/v2"
libpq "github.com/platformsh/config-reader-go/v2/libpq"
)
func UsageExamplePostgreSQL() string {
// Create a NewRuntimeConfig object to ease reading the Platform.sh environment variables.
// You can alternatively use os.Getenv() yourself.
config, err := psh.NewRuntimeConfig()
checkErr(err)
// The 'database' relationship is generally the name of the primary SQL database of an application.
// It could be anything, though, as in the case here where it's called "postgresql".
credentials, err := config.Credentials("postgresql")
checkErr(err)
// Retrieve the formatted credentials.
formatted, err := libpq.FormattedCredentials(credentials)
checkErr(err)
// Connect.
db, err := sql.Open("postgres", formatted)
checkErr(err)
defer db.Close()
// Creating a table.
sqlCreate := \x60
CREATE TABLE IF NOT EXISTS PeopleGo (
id SERIAL PRIMARY KEY,
name VARCHAR(30) NOT NULL,
city VARCHAR(30) NOT NULL);\x60
_, err = db.Exec(sqlCreate)
checkErr(err)
// Insert data.
sqlInsert := \x60
INSERT INTO PeopleGo(name, city) VALUES
('Neil Armstrong', 'Moon'),
('Buzz Aldrin', 'Glen Ridge'),
('Sally Ride', 'La Jolla');\x60
_, err = db.Exec(sqlInsert)
checkErr(err)
table := \x60<table>
<thead>
<tr><th>Name</th><th>City</th></tr>
</thead>
<tbody>\x60
var id int
var name string
var city string
// Read it back.
rows, err := db.Query("SELECT * FROM PeopleGo")
if err != nil {
panic(err)
} else {
for rows.Next() {
err = rows.Scan(&id, &name, &city)
checkErr(err)
table += fmt.Sprintf("<tr><td>%s</td><td>%s</td><tr>\n", name, city)
}
table += "</tbody>\n</table>\n"
}
_, err = db.Exec("DROP TABLE PeopleGo;")
checkErr(err)
return table
}
package examples
import (
"fmt"
psh "github.com/platformsh/config-reader-go/v2"
amqpPsh "github.com/platformsh/config-reader-go/v2/amqp"
"github.com/streadway/amqp"
"sync"
)
func UsageExampleRabbitMQ() string {
// Create a NewRuntimeConfig object to ease reading the Platform.sh environment variables.
// You can alternatively use os.Getenv() yourself.
config, err := psh.NewRuntimeConfig()
checkErr(err)
// Get the credentials to connect to RabbitMQ.
credentials, err := config.Credentials("rabbitmq")
checkErr(err)
// Use the amqp formatted credentials package.
formatted, err := amqpPsh.FormattedCredentials(credentials)
checkErr(err)
// Connect to the RabbitMQ server.
connection, err := amqp.Dial(formatted)
checkErr(err)
defer connection.Close()
// Make a channel.
channel, err := connection.Channel()
checkErr(err)
defer channel.Close()
// Create a queue.
q, err := channel.QueueDeclare(
"deploy_days", // name
false, // durable
false, // delete when unused
false, // exclusive
false, // no-wait
nil, // arguments
)
body := "Friday"
msg := fmt.Sprintf("Deploying on %s", body)
// Publish a message.
err = channel.Publish(
"", // exchange
q.Name, // routing key
false, // mandatory
false, // immediate
amqp.Publishing{
ContentType: "text/plain",
Body: []byte(msg),
})
checkErr(err)
outputMSG := fmt.Sprintf("[x] Sent '%s' <br>", body)
// Consume the message.
msgs, err := channel.Consume(
q.Name, // queue
"", // consumer
true, // auto-ack
false, // exclusive
false, // no-local
false, // no-wait
nil, // args
)
checkErr(err)
var received string
var wg sync.WaitGroup
wg.Add(1)
go func() {
for d := range msgs {
received = fmt.Sprintf("[x] Received message: '%s' <br>", d.Body)
wg.Done()
}
}()
wg.Wait()
outputMSG += received
return outputMSG
}
package examples
import (
"fmt"
psh "github.com/platformsh/config-reader-go/v2"
gosolr "github.com/platformsh/config-reader-go/v2/gosolr"
solr "github.com/rtt/Go-Solr"
)
func UsageExampleSolr() string {
// Create a NewRuntimeConfig object to ease reading the Platform.sh environment variables.
// You can alternatively use os.Getenv() yourself.
config, err := psh.NewRuntimeConfig()
checkErr(err)
// Get the credentials to connect to the Solr service.
credentials, err := config.Credentials("solr")
checkErr(err)
// Retrieve Solr formatted credentials.
formatted, err := gosolr.FormattedCredentials(credentials)
checkErr(err)
// Connect to Solr using the formatted credentials.
connection := &solr.Connection{URL: formatted}
// Add a document and commit the operation.
docAdd := map[string]interface{}{
"add": []interface{}{
map[string]interface{}{"id": 123, "name": "Valentina Tereshkova"},
},
}
respAdd, err := connection.Update(docAdd, true)
checkErr(err)
// Select the document.
q := &solr.Query{
Params: solr.URLParamMap{
"q": []string{"id:123"},
},
}
resSelect, err := connection.CustomSelect(q, "query")
checkErr(err)
// Delete the document and commit the operation.
docDelete := map[string]interface{}{
"delete": map[string]interface{}{
"id": 123,
},
}
resDel, err := connection.Update(docDelete, true)
checkErr(err)
message := fmt.Sprintf(\x60Adding one document - %s<br>
Selecting document (1 expected): %d<br>
Deleting document - %s<br>
\x60, respAdd, resSelect.Results.NumFound, resDel)
return message
}
Project templates
Platform.sh offers a project templates for Go applications using the structure described above. It can be used as a starting point or reference for building your own website or web application.
Basic Go
This template provides the most basic configuration for running a custom Go project using Go modules. It demonstrates the Platform.sh `config-reader` library and connecting to a MariaDB instance. It can be used to build a very rudimentary application but is intended primarily as a documentation reference.
Go is a statically typed, compiled language with an emphasis on easy concurrency and network services.
Features:
- Go 1.15
- MariaDB 10.4
- Automatic TLS certificates
- Git module-based build
View the repository on GitHub.
Beego
This template demonstrates building the Beego framework for Platform.sh using Go modules. It includes a minimalist application skeleton that demonstrates how to connect to a MariaDB server. It is intended for you to use as a starting point and modify for your own needs.
Beego is a popular web framework written in Go.
Features:
- Go 1.14
- MariaDB 10.4
- Automatic TLS certificates
- Git module-based build
View the repository on GitHub.
Echo
This template demonstrates building the Echo framework for Platform.sh using Go modules. It includes a minimalist application skeleton that demonstrates how to connect to a MariaDB server. It is intended for you to use as a starting point and modify for your own needs.
Echo is a lightweight, minimalist web framework written in Go.
Features:
- Go 1.14
- MariaDB 10.4
- Automatic TLS certificates
- Git module-based build
View the repository on GitHub.
Elastic APM with Kibana
This template builds Elastic APM (Application Performance Monitoring) with a Kibana front-end. It is intended as a complete self-contained monitoring solution, although authentication needs to be configured for your specific application.
Features:
- Elasticsearch 7.2
View the repository on GitHub.
Gin
This template demonstrates building the Gin framework for Platform.sh using Go modules. It includes a minimalist application skeleton that demonstrates how to connect to a MariaDB server for data storage. It is intended for you to use as a starting point and modify for your own needs.
Gin is a lightweight web framework written in Go that emphasizes performance.
Features:
- Go 1.14
- MariaDB 10.4
- Git module-based build
View the repository on GitHub.
Hugo
This template provides a basic Hugo skeleton. All files are generated at build time, so at runtime only static files need to be served. The Hugo executable itself is downloaded during the build hook. You can specify the version to use by updating the `.platform.app.yaml` file. It also includes a minimal template to get you started, but you are free to replace it with your own template.
Hugo is a static site generator written in Go, using Go's native template packages for formatting.
Features:
- Go 1.15
- Automatic TLS certificates
- Hugo downloaded on the fly during build
View the repository on GitHub.
Mattermost
This template builds Mattermost on Platform.sh, configuring the deployment through user-defined environment variables. The Mattermost binary is downloaded on the fly during the build step. It includes a PostgreSQL database and Elasticsearch for indexing, both of which come pre-configured.
Mattermost is an open-source messaging framework written in Go and React.
Features:
- Go 1.14
- PostgreSQL 12
- * Elasticsearch 7.2
View the repository on GitHub.