baker

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Mar 12, 2020 License: MIT Imports: 6 Imported by: 0

README

   ____        _                                
  | __ )  __ _| | _____ _ __      __ _  ___   
  |  _ \ / _  | |/ / _ \ '__|    / _  |/ _ \  
  | |_) | (_| |   <  __/ |   _  | (_| | (_) | 
  |____/ \__,_|_|\_\___|_|  (_)  \__, |\___/  
                                 |___/

Introduction

Baker.go is a dynamic http reverse proxy with battery included. It has docker driver to listen to events coming from docker and register services to receive incoming traffic.

Features

Baker.go was rewritten based on previous baker. The code is simpler and more easy to be modified.

Heare are couple of main features of Baker.go:

  • Docker driver to listen to Docker's events
  • Has exposed a driver interface which can be easily hook to other orchestration engines
  • Dyanmic configurations, no need to restrat reverse proxy in order to change the configration
  • Uses only Standard Library
  • It can be use as library as it has implemented http.Handler interface
  • Highly extendable as most of the componets have exposed interfaces
  • Middleware like feature to change the incoming traffic
  • Round-Robin loadbalancing by default
  • Automatically updates and creates SSL certificates using Let's Encrypt

Usage

In order to use Baker.go, it needs to be accessed to /var/run/docker.sock file descriptor. The following docker-compose file contains enough logic to keep it started:

version: '3.5'

services:
  service1:
    image: alinz/baker.go:latest

    environment:
      # enables ACME system
      - BAKER_ACME=NO
      # folder location which holds all certification
      - BAKER_ACME_PATH=/acme/cert
      # log level of baker. Debug, Warn, Info, Error are the values for level
      - BAKER_LOG_LEVEL=Debug

    ports:
      - '80:80'
      - '443:443' # if BAKER_ACME set to YES, this port needs to be available

    networks:
      - baker

    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./acme/cert:/acme/cert

networks:
  baker:
    name: baker_net
    driver: bridge

if automatice SSL certificate is desiered, BAKER_ACME should be set to YES. Once the docker-compose.yml is launches, the following content message should be displayed:

service1_1  | 
service1_1  |   ____        _                                
service1_1  |  | __ )  __ _| | _____ _ __      __ _  ___   
service1_1  |  |  _ \ / _  | |/ / _ \ '__|    / _  |/ _ \  
service1_1  |  | |_) | (_| |   <  __/ |   _  | (_| | (_) | 
service1_1  |  |____/ \__,_|_|\_\___|_|  (_)  \__, |\___/  
service1_1  |                                 |___/
service1_1  | Version: vx.x.x
service1_1  | Git Hash: xxxxxxxxxxxxxxxxxxxxx
service1_1  | https://github.com/alinz/baker.go

NOTE: depends on log level, the content of stdout should be different.

Since Baker.go listens to docker's events, any containers that deploy to docker engine will be registered to Baker.go. In order to configure the registration process, docker's label is being used.

There are 3 custom labels which described all requirements:

  • baker.enable

    containers need to explicitly say that they want to be registered at Baker.go

  • baker.network

    describes which network name Baker.go is running. This is essential for baker to route the traffic

  • baker.service.port

    describes which ports this container is listening to

  • baker.service.ping

    describes which path Baker.go should use to check the health check and get the dynamic configuration

A sample example of docker-compose.yml for a service is as follows:

version: '3.5'

services:
  service1:
    image: baker/service:latest

    labels:
      - 'baker.enable=true'
      - 'baker.network=baker_net'
      - 'baker.service.port=8000'
      - 'baker.service.ping=/config'

    networks:
      - baker

networks:
  baker:
    external:
      name: baker_net

Now, the service needs to have a custom /config path that returns the following JSON, this path will be hit by Baker.go every 10 seconds.

[
  {
    "domain": "api.example.com",
    "path": "/v1*",
    "ready": true,
    "rules": [
      {
        "name": "path_replace",
        "config": {
          "search": "/v1",
          "replace": "",
          "times": 1
        }
      }
    ]
  },
  {
    "domain": "example.com",
    "path": "/*",
    "ready": true
  }  
]

This configuration tells Baker.go that there are 2 domains, api.example.com/v1* and example.com/* which I want to receive the incoming traffic from. The rules also saying that if a request comming as api.example.com/v1/users/1, replace /v1 to /. So the container will receive the request path as /users/1

As Library

Since Baker.go has been implemented http.Hanlder interface, it can be used inside your go application as library.

Here are the things need to be done:

First you need to registered all rules that your application needed.

  import "github.com/alinz/baker.go/pkg/rule"

	// register all rules
	rule.Register(
		&rule.PathReplaceRegistry{},
		// next rule here
	)

A custom rule can be created by creating 2 structs, one for registration and one for execution.


// Registrar interface for registring your rule with `rule.Register`
type Registrar interface {
	Name() string
	CreateHandler() Handler
}

// Hanlder is the execution of rule. Everytime a rule loaded via config path, it first validates by calling Valid method
type Handler interface {
	Valid() error
	ApplyRule(r *http.Request)
}

Once rules have been registred, a watcher needs to be initialized. Watcher is an interface from driver.Watcher


// Watcher defines how driver should react
type Watcher interface {
	// Container gets the next availabel container
	// this is a blocking calls
	Container() *baker.Container
}

Here is an example of docker's watcher:


// initialize custim watcher
dockerWatcher := docker.New(docker.DefaultClient, docker.DefaultAddr)
err := dockerWatcher.Start()
if err != nil {
	panic(err)
}
  

once a watcher is started, an engine can be created


// initialize engine based on watcher
engine := engine.New(dockerWatcher)
// start the engine, if anything goes wrong, this method will
// panic
engine.Start()
  

Engine is implemented http.Handler interface and can be used as a handler to a custom routing library.

Documentation

Index

Constants

View Source
const (
	ErrDomainNotFound        = errors.Value("domain not found")
	ErrPathNotFound          = errors.Value("path not found")
	ErrServiceNotInitialized = errors.Value("service not initialized")
	ErrTargetNotFound        = errors.Value("target not found")
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Config added in v0.1.0

type Config struct {
	Domain string        `json:"domain"` // could be example.com. www.example.com, api.example.com
	Path   string        `json:"path"`   // could be /v1/system, /v1/system*
	Ready  bool          `json:"ready"`
	Rules  rule.Handlers `json:"rules"`
}

Config is a configuration which can be sent by service

type Container

type Container struct {
	ID         string        // Unique ID generated by container orcestration
	Active     bool          // Active show wwheather container is added or removed
	RemoteAddr addr.Endpoint // RemoteAddr is IP:PORT which container is listening to
	ConfigPath string        // ConfigPath is the path to extract config data
	Err        error         // Err if error detected by orcestration system, it pass it here

}

Container is piece of object which contains information about Container

type Domain added in v0.1.0

type Domain struct {
	// contains filtered or unexported fields
}

Domain is a single unit which contains all the paths

func (*Domain) Add added in v0.1.0

func (d *Domain) Add(container *Container, config *Config) error

Add inserts/updates path for given container

func (*Domain) Get added in v0.1.0

func (d *Domain) Get(path string) (*Service, error)

Get gets any service base on matching path the matching path is based on trie

func (*Domain) Remove added in v0.1.0

func (d *Domain) Remove(containerID string, path string) error

Remove service from domain

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service struct

func (*Service) Add added in v0.1.0

func (s *Service) Add(container *Container, config *Config) error

Add creates target object based on container and config and adds it into Service's targets map.

func (*Service) Get added in v0.1.0

func (s *Service) Get() (*Target, error)

Get returns a Target based on balancer strategy. Currently, round-robin is being used Refer to internal/balanc package

func (*Service) Remove added in v0.1.0

func (s *Service) Remove(containerID string) error

Remove removes container id from service

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is contains all registered domains

func NewStore added in v0.1.0

func NewStore() *Store

NewStore creates new Store

func (*Store) Add added in v0.1.0

func (s *Store) Add(container *Container, config *Config) error

Add starts the chain of adding a new container

func (*Store) Get added in v0.1.0

func (s *Store) Get(domain string) (*Domain, error)

Get gets domain object. ErrDomainNotFound is otherwise

func (*Store) Remove added in v0.1.0

func (s *Store) Remove(container *Container) error

Remove container in tree

type Target added in v0.1.0

type Target struct {
	Container *Container
	Config    *Config
}

Target is a wrapper for both container and config

Directories

Path Synopsis
cmd
baker command
examples
service1 command
service2 command
internal
pkg

Jump to

Keyboard shortcuts

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