Google Cloud Run: A serverless platform for containerized applications
Tags: gcp, cloudrun, go, serverless
Published: 2023-10-08 03:33:42

Google has always been at the forefront of cloud innovation, and with its introduction of Cloud Run, it has once again demonstrated its commitment to providing top-tier cloud solutions for developers. In this blog post, we'll delve deep into the wonders of Google Cloud Run: what it is, its benefits, and how to get started.

Google Cloud Run is a fully managed compute platform that enables you to run containerized applications without provisioning or managing servers. Cloud Run scales your application automatically based on demand, so you can focus on building great applications without worrying about the infrastructure.

There are many benefits to using Cloud Run, including:

- __Serverless:__ Cloud Run takes care of all the infrastructure management for you, so you can focus on your code.
- __Scalability:__ Cloud Run scales your application automatically based on demand, so you can handle unexpected spikes in traffic without any downtime.
- __Portability:__ Cloud Run uses standard containers, so you can easily port your applications to and from Cloud Run.
- __Pay as You Go:__ With Cloud Run, you only pay for the compute time you use. If your application isn't running, you aren't charged. This not only makes it cost-effective but also ensures that you're not overspending on idle resources.
- __Developer Friendly:__ Cloud Run supports multiple programming languages, and with its integration with __Cloud Build__, developers can automatically build and deploy their applications directly from their source repositories.

## Use cases for Cloud Run
Cloud Run is a good choice for a wide range of applications, including:

- __Web applications:__ Cloud Run is a great way to run web applications, such as blogs, e-commerce sites, and content management systems.
- __Microservices:__ Cloud Run is a good choice for running microservices architectures, where each service is independently scalable and deployable.
- __Event-driven applications:__ Cloud Run can be used to build event-driven applications, such as real-time data processing and notification systems.
- __Machine learning applications:__ Cloud Run can be used to run machine learning applications, such as image recognition and natural language processing models.

[Click here](https://cloud.google.com/run/docs/overview/what-is-cloud-run) for more information, how to install and other steps.

## Example
### Tools
__Operating Systems:__ [Kubuntu](https://kubuntu.org/)
__IDE:__ [VSCode](https://code.visualstudio.com/) [Google Cloud Code](https://marketplace.visualstudio.com/items?itemName=GoogleCloudTools.cloudcode)
__Programming Language:__ [Go](https://go.dev/)
__Container Runtime:__ [Docker](https://www.docker.com/)

We will create a microservice with some endpoints called __patient-svc__, which will be part of a Hospital Management System

- Register new patient
- Update patient details
- Retrieve patient details

1. __Ctrl + P__ then type __> Cloud Run: New Application__.
2. Select the option __Cloud Run application__.
3. In this case select __Go: Cloud Run__.
4. Select the directory to save the project with the name __patient-svc__.
5. In the Cloud Code status bar, click the active project name.![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qui4r9u0y0hdeksq3qkm.png)

__main.go__
```go
package main

import (
	"encoding/json"
	"log"
	"net/http"
	"sync"
)

type Patient struct {
	ID      string `json:"id"`
	Name    string `json:"name"`
	Address string `json:"address"`
}

var patients = make(map[string]Patient)
var mu sync.Mutex

func RegisterPatient(w http.ResponseWriter, r *http.Request) {
	var p Patient
	if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	mu.Lock()
	patients[p.ID] = p
	mu.Unlock()

	w.WriteHeader(http.StatusCreated)
	json.NewEncoder(w).Encode(p)
}

func UpdatePatientDetails(w http.ResponseWriter, r *http.Request) {
	var p Patient
	if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	mu.Lock()
	if _, exists := patients[p.ID]; exists {
		patients[p.ID] = p
	} else {
		http.Error(w, "Patient not found", http.StatusNotFound)
		mu.Unlock()
		return
	}
	mu.Unlock()

	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(p)
}

func RetrievePatientDetails(w http.ResponseWriter, r *http.Request) {
	id := r.URL.Query().Get("id")
	if id == "" {
		http.Error(w, "ID is required", http.StatusBadRequest)
		return
	}

	mu.Lock()
	p, exists := patients[id]
	mu.Unlock()
	if !exists {
		http.Error(w, "Patient not found", http.StatusNotFound)
		return
	}

	json.NewEncoder(w).Encode(p)
}

func main() {
	http.HandleFunc("/register", RegisterPatient)
	http.HandleFunc("/update", UpdatePatientDetails)
	http.HandleFunc("/retrieve", RetrievePatientDetails)

	port := "8080"
	log.Printf("Server started on :%s", port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}
```
__main_test.go__
```go
func TestRegisterPatient(t *testing.T) {
    // Create a new request with a JSON payload
    reqBody := []byte(`{"id": "123", "name": "John Doe", "age": 35}`)
    req, err := http.NewRequest("POST", "/register", bytes.NewBuffer(reqBody))
    if err != nil {
        t.Fatal(err)
    }

    // Set the request header content type to JSON
    req.Header.Set("Content-Type", "application/json")

    // Create a response recorder to record the response
    rr := httptest.NewRecorder()

    // Call the RegisterPatient function with the response recorder and request
    handler := http.HandlerFunc(RegisterPatient)
    handler.ServeHTTP(rr, req)

    // Check the status code is 201 Created
    if status := rr.Code; status != http.StatusCreated {
        t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusCreated)
    }

    // Check the response body is what we expect
    expected := `{"id":"123","name":"John Doe","age":35}`
    if rr.Body.String() != expected {
        t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected)
    }
}

func TestUpdatePatientDetails(t *testing.T) {
    // Create a new request with a JSON payload
    reqBody := []byte(`{"id": "123", "name": "John Doe", "age": 36}`)
    req, err := http.NewRequest("PUT", "/update", bytes.NewBuffer(reqBody))
    if err != nil {
        t.Fatal(err)
    }

    // Set the request header content type to JSON
    req.Header.Set("Content-Type", "application/json")

    // Create a response recorder to record the response
    rr := httptest.NewRecorder()

    // Call the UpdatePatientDetails function with the response recorder and request
    handler := http.HandlerFunc(UpdatePatientDetails)
    handler.ServeHTTP(rr, req)

    // Check the status code is 200 OK
    if status := rr.Code; status != http.StatusOK {
        t.Errorf("handler returned wrong status code: got %v want %v", status, http.StatusOK)
    }

    // Check the response body is what we expect
    expected := `{"id":"123","name":"John Doe","age":36}`
    if rr.Body.String() != expected {
        t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected)
    }
}
```
### Debug
1. In the Cloud Code status bar, click the active project name.![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qui4r9u0y0hdeksq3qkm.png)
2. Select __Debug on Cloud Run Emulator__. If it is the first time a form will appear. This is how I have it configured.
![screenshot01](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6az6o145cx7dn800mh1e.png)
3. Debug View from the VSCode Code Editor

![vscode debug](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tpapvy1424va3gpm12k8.png)

4. Test the endpoints
```sh
$ curl -X POST -H "Content-Type: application/json" -d '{"id": "123", "name": "John Doe", "age": 35}' http://localhost:8080/register
{"id":"123","name":"John Doe","address":""}
```
```sh
$ curl -X PUT -H "Content-Type: application/json" -d '{"id": "123", "name": "John Doe", "age": 36}' http://localhost:8080/update
{"id":"123","name":"John Doe","address":""}
```
```sh
$ curl -X GET http://localhost:8080/retrieve?id=123
{"id":"123","name":"John Doe","address":""}
```
## Deploy
Refer to this guide for specific details https://cloud.google.com/run/docs/deploying

I present some screenshots of the details of my deployment using the Google Cloud Console.

![Create a Service](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hizh8ddpneq1izq6gyde.png)

![Logs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m1wcdblqzgros75cx9ev.png)

https://patient-svc-3v56nzrvtq-uc.a.run.app
```sh
curl -X POST -H "Content-Type: application/json" -d '{"id": "123", "name": "John Doe", "age": 35}' https://patient-svc-3v56nzrvtq-uc.a.run.app/register 
{"id":"123","name":"John Doe","address":""}
```

Google Cloud Run provides developers with a robust, scalable, and cost-effective platform to deploy containerized applications without the hassle of server management. As organizations continue to look for solutions that allow them to move fast and adapt to changing needs, platforms like Cloud Run will only become more pivotal. So, whether you're a startup or a well-established business, it might just be the right time to explore what Google Cloud Run can offer you!