goa/

directory
v0.31.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 20, 2020 License: Apache-2.0

README

GOA OpenAPI Example

This example was created using goa framework. Using goa we have created a service provider that exposes a resource for managing 'Bottles' allowing the consumer to create and read bottles.

Overview

goa is a framework for building micro-services and REST APIs in Go using a unique design-first approach.

This example service provider APIs have been designed using goa's DSL and leveraging the goa framework the server, client, swagger documentation and a CLI were generated automatically. This is where the real power of goa resides, allowing service providers to focus on improving their services while still offering good integration capabilities.

The code containing the API design is located in the 'design' folder. This file contains all the information related to the API from where goa will feed and generate the code desired (e,g: server api, client lib, documentation, cli, etc). In this example, everything inside the api folder was auto-generated by goa excluding the design/design.go file.

Running the example goa API

This service is fully OpenAPI Terraform compliant and the swagger document can be found here.

Using make target 'faster approach'

A make target is provided to bring up the API service running in a docker container:

$ cd $GOPATH/github.com/dikhan/terraform-provider-openapi
$ DC_SERVICE=goa-service-provider-api make local-env

Similarly, a local-env-down is provided to shutdown the service:

$ make local-env-down
Manual approach

A dockerfile is provided to run the example in a container:

  • To build the container:
$ cd $GOPATH/github.com/dikhan/terraform-provider-openapi/examples/goa/api
$ docker build -t goa-service-provider-api .
  • To run the container:
$ docker run -p 9090:9090 goa-service-provider-api

Installing 'goa' OpenAPI Terraform provider

The following command will build the OpenAPI Terraform provider and install the terraform-provider-goa terraform provider plugin in Terraform's plugin installation directory:

$ cd $GOPATH/github.com/dikhan/terraform-provider-openapi/
$ PROVIDER_NAME="goa" make install

Running Terraform

Now that the terraform-provider-goa plugin is installed, we can go ahead and execute terraform passing the following env variables or if using the plugin configuration file the env variables won't be needed:

$ cd $GOPATH/github.com/dikhan/terraform-provider-openapi/examples/goa/
$ terraform init && OTF_VAR_goa_SWAGGER_URL="http://localhost:9090/swagger/swagger.yaml" OTF_INSECURE_SKIP_VERIFY=true terraform plan

Below is an output of the terraform execution:


$ cd $GOPATH/github.com/dikhan/terraform-provider-openapi/examples/goa
$ terraform init && OTF_VAR_goa_SWAGGER_URL="http://localhost:9090/swagger/swagger.yaml" terraform plan

Initializing provider plugins...

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.


------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  + goa_bottles.my_bottle
      id:      <computed>
      name:    "Name of bottle"
      rating:  "3"
      vintage: "2653"


Plan: 1 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

Running Terraform using make

Alternatively, a make target is also provided to achieve the same output by executing the following command:

$ cd $GOPATH/github.com/dikhan/terraform-provider-openapi/
$ TF_CMD=plan make run-terraform-example-goa

The above command will:

  • Compile, build and install the terraform-provider-goa terraform provider
  • Bring up the API service running in the docker container
  • Execute the Terraform command specified with the TF_CMD variable against the main.tf containing a resource provided by the goa service.

How to use GOA to design a service that is OpenAPI Terraform compliant?

The following describes the contents of this example and how to use goa to auto-generate code from the API design DSL.

Pre-requisites

  • Go go1.12.4
  • goa latest from master

Running goagen

The folder located at design folder contains the API design described using goa DSL. This folder is used by goa to auto-generate the desired files and ib this example the command used is as follows:

$ cd $GOPATH/github.com/dikhan/terraform-provider-openapi/examples/goa/api
$ goagen bootstrap -d github.com/dikhan/terraform-provider-openapi/examples/goa/api/design

Running the above command will auto-generate the application code, application scaffolding, swagger file and a client package and tool. In order to run goagen the binary has to be installed in your path, for that follow goa installation instructions)

The output displayed after running goagen will be something like:

app
app/contexts.go
app/controllers.go
app/hrefs.go
app/media_types.go
app/user_types.go
app/test
app/test/bottle_testing.go
main.go
bottle.go
tool/cellar-cli
tool/cellar-cli/main.go
tool/cli
tool/cli/commands.go
client
client/client.go
client/bottle.go
client/user_types.go
client/media_types.go
swagger
swagger/swagger.json
swagger/swagger.yaml

As you can see, goa can make you save a lot of time. The server side code, client library, API documentation and even a CLI was created for free by ust running one command. The server side code generated handles all the things related to transport and connections, however the actual logic needs to be implemented by the service provider of course.

In this example, the auto-generated bottle.go file has been updated to use an in-memory db (just a simple map) so we are able to use the terraform-provider-openapi against the API and manage the resources created.

Things to know when using goa and the terraform-provider-openapi

goa handles data types in a particular way, having two models that describe the request and the responses respectively:

  • Payload: content posted as part of the request. The request payload describes the shape of the request body.
  • MediaType: content returned as part of the response. A response media type defines the shape of response bodies.

This is a valid separation of concern when exposing the API models as requests body models are completely decoupled from response body models giving more flexibility on what is intended to be exposed for consumption as well as representation of the final object created. However, in order for terraform to know what values are computed and which ones are meant to be provided by the user when creating a resource, a readOnly flag needs to be attached to the attributes that are computed as described below:

	Attribute("id", String, "Unique bottle ID", func() {
		// This makes the id attribute read-only, this means that the value will be computed by the API and therefore parameter
		// is not expected from the user when invoking the API
		ReadOnly()
	})

The above will end up being rendered when executing goagen into the following definition:

definitions:
  BottlePayload:
    description: BottlePayload is the type used to create bottles
    example:
      id: Enim sapiente expedita sit.
      name: x
      rating: 4
      vintage: 2653
    properties:
      id:
        description: Unique bottle ID
        example: Enim sapiente expedita sit.
        readOnly: true // <-- This is the important bit, here terraform-provider-openapi will understand that this value is computed
        type: string

This way terraform will know how to differentiate between expected fields that should be populated by the user and expected fields that should be computed by the API, avoiding inecessary conflicts between the main.tf file and local terraform state.

Since writing both Payload and MediaType might be a bit redundant, goa has a reference DSL that basically will inherit the attribute properties from the data type being referened:

Reference(BottlePayload)

Below is a full example on how both Payload and MediaType can be described reducing as much as possible the boiler plate needed:

var BottlePayload = Type("BottlePayload", func() {
	Description("BottlePayload is the type used to create bottles")

	Attribute("id", String, "Unique bottle ID", func() {
		// This makes the id attribute read-only, this means that the value will be computed by the API and therefore parameter
		// is not expected from the user when invoking the API
		ReadOnly()
	})

	Attribute("name", String, "Name of bottle", func() {
		MinLength(1)
	})
	Attribute("vintage", Integer, "Vintage of bottle", func() {
		Minimum(1900)
	})
	Attribute("rating", Integer, "Rating of bottle", func() {
		Minimum(1)
		Maximum(5)
	})
	Required("name", "vintage", "rating")
})

var BottleMedia = MediaType("application/vnd.gophercon.goa.bottle", func() {
	TypeName("bottle")
	// Reusing BottlePayload reduces the boiler plate having to define attribute properties in one place only
	Reference(BottlePayload)

	Attributes(func() {
		Attribute("id")
		Attribute("name")
		Attribute("vintage")
		Attribute("rating")
		Required("id", "name", "vintage", "rating")
	})

	View("default", func() {
		Attribute("id")
		Attribute("name")
		Attribute("vintage")
		Attribute("rating")
	})
})

The design file contains a fill example for the reference.

Directories

Path Synopsis
api
app

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL