Skip to content

samples/guestbook: add sample application #68

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Jun 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,13 @@

# Populated config files
tests/gcp/app/gcp-test.yaml

# Cryptographic keys
*.pem
*.json

# Terraform Temporary Files
*.tfstate
*.tfstate.backup
.terraform/
terraform.tfvars
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ require (
contrib.go.opencensus.io/exporter/stackdriver v0.0.0-20180421005815-665cf5131b71
github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20180321230639-1e456b1c68cb
github.com/aws/aws-sdk-go v1.13.20
github.com/aws/aws-sdk-go-v2 v0.0.0-20180526011417-ff1a530c3150
github.com/aws/aws-xray-sdk-go v1.0.0-rc.5
github.com/census-ecosystem/opencensus-go-exporter-aws v0.0.0-20180411051634-41633bc1ff6b
github.com/dnaeon/go-vcr v0.0.0-20180504081357-f8a7e8b9c630
github.com/fsnotify/fsnotify v1.4.7
Expand Down
120 changes: 120 additions & 0 deletions samples/guestbook/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# Guestbook Sample

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to have a few sentences summarizing what this sample does, e.g., "Guestbook is a sample application which records visitors' names. It also demonstrates a common (standard, conventional?) use of go-wire and go-cloud."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Guestbook is a sample application that records visitors' messages, displays a
cloud banner, and an administrative message. The main business logic is
written in a cloud-agnostic manner using MySQL, the generic blob API, and the
generic runtimevar API. All platform-specific code is set up by Wire.

## Prerequisites

You will need to install the following software to run this sample:

- [Go](https://golang.org/doc/install) and
[vgo](https://go.googlesource.com/vgo)
- [Docker](https://docs.docker.com/install/)
- [Terraform](https://www.terraform.io/intro/getting-started/install.html)
- [jq](https://stedolan.github.io/jq/download/)
- [gcloud CLI](https://cloud.google.com/sdk/downloads), if you want to use GCP
- [aws CLI](https://docs.aws.amazon.com/cli/latest/userguide/installing.html),
if you want to use AWS

## Building

`gowire` is not compatible with `vgo` yet, so you must run `vgo vendor`
first to download all the dependencies in `go.mod`. Running `gowire`
generates the Wire code.

```shell
# First time, for gowire.
$ vgo vendor

# Now build:
$ gowire && vgo build
```

## Running Locally

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again a quick summary here would be nice. "You will need to run a MySQL database using Docker and then migrate that database. Then, you can start the app locally" or something similar.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

You will need to run a local MySQL database server, set up a directory that
simulates a bucket, and create a local message of the day. `localdb.sh` is a
script that runs a temporary database using Docker:

```shell
./localdb.sh
```

In another terminal, you can run:

```shell
# Create the local bucket directory.
mkdir blobs

# Set a local Message of the Day
echo 'Hello, World!' > motd.txt

# Run the server.
./guestbook -env=local -bucket=blobs -motd_var=motd.txt
```

Your server should be running on http://localhost:8080/.

You can stop the MySQL database server with Ctrl-\. MySQL ignores Ctrl-C
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a very nice touch. I would have mashed ctrl-c endlessly otherwise.

(SIGINT).

## Running on Google Cloud Platform (GCP)

If you want to run this sample on GCP, you need to create a project, download
the gcloud SDK, and log in. You can then use Terraform, a tool for
initializing cloud resources, to set up your project. Finally, this sample
provides a script for building the Guestbook binary and deploying it to the
Kubernetes cluster created by Terraform.

```shell
gcloud auth application-default login
cd gcp
terraform init

# Terraform will prompt you for your GCP project ID, desired region,
# and desired zone.
terraform apply
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Granted, I know how to fix this myself by enabling the API (i.e., gcloud services enable container.googleapis.com) and adjusting IAM permissions, but it would be nice if README told me to do this or if the terraform config was updated to ensure permissions worked out of the box.

After running terraform apply on a vanilla setup against a fresh account, I get this:

3 error(s) occurred:

* google_project_service.storage: 1 error(s) occurred:

* google_project_service.storage: Error enabling service: failed to issue request: googleapi: Error 403: The caller does not have permission, forbidden
* google_project_service.sql: 1 error(s) occurred:

* google_project_service.sql: Error enabling service: failed to issue request: googleapi: Error 403: The caller does not have permission, forbidden
* google_project_service.container: 1 error(s) occurred:

* google_project_service.container: Error enabling service: Error waiting for apis ["container.googleapis.com"] to be enabled for enocom-dev: googleapi: Error 403: The caller does not have permission, forbidden

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ironically, I believe these messages come up because the services are already enabled. :\ Tracking bug is hashicorp/terraform-provider-google#1579


./deploy.sh
```

The deploy script will display the URL of your running service.

To clean up the created resources, run `terraform destroy` inside the `gcp`
directory using the same variables you entered during `terraform apply`.

## Running on Amazon Web Services (AWS)

If you want to run this sample on AWS, you need to set up an account, download
the AWS command line interface, and log in. You can then use Terraform, a tool
for initializing cloud resources, to set up your project. This will create an
EC2 instance you can connect to and run your binary, copying over the
configuration

```shell
aws configure
vgo build
cd aws
terraform init
terraform apply -var region=us-west-1

# SSH into the EC2 instance.
ssh "admin@$( terraform output instance_host )"
```

When you're connected to the server, run the server binary. Replace the
command-line flag values with values from the output of `terraform apply`.

```
AWS_REGION=us-west-1 ./guestbook -env=aws \
-bucket=... -db_host=... -motd_var=...
```

You can then visit the server at `http://INSTANCE_HOST:8080/`, where
`INSTANCE_HOST` is the value of `terraform output instance_host` run on your
local machine.

To clean up the created resources, run `terraform destroy` inside the `aws`
directory using the same variables you entered during `terraform apply`.
194 changes: 194 additions & 0 deletions samples/guestbook/aws/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

provider "aws" {
version = "~> 1.22"
region = "${var.region}"
}

provider "random" {
version = "~> 1.3"
}

# Firewalls

resource "aws_security_group" "guestbook" {
name_prefix = "guestbook"
description = "Sandbox for the Guestbook Go Cloud sample app."

ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Public SSH access"
}

ingress {
from_port = 8080
to_port = 8080
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Public HTTP access"
}

ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
self = true
description = "MySQL within group"
}

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
description = "All outgoing traffic allowed"
}
}

# SQL Database (RDS)

resource "random_string" "db_password" {
special = false
length = 20
}

resource "aws_db_instance" "guestbook" {
identifier_prefix = "guestbook"
engine = "mysql"
engine_version = "5.6.39"
instance_class = "db.t2.micro"
allocated_storage = 20
username = "root"
password = "${random_string.db_password.result}"
name = "guestbook"
publicly_accessible = true
vpc_security_group_ids = ["${aws_security_group.guestbook.id}"]
skip_final_snapshot = true

provisioner "local-exec" {
# TODO(light): Reuse credentials from Terraform.
command = "cat '${path.module}'/../schema.sql '${path.module}'/../roles.sql | '${path.module}'/provision-db.sh '${aws_db_instance.guestbook.address}' '${aws_security_group.guestbook.id}' guestbook '${random_string.db_password.result}'"
}
}

# Blob Storage (S3)

resource "aws_s3_bucket" "guestbook" {
bucket_prefix = "guestbook"
}

# Paramstore (SSM)

resource "aws_ssm_parameter" "motd" {
name = "${var.paramstore_var}"
type = "String"
value = "ohai from AWS"
}

# Compute (EC2)

resource "aws_iam_role" "guestbook" {
name_prefix = "guestbook"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Allow",
"Principal": {"Service": "ec2.amazonaws.com"},
"Action": "sts:AssumeRole"
}
}
EOF
}

resource "aws_iam_role_policy" "guestbook" {
name_prefix = "Guestbook-Policy"
role = "${aws_iam_role.guestbook.id}"

policy = <<EOF
{
"Version": "2012-10-17",
"Statement": {
"Effect": "Allow",
"Action": [
"s3:GetObject",
"ssm:DescribeParameters",
"ssm:GetParameter",
"ssm:GetParameters",
"xray:PutTraceSegments",
"xray:PutTelemetryRecords"
],
"Resource": "*"
}
}
EOF
}

resource "aws_iam_instance_profile" "guestbook" {
name_prefix = "guestbook"
role = "${aws_iam_role.guestbook.name}"
}

data "aws_ami" "debian" {
most_recent = true

filter {
name = "product-code"
values = ["55q52qvgjfpdj2fpfy9mb1lo4"]
}

filter {
name = "product-code.type"
values = ["marketplace"]
}

filter {
name = "architecture"
values = ["x86_64"]
}

owners = ["679593333241"]
}

resource "aws_key_pair" "guestbook" {
key_name_prefix = "guestbook"
public_key = "${var.ssh_public_key}"
}

resource "aws_instance" "guestbook" {
ami = "${data.aws_ami.debian.id}"
instance_type = "t2.micro"
vpc_security_group_ids = ["${aws_security_group.guestbook.id}"]
iam_instance_profile = "${aws_iam_instance_profile.guestbook.id}"
key_name = "${aws_key_pair.guestbook.key_name}"

connection {
type = "ssh"
user = "admin"
}

provisioner "file" {
source = "${path.module}/../guestbook"
destination = "/home/admin/guestbook"
}

provisioner "remote-exec" {
inline = ["chmod +x /home/admin/guestbook"]
}
}
44 changes: 44 additions & 0 deletions samples/guestbook/aws/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

output "region" {
value = "${var.region}"
description = "Region the resources were created in."
}

output "bucket" {
value = "${aws_s3_bucket.guestbook.id}"
description = "Name of the S3 bucket created to store images."
}

output "database_host" {
value = "${aws_db_instance.guestbook.address}"
description = "Host name of the RDS MySQL database."
}

output "database_root_password" {
value = "${random_string.db_password.result}"
sensitive = true
description = "Password for the root user of the RDS MySQL databse."
}

output "paramstore_var" {
value = "${var.paramstore_var}"
description = "Location of the SSM Parameter Store Message of the Day variable."
}

output "instance_host" {
value = "${aws_instance.guestbook.public_ip}"
description = "Address of the EC2 instance."
}
Loading