Tuesday, 13 February, 2018 UTC


Summary

So you’ve been developing an application using the Go programming language with a team of developers for your organization. Best practice would say that you need to create proper tests and develop a continuous integration and deployment strategy.
If you’ve been keeping up with my tutorials, you’ll remember that I wrote about creating a continuous deployment pipeline with Node.js and Jenkins. This time around we’re going to change things up with the development technology and CI / CD service.
We’re going to see how to continuously deploy a Golang application that interacts with Couchbase with the popular Travis CI service.
The goal of this tutorial is to help you create a Golang application that communicates with Couchbase. This application will have proper unit tests that will be used when Travis CI is triggered. If the tests are successful in Travis CI, the application will be automatically deployed to some remote server with SSH.
As a prerequisite, you will need to have at least one Couchbase Server instance available to you. This Couchbase Server instance will be used during deployment.
Developing an Application with Go and Couchbase NoSQL
We’ll be creating a fresh Golang project for this example. Somewhere in your $GOPATH create a file called main.go and include the following:
package main

import (
	"fmt"
	"os"

	gocb "gopkg.in/couchbase/gocb.v1"
)

type Person struct {
	Type      string `json:"type"`
	Firstname string `json:"firstname"`
	Lastname  string `json:"lastname"`
}

var bucket *gocb.Bucket

func GetPersonDocument(key string) (interface{}, error) {
	var data interface{}
	_, err := bucket.Get(key, &data)
	if err != nil {
		return nil, err
	}
	return data, nil
}

func CreatePersonDocument(key string, data interface{}) (interface{}, error) {
	_, err := bucket.Insert(key, data, 0)
	if err != nil {
		return nil, err
	}
	return data, nil
}

func main() {
	fmt.Println("Starting the application...")
	cluster, _ := gocb.Connect("couchbase://" + os.Getenv("DB_HOST"))
	cluster.Authenticate(gocb.PasswordAuthenticator{Username: os.Getenv("DB_USER"), Password: os.Getenv("DB_PASS")})
	bucket, _ = cluster.OpenBucket(os.Getenv("DB_BUCKET"), "")
	fmt.Println(GetPersonDocument("8eaf1065-5bc7-49b5-8f04-c6a33472d9d5"))
	CreatePersonDocument("blawson", Person{Type: "person", Firstname: "Brett", Lastname: "Lawson"})
}
The above code is simple, but will change significantly as we progress in this tutorial. For now, let’s figure out what it does.
Because we’re using Couchbase, we need to have obtained the Go SDK. This can be installed by executing the following:
go get gopkg.in/couchbase/gocb.v1
With the Go SDK installed, we can add it to our project imports.
The project will have two very simple functions. There will be a function for retrieving a document and a function for creating a document. Each document will have the Person model as defined in the struct.
func main() {
	fmt.Println("Starting the application...")
	cluster, _ := gocb.Connect("couchbase://" + os.Getenv("DB_HOST"))
	cluster.Authenticate(gocb.PasswordAuthenticator{Username: os.Getenv("DB_USER"), Password: os.Getenv("DB_PASS")})
	bucket, _ = cluster.OpenBucket(os.Getenv("DB_BUCKET"), "")
	fmt.Println(GetPersonDocument("8eaf1065-5bc7-49b5-8f04-c6a33472d9d5"))
	CreatePersonDocument("blawson", Person{Type: "person", Firstname: "Brett", Lastname: "Lawson"})
}
When the application starts, it will connect to a Couchbase cluster as specified by environment variables. Likewise an RBAC account will be authenticated and a bucket will be opened.
After the database is good to go, we first get a document based on an id:
func GetPersonDocument(key string) (interface{}, error) {
	var data interface{}
	_, err := bucket.Get(key, &data)
	if err != nil {
		return nil, err
	}
	return data, nil
}
Assuming the document exists, it will be returned. After the document is retrieved, we insert a new document into the database.
func CreatePersonDocument(key string, data interface{}) (interface{}, error) {
	_, err := bucket.Insert(key, data, 0)
	if err != nil {
		return nil, err
	}
	return data, nil
}
Now you might be wondering, why we are creating functions for this at all. I mean we aren’t doing anything special beyond using the actual SDK.
Use your imagination on these functions. Assume they are something complex because we’ll be writing test cases for them. They are simple in this example to remain easy to understand.
Designing Unit Tests with Mock Data
We have a foundation to our project, so we can start thinking about writing test cases for our application. Before we start writing tests, let’s make a few changes to the main.go file that we had just created.
Open the main.go file and include the following:
package main

import (
	"fmt"
	"os"

	gocb "gopkg.in/couchbase/gocb.v1"
)

type BucketInterface interface {
	Get(key string, value interface{}) (gocb.Cas, error)
	Insert(key string, value interface{}, expiry uint32) (gocb.Cas, error)
}

type Database struct {
	bucket BucketInterface
}

type Person struct {
	Type      string `json:"type"`
	Firstname string `json:"firstname"`
	Lastname  string `json:"lastname"`
}

var bucket BucketInterface

func (d Database) GetPersonDocument(key string) (interface{}, error) {
	var data interface{}
	_, err := d.bucket.Get(key, &data)
	if err != nil {
		return nil, err
	}
	return data, nil
}

func (d Database) CreatePersonDocument(key string, data interface{}) (interface{}, error) {
	_, err := d.bucket.Insert(key, data, 0)
	if err != nil {
		return nil, err
	}
	return data, nil
}

func main() {
	fmt.Println("Starting the application...")
	var database Database
	cluster, _ := gocb.Connect("couchbase://" + os.Getenv("DB_HOST"))
	cluster.Authenticate(gocb.PasswordAuthenticator{Username: os.Getenv("DB_USER"), Password: os.Getenv("DB_PASS")})
	database.bucket, _ = cluster.OpenBucket(os.Getenv("DB_BUCKET"), "")
	fmt.Println(database.GetPersonDocument("8eaf1065-5bc7-49b5-8f04-c6a33472d9d5"))
	database.CreatePersonDocument("blawson", Person{Type: "person", Firstname: "Brett", Lastname: "Lawson"})
}
Notice that there are some slight changes to the above code.
When writing tests for functions that interact with your database, it probably isn’t a good idea to test against your actual database. Instead we’re going to want to use mock data. However, we’re going to want to write tests without jumping through too many hoops.
To test easily, we can create an interface that represents our Couchbase bucket. In our main code, we’ll use an actual bucket and in our test code we’ll use a mock bucket.
type BucketInterface interface {
	Get(key string, value interface{}) (gocb.Cas, error)
	Insert(key string, value interface{}, expiry uint32) (gocb.Cas, error)
}

type Database struct {
	bucket BucketInterface
}
In our example, the interface for our bucket only includes two of the many possible SDK functions. This interface matches what we’d see in the actual SDK.
The two functions that we had created are now part of the Database class.
Create a main_test.go file in your project and make sure it contains the following code:
package main

import (
	"encoding/json"
	"os"
	"testing"

	"github.com/mitchellh/mapstructure"
	gocb "gopkg.in/couchbase/gocb.v1"
)

type MockBucket struct{}

var testdatabase Database

func convert(start interface{}, end interface{}) error {
	bytes, err := json.Marshal(start)
	if err != nil {
		return err
	}
	err = json.Unmarshal(bytes, end)
	if err != nil {
		return err
	}
	return nil
}

func (b MockBucket) Get(key string, value interface{}) (gocb.Cas, error) {
	switch key {
	case "nraboy":
		err := convert(Person{Type: "person", Firstname: "Nic", Lastname: "Raboy"}, value)
		if err != nil {
			return 0, err
		}
	default:
		return 0, gocb.ErrKeyNotFound
	}
	return 1, nil
}

func (b MockBucket) Insert(key string, value interface{}, expiry uint32) (gocb.Cas, error) {
	switch key {
	case "nraboy":
		return 0, gocb.ErrKeyExists
	}
	return 1, nil
}

func TestMain(m *testing.M) {
	testdatabase.bucket = &MockBucket{}
	os.Exit(m.Run())
}

func TestGetPersonDocument(t *testing.T) {
	data, err := testdatabase.GetPersonDocument("nraboy")
	if err != nil {
		t.Fatalf("Expected `err` to be `%s`, but got `%s`", "nil", err)
	}
	var person Person
	mapstructure.Decode(data, &person)
	if person.Type != "person" {
		t.Fatalf("Expected `type` to be %s, but got %s", "person", person.Type)
	}
}

func TestCreatePersonDocument(t *testing.T) {
	_, err := testdatabase.CreatePersonDocument("blawson", Person{Type: "person", Firstname: "Brett", Lastname: "Lawson"})
	if err != nil {
		t.Fatalf("Expected `err` to be `%s`, but got `%s`", "nil", err)
	}
}
The above code is really where the magic comes in for our project. It uses a mixture of code from the parent file and custom test code.
Take the following for example:
type MockBucket struct{}

var testdatabase Database
When testing, we aren’t going to be working with an actual Couchbase bucket, so we have to create our own dummy struct. We will be using the Database data structure from the parent file and it uses the BucketInterface.
When the tests start, we can create our dummy bucket:
func TestMain(m *testing.M) {
	testdatabase.bucket = &MockBucket{}
	os.Exit(m.Run())
}
Now, since our mock bucket doesn’t have any functions like the real bucket, we have to create the functions as defined in our interface. In other words, we have to create an Insert and a Get function.
Starting with the Insert function:
func (b MockBucket) Insert(key string, value interface{}, expiry uint32) (gocb.Cas, error) {
	switch key {
	case "nraboy":
		return 0, gocb.ErrKeyExists
	}
	return 1, nil
}
Our test is going to be simple. We’re going to try to insert any document except one with a key of nraboy. The test will either succeed or fail based on the key.
The Get function works in a similar fashion.
func convert(start interface{}, end interface{}) error {
	bytes, err := json.Marshal(start)
	if err != nil {
		return err
	}
	err = json.Unmarshal(bytes, end)
	if err != nil {
		return err
	}
	return nil
}

func (b MockBucket) Get(key string, value interface{}) (gocb.Cas, error) {
	switch key {
	case "nraboy":
		err := convert(Person{Type: "person", Firstname: "Nic", Lastname: "Raboy"}, value)
		if err != nil {
			return 0, err
		}
	default:
		return 0, gocb.ErrKeyNotFound
	}
	return 1, nil
}
When trying to get a document, we are expecting a key of nraboy. If the correct key was provided, return some data marshalled by our convert function, otherwise return an error.
Again, these are mock versions of what we’d find in the Go SDK for bucket operations. With the interface functions created, we can write our two test functions.
func TestGetPersonDocument(t *testing.T) {
	data, err := testdatabase.GetPersonDocument("nraboy")
	if err != nil {
		t.Fatalf("Expected `err` to be `%s`, but got `%s`", "nil", err)
	}
	var person Person
	mapstructure.Decode(data, &person)
	if person.Type != "person" {
		t.Fatalf("Expected `type` to be %s, but got %s", "person", person.Type)
	}
}
The TestGetPersonDocument will use our testdatabase variable which uses the mock bucket. However, it uses the GetPersonDocument from the main.go file. Remember, our parent function could be way more complex, but the database stuff is now mock.
The result of our function will be decoded into the Person data structure using the mapstructure package. More information on using the mapstructure package can be read about here.
func TestCreatePersonDocument(t *testing.T) {
	_, err := testdatabase.CreatePersonDocument("blawson", Person{Type: "person", Firstname: "Brett", Lastname: "Lawson"})
	if err != nil {
		t.Fatalf("Expected `err` to be `%s`, but got `%s`", "nil", err)
	}
}
The TestCreatePersonDocument function follows the same strategy. We are using the mock bucket, but the very real CreatePersonDocument function. The Insert will use what we had created in our test.
There isn’t too much to testing a Golang application that includes database interaction. Your best bet is to create an interface and use your own custom mock versions of the functions and data.
Creating a YAML Configuration for Travis CI
So we have our Go application with a few unit tests. Now we need to be able to use Travis CI to automatically test our application and deploy it if successful.
Travis CI functions off a YAML configuration file. Take the following configuration for example:
language: go
go:
    - 1.8
before_script:
    - sudo apt-get install -qq sshpass
script:
    - go test -v
    - go build
after_success:
    - sshpass -p $SSH_PASS scp -o stricthostkeychecking=no golang-ci-example $SSH_USER@$SSH_HOST:~/
    - sshpass -p $SSH_PASS ssh -o StrictHostKeyChecking=no $SSH_USER@$SSH_HOST DB_HOST=$DB_HOST DB_USER=$DB_USER DB_PASS=$DB_PASS DB_BUCKET=$DB_BUCKET ./golang-ci-example
env:
    global:
notifications:
    on_success: never
    on_failure: never
The above configuration says that we are using Go 1.8. Before we start executing scripts, we need to download a necessary package. We need sshpass which allows us to SSH with plaintext passwords. In the end you’ll probably want to use keys, but for this example it is fine.
After sshpass has downloaded, we need to run our tests. This is done in the scripts section. After the first script finishes, we can build our project.
Assuming both scripts have completed successfully, we want to focus on the deployment.
after_success:
    - sshpass -p $SSH_PASS scp -o stricthostkeychecking=no golang-ci-example $SSH_USER@$SSH_HOST:~/
    - sshpass -p $SSH_PASS ssh -o StrictHostKeyChecking=no $SSH_USER@$SSH_HOST DB_HOST=$DB_HOST DB_USER=$DB_USER DB_PASS=$DB_PASS DB_BUCKET=$DB_BUCKET ./golang-ci-example
Two different commands are executed for deployment. The first command will copy the binary to a remote server and the second command will run that file on the remote server.
Both deployment commands reference environment variables in the CI / CD process that are later passed to the server.
Now, you’ll notice that we haven’t set any environment variables. These variables are sensitive and shouldn’t be plain text within our Git repository. Instead we can use secrets with Travis CI.
Download the travis CLI tool by following the Travis CI documentation. With the tool installed, execute the following:
travis encrypt SSH_HOST=YOUR_REMOTE_SERVER --add
travis encrypt SSH_USER=YOUR_REMOTE_SERVER_USER --add
travis encrypt SSH_PASS=YOUR_REMOTE_SERVER_PASS --add

travis encrypt DB_HOST=YOUR_COUCHBASE_SERVER --add
travis encrypt DB_USER=YOUR_COUCHBASE_SERVER_USER --add
travis encrypt DB_PASS=YOUR_COUCHBASE_SERVER_PASS --add
travis encrypt DB_BUCKET=YOUR_COUCHBASE_BUCKET --add
Instead of adding all your information as plain text in the YAML file, it is encrypted and can be understood by Travis CI. The YAML configuration it adds will look something like this:
env:
    global:
        - secure: C/5OS6jDmcubVA/AKmapQDON9YH4+eK31geLzcCz6DjoG0zDCWX3qas+y3thij3sQ6E+54fUGZO/8AeoVykfyEfhLRICgVw4d7RjGbjPFqDABVOFTB+HXxRNk5mNWGz/V07r4N/tnM+XMzUBKxOtgNNL7G0SjEPS9ormJGUbH3pYwnlTpzOUXHzvJ0x8Ynk7vC3Bfg5X0ALpNtjK7Wc0WCOCxhnDraUcWiBR3kp43QPac9nw/eiSicLz1Y88ToxrXF2o6aPJqCrP0cpEDAMmtB89j14OIUDmaULssPk6WWWAeE4CvaqOlRG+gr5j2z9UgEfO6DoaIcL6TZQDCOQQix1f+5AMfexOFVn6oUO8O4x1wtfYwZwnajZ+O4R3ZKb1nHg1rY16m8O61yB0wdoaSP6NVn/lyGkwmz7tNWGhGoxMgvG9U6yZuOYqwseP67y2y99zg94kR4WNF7NeH5YvaZo53d5pgPRMy/0w15wJRJFgK3ef87EgMRWCJqVTEOw0bo+SQRPvDZRa7MgraxoZRkW2FKqYShWb7Ch0FlTTAY3ZCfynG+mXcDjMbaDWaUkea8KIfb7r7/a8vDF5bXwNGDkQkbcZEMh46CnZxiLWgqsviBlISWuQAN1VgBLSI/O/uhJPHJzNGyhotDQf9W6Y+DiKqW40z3rAf4ZB9Li4+b4=
        - secure: Pcv4MzEb0LD3LV9dtfevGwdI8wgkSAkgSq+lMByx6eQOLvA9mc4PVFin6A2Dc1FpObSuZfDLhr/eGuFg8TbaY7KncPc7r/OrPsSb9xCN9xPRlHP7mC+3t1NWjdGt6lVI647hlFQBs+MO8OHEyUquE6XHUZ8mnYe13TychlOM1VG+M2luyA46hEBMrUTId6CgsytqqCGWNqqZ5HBY01/ne4qxQUX3LI3dPUThFjpKqYqLBx2S7FlMDhOYL01XPQ+yQ2CWWCzCau876IM5Mzey73brj4XE6SerFi6tBxLFtk7LxVa+6JvSi5tLZZ91kf6o6P7WD6O0q4T48BeUg5ewGFbX0+JF5YO7OA0ZAT3n8rKsfZCjiAO4GtHRcG0wV1Uh+etJSoWqR0+dZk4g7L9lIOOKwQiTl+7PoMYW3yhvbilLjcJ/yektQJe/NjSJFHqYH7EqeqbR3IAzjkxepZl6vwY08acoTdW918E27Lj7+mjUuUJSxT8aY8+DWQg6+RLSYXjLXsZ1m77VsYQ6gpEyXJYrnPnybVHX57GC7kXGonM4UAbtN3D80Qr37/OX7i06MnJTa2xgkUVl/HK7HfFlQsaX8yIC1zzaNHwNVKxIIGu/hLM6GAPpF49y2ictmVbu0UKABcak4GdiF7zhrOF91p0Qui/xcIcz2/f+XBFU6vE=
        - secure: KHaZKnIBoJmsVMYQUFAFAFbbqquM/Stht0AZdsB6sbBvYnz2XLAaxpL89tBGp7IEibXW9/C4t034oAQPaeF6AuODboetgvtjxGTOi+vcBqAj9l07IlajH89yi2Tx6zTq8DvyCKBjaNu0ocD70rplqEBZwa/mttqw+YXKv29C4eunusho/2Jcq+uztTJdiGZsy73NRAFFSLLezzTEQ4sLYc9v/PYOiP/BwcPzemj1DpQr8awly7LtQSj6QdTrjLotQmVTvdXsFHUcPsQdRlLKcy781Q5oP95inujB37vcusOjTru6bXYUzS8hMz/JkUX4bocvd4yf1SLEmBbocHu6X4wmuI+JxD8WSBnkbJHjnWDPyEm5VRzq2rT+9hoe83XN9wm+im+bEfQUmYmyttCDmCogl+vaX337JaHrKESVtBlBRPUOlS6xM/6gi8HT7EbG4Ir1qqNVGVbbdKemD+odcQqNQDVREkqSCOD0rdmXjJSpb8BSH5ftqF3lvfsVyO9fUkzxZfhL7O6pkiH4LGqwU1RaMKhlixQBhrjtRTLfluHR3/9tZxKhyQHc/pXV+wWHig2ziKVhk02c82fNAD5J4lpf8QK5wzWqBBnsPQ20/gRXoqi3QkF66ZUKTy8r/03NHeJtSraWOYE5/6kzNSCcnL245l6r73fc5IJCa1+Ijmc=
        - secure: o/N//U7qZJyERaGi89H00rdgma1ACL2O1vEgn/qZdJx0OQk02Sj1w9H0pMawMcWlLCAlZZzwdnwkH604p76iSiTffknEZ0kfr/YWise1ACeO2NLxMcn/o+ch9Hjpuc9vWkMsFRqDv9WFYd6xRcAz8BRAeavhe3kykcBOacY7ZLGIc2NQarlhKXPY4xpPEhAhjnCrmsMq1ppCbnINAu8dLx4lyTCGF4FHYwPsCUylfgGI4Kr5gO1ikg7mLV1rL0RYmKm0MQrQbOuUrlnXdRaTbsBKhMBWDUjlmg+aeC4DPOcSTEvJ3YEhDk8bLDttr8RRhEUlgGkNNN21cpCBvbhSD6OaZdwFF6PFyd63CBSgi/abv36iewjgGwjKoHdptkSiMQzYBe4qzjD4MupYwrgj8TibfW+Y21vzNtY6BnpXnuetCD41NgY5PZyLfQkvi6YcIi6MW9x4vc87EPhO8JcTMvchoQgg9LVv+BoA3Wf8qz1aRiLnctEeOwAdix6bGxh3JZqR+5HYvLSCdWrnwhKbWBgju3icYZ9odDnLfrDqPhcsPfU4BBUh+xU1khuv/n3Vy27y81iqpWBb+iGH59IL+FPZDMeGETGZiAkDHiJxu9s3fr1VtK/B8WGKqrGbKU3GOOm0JEfZJHDRmchDLOJoRlbubcljDnfSAFEMy19XWEI=
        - secure: l0/Z0FTznp9w2pmr1QxjTxtQsnaVUHZNDk+EBWyiTepxkkgH8ohDqM5jocaZQyMZmV3WUNG1phBD1S+MaF+ou1BIfHGKmnOm7OXujskgdvtvNzqFKCfkh30qcaipzM653VrZdatnU9zwzbgURWSkPXQ5B05yrs/AH0sBOaIgl1J7JOXV6RZAOLkBahjMTSKc34ckBNt1Q6WpIKnzYqPdYpLpS8nmDZVszC7Fo4ykXsm7lloFJ43Ii+5D9culyGpq6z2eFvpALEYnixSkl3zUFNwuf1CIdsFBJThWdbYMlO6R2vi0N8QszMSLMc+Ry+HHm54E6IhbrReKCV8Rnho1DF4+0bmAbj7xNIl9uAFOOhuONLYzQLH+x7FxyjKJx8EiirgofO8EUdfEkXauIr+hufYboepMGjGIcUiO+VUXHo1w+o6GIHVGddxtnBU8ryctiZwUOkLJpR6ex6tltuJCFw5YzXvcLOf0+ZYoNZAFctc0DA0hpIAyv1ZoVGEpX+3Mi14zv7LYkLhPKnjc4MIm6mkQfMcBsVjK1aGow6ypDpQFcHKTNXOxNepD8uM9afoR11hhy8jPD8VtWNTn9vW+Szocoe65EbWmz3V0YDLmfCgm0Ltg4IanJOH6cEV8uUNYjHuj4X731dnmBa/vlGDyhSeLCjzcuSkukmqVZhLUpMI=
        - secure: VQE+9BAya6XAUBsGKF5kqCm1L6eO2mmaM9VmEx0u+xObUygjq32FN3i/mQ6cs26SQJQ07XDBnwsnAhTY3/u9ZP6hJrpY/2Q2dOMuUHU6kx5gJhAGfnza8yv4cedCl54dSRf5zH5YdO7kb8jwhBFvjMJrbzCmnCuFrWbFKgLqOCqqykL51o48Q33kIbkbj+t8tmVu0JyyQ8kpBpgVgpilPWWeR2xtKkKEskU4Shpywjy02maOxgt6BHQFumdzPIT/h2Bwb8qh74k8CrqQwvmA9Vxcg2qHCH9G8NEXbwd4BFxncET8FJfgqvCuuu3EWiT46EhG2xgI9/1cgzlf16ZMnQIzhuSs6fnOlNlK2PJtijovVlQDxVpXJ863oWVpln5V3vfqGllJ9lEaF4TVuVQG3jsXGfX5j2BMwiLMvMF+o0Ym/YUne84jCoJ5+ImJa9o0I+8qC32QV+OElJ/BtCuB1koiCYz4cKcl5i/6+0J0TSQKzj3/RlZKndAgnAoT1qBQwL4oHG5ZWXJWO85BYwy/9jBroGLASHR70oDPkmGR4Msgm+B/qq520LljG72jA220IBnNK8LKXQpzn0rPaN9VwHz2HoSb0O7x6eagKzEoL0AiMOsIMyD1kA1xLWX6RAP8T1Mo1PP2Gv8lzcRCKcj5SMlw5I3ZLcT14EW4gxW1l8s=
        - secure: RDgyGNdr2kpQzYT4hsCLH/UyoxdIG83r37Fqg0J6rc7mY3u6sj62Btp20Z1f7Hh9R2e6tPZJcSY6NtT4fj6OcGQgHASAPjmYl6Fn+MFOcwIfS6Y2Pu8BGQlzTpDzeSqg/JHNeStD29yervJuPGshrrZKeTECkV1PwHQMbRS+uENVoO4VM11dQXa+GWTznyrjItIKUabHSJ520QbheAnHJ1/NkUnc824nTvwZoQ4zw9YgAE/UvvHfyAZ4+3HjUsUaI1ZiD/UHk2VjZsllp7dTPlW33a1MZL2MJD+wWtPmi6xVeNF2pIvX4TnhQYpQZ1oP5U6UZrFgLxe9doIpSgswPObR2pHUySg/Ts/jAY0O/JfZH+SH0tyzrUrllLm4KirkHgiLbjcHGlzvjuGAOdwUgrEmVser7x3kKnj6IQiE9iAqm/jzGsbS1M3zSY6Um1vyjyqPFXwF10HP7cvdA290Wrjrqov6o+H4N21ldBvZp1EQuyRsE8cPaVt8ik5ti0ZN9P/8/oZmvJsLHHGOoIkkusDy7m61H9GSNr1d52cPHiVJyEdKtQJPTRdoMirgz1wpJiY/OCHzifxkuVuf1bBo1IjVbC5aG+5Halx2NPX5QW8Lfl2xJMprA1rvNnFLXiaX8hZUVDrvhA/zNJFY8yu23pW0OInsJ7HAggUw+EHpaXM=
This is something you can keep in your Git repository and feel safe. An even safer approach would be to use public and private keys for SSH, but this is fine for this example.
When you push your project, assuming you’ve linked it in your Travis CI account, the application will test and deploy automatically.
The Significance of NoSQL with Continuous Deployment
We saw how to create tests that use mock data as a database. However, what is really convenient is that we don’t need to create upgrade scripts when our data changes. In a relational database you’d have to create alter scripts for your table. With Couchbase, if your data model needs to change, just change to struct for Person in the code.
Conclusion
You just saw how to use Travis CI to continuously deploy your Golang project that uses a Couchbase NoSQL database. During the continuous integration phase, the application is tested using mock data. When the tests pass and the project is built, it is pushed to a remote server and ran.
The example used in this tutorial can be found on GitHub. Just note that the remote server that the project is pushed to has been removed.
If you’re interested in learning more about Go with Couchbase, check out the Couchbase Developer Portal.
The post Continuously Deploying a Golang Application Using Travis CI appeared first on The Couchbase Blog.