Skip to content
Roman Azami edited this page May 1, 2019 · 5 revisions

Contents

  1. How to
  2. Installing the Atlas CLI
  3. Bootstrap an Application
  4. Creating a sample Contacts app
    1. Run bootstrap command to create project
    2. Navigating the project
    3. Understanding Configuration
    4. Running sample generated service
    5. Adding a new service
    6. Regenerating the protobuf
    7. Implementing new services
    8. Adding the database layer
    9. Running application

How to

This wiki walks you through how to use the Atlas-cli and some toolkit features to create a sample project. The project will be a simpler version of atlas-contacts-app. Please, view the atlas-contacts-app for a more complete example.

Goal:

  • Create a contacts application that allows you to save and retrieve contacts from a database
  • Learn how to use the bootstrap tool and flags
  • Understand the project structure and how to add new methods
  • Usage of the generated Makefile
  • How to run your application and send requests

Installing the Atlas CLI

The following steps will install the atlas binary to your $GOBIN directory.

$ go get github.com/infobloxopen/atlas-cli/atlas

You're all set! Alternatively, you can clone the repository and install the binary manually.

$ git clone https://github.com/infobloxopen/atlas-cli.git
$ cd atlas-cli
$ make

The atlas-cli tool and generated apps also depend on the golang dep tool. On OSX it can be installed like this:

$ brew install dep

Bootstrap an Application

Rather than build applications completely from scratch, you can leverage the command-line tool to initialize a new project. This will generate the necessary files and folders to get started.

$ atlas init-app -name=my-application
$ cd my-application

Flags

Here's the full set of flags for the init-app command.

Flag Description Required Default Value
name The name of the new application Yes N/A
db Bootstrap the application with PostgreSQL database integration No false
gateway Initialize the application with a gRPC gateway No false
health Initialize the application with internal health checks No false
pubsub Initialize the application with a atlas-pubsub example No false
registry The Docker registry where application images are pushed No ""

You can run atlas init-app --help to see these flags and their descriptions on the command-line.

Creating a sample Contacts app

  1. Run bootstrap command to create project

    atlas init-app -name "contacts-app" -gateway -db -health

    This will create a project with the name "contacts-app" that will contain a grpc gateway proxy which will allow your application to call REST endpoints, a database, and health checks.

  1. Navigating the project

    Directory Description
    cmd Contains config, gateway, and server files
    db Empty when initialized but needs to include database migration files if needed and test fixtures
    deploy Empty when initialized but needs to include kubernetes manifest files
    docker Dockerfiles for the application
    pkg Contains the proto file and the autogenerated sources by protobuf (Majority implementation goes here)
    vendor Local copies of external dependencies to satisfy imports of those dependencies
    Gopkg.toml Initialized by dep (dependency management tool for Go) contains all dependencies for your project
    Makefile Contains helpful commands to get your application running
    README.md Readme template for your project

  1. Understanding Configuration
    • Generated atlas projects use viper, a complete configuration solution that allows an application to run from different environments.
    • For a more detailed summary look at configuration
    • All the configurations for this project exist in cmd/server/config.go
    • By default it will autogenerate everything that was provided when bootstrapping the application
      • Since we passed the db flag we want to ensure that we have a postgres server running and have created a contacts_app database for our application (CREATE DATABASE contacts_app;)
      • Make any necessary changes inside config.go to ensure that database adresss and port is correct.
      • Note: atlas-cli auto generates a db for postgres that uses protoc-gen-gorm if you want to use another RDB you would have to implement it yourself. d

  1. Running sample generated service
    • Each project gets created with a sample service called version
    • All services are created in pkg/pb/service.proto
    • To run the project make sure your in the project direcory and run: go run cmd/server/*.go
    • This will run your project in localhost:8080/v1/contacts-app (based on config.go)
    • To call service:
        curl localhost:8080/contacts-app/v1/version
    
    • You will receive a response as follows:
        {"version":"0.0.1"}
    
    • If you don't receive this please check your config.go and make sure you using the correct address.

  1. Adding a new service
    • To add a new service we need to define the message and service inside the proto file.
    • Atlas projects use protocol buffers to define how the data should be structured
    • Lets first create a definition for a Contact object:
        // Contact represents a particular person in an contacts list.
        message Contact {
            // The id associated to the person
            int64 id = 1;
    
            // The first name of the person.
            string first_name = 2;
    
            // The middle name of the person.
            string middle_name = 3;
    
            // The last name of the person.
            string last_name = 4;
    
            // The person's primary email.
            string email = 5;
    
            // The home address of the person.
            string home_address = 6;
        }
    
    • Next, we need to create a service which needs a few things
      • A request
      • A response
      • Service definition
      • Service implementation
    • First, we need to define the response and request for our service. For our first example we will create a service that doesn't depend on a databse. We will create a service that will tell return your first name in reverse.
        message ReverseRequest {
            Contact payload = 1;
        }
    
        message ReverseResponse {
            string result = 1;
        }
    
    • Next, we need to define our service definition
      • Since we are using a grpc gateway proxy we need to define the endpoint for our service using 'option (google.api.http)'
      • For more info look at: GRPC Gateway
    rpc Reverse (ReverseRequest) returns (ReverseResponse) {å
        option (google.api.http) = {
            post: "/reverse"
            body: "payload"
        };
    }
    

  1. Regenerating the protobuf
    • Whenever we update our proto file we want to update our generated protobuf files to reflect the changes
    • make protobuf

  1. Implementing new services
    • Now that we have added a new service we want to write the implementation for it
    • Inside pkg/svc/zserver.go add the following:
    // Reverse returns the first name in reverse
    func (server) Reverse(ctx context.Context, req *pb.ReverseRequest) (*pb.ReverseResponse, error) {
        r := []rune(req.Payload.FirstName)
        for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
            r[i], r[j] = r[j], r[i]
        }
        return &pb.ReverseResponse{Result: string(r)}, nil
    }
    
    • Run your project again (go run cmd/server/*.go)
    • Check service:
    curl -X POST   http://0.0.0.0:8080/contacts-app/v1/reverse   -H /v1/reverse  -d '{  "first_name": "John",  "last_name": "Doe",
    "email": "[email protected]"
    }'
    
    • Congratulations for creating your first service!

  1. Adding the database layer
    • Atlas Projects are recommended to use protoc-gen-gorm a protobuf compiler plugin designed to generate GORM models and APIs for simple object persistence tasks.

    • When using protoc-gen-gorm we want to remember the following things:

      1. Add option (gorm.opts).ormable = true; to the messsages and services that you want to be auto generated.
      2. When adding the gorm model we need to add the new server inside the zserver.go file and update the grpc.go to include that server.
    • Update your Contacts definition as below to generate gorm models.

      // Contact represents a particular person in an contacts list.
      message Contact {
          option (gorm.opts).ormable = true;
          
          // The id associated to the person
          int64 id = 1;
      
          // The first name of the person.
          string first_name = 2;
      
          // The middle name of the person.
          string middle_name = 3;
      
          // The last name of the person.
          string last_name = 4;
      
          // The person's primary email.
          string email = 5;
      
          // The home address of the person.
          string home_address = 6;
      }
      
    • Next, we want to add all the CRUD definitions in the proto file

          message CreateContactRequest {
              Contact payload = 1;
          }
      
          message CreateContactResponse {
              Contact result = 1;
          }
      
          message ReadContactRequest {
              int64 id = 1;
          }
      
          message ReadContactResponse {
              Contact result = 1;
          }
      
          message UpdateContactRequest {
              Contact payload = 1;
          }
      
          message UpdateContactResponse {
              Contact result = 1;
          }
      
          message DeleteContactRequest {
              int64 id = 1;
          }
      
          message DeleteContactResponse {}
      
          // Inside service add: 
      
          option (gorm.server).autogen = true;
          
          // Use this method to create a contact information.
          rpc Create (CreateContactRequest) returns (CreateContactResponse) {
              option (google.api.http) = {
                  post: "/contacts"
                  body: "payload"
              };
          }
      
          // Use this method to read a contact information by identifier.
          rpc Read (ReadContactRequest) returns (ReadContactResponse) {
              option (google.api.http) = {
                  get: "/contacts/{id}"
              };
          }
      
          // Use this method to update a contact information.
          rpc Update (UpdateContactRequest) returns (UpdateContactResponse) {
              option (google.api.http) = {
                  put: "/contacts/{payload.id}"
                  body: "payload"
                  additional_bindings: {
                      patch: "/contacts/{payload.id}"
                      body: "payload"
                  }
              };
          }
      
          // Use this method to delete a particular contact.
          rpc Delete (DeleteContactRequest) returns (DeleteContactResponse) {
              option (google.api.http) = {
                  delete: "/contacts/{id}"
              };
              option (gorm.method).object_type = "Contact";
          }
      
      
    • Update the server in zserver.go

      func NewContactsServer(database *gorm.DB) (pb.ContactsAppServer, error) {
          return &contactsServer{&pb.ContactsAppDefaultServer{DB: database}}, nil
      }
      
      type contactsServer struct {
          *pb.ContactsAppDefaultServer
      }
      
    • Change the basic server to your specific server inside grpc.go

      // old: s, err := svc.NewBasicServer(db)
      s, err := svc.NewContactsServer(db)
      
    • Lastly, lets create a table in our database so that we can start using our new services

      CREATE TABLE contacts(
          id serial PRIMARY KEY,
          first_name VARCHAR (50) NOT NULL,
          middle_name VARCHAR (50),
          last_name VARCHAR (50) NOT NULL,
          email VARCHAR (260) UNIQUE NOT NULL,
          home_address VARCHAR (260)
      );
      
    • To test create:

    curl -X POST \
        http://0.0.0.0:8080/contacts-app/v1/contacts \
        -H 'Content-Type: application/json' \
        -d '{
        "first_name": "Roman",
        "last_name": "Azami",
        "email": "[email protected]"
    }'
    
    

  1. Running application
    • To run locally:
    go run cmd/server/*.go
    
    • To make docker image:
    make docker 
    
Clone this wiki locally