Skip to content

Commit

Permalink
Add Static service feature
Browse files Browse the repository at this point in the history
  • Loading branch information
alinz committed Aug 22, 2024
1 parent 143f83b commit 8ac57fa
Show file tree
Hide file tree
Showing 14 changed files with 158 additions and 46 deletions.
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,13 @@ Baker is a dynamic HTTP reverse proxy with a focus on extensibility and flexibil
- Automatic SSL certificate updates and creation using Let's Encrypt.
- Configurable rate limiter per domain and path.
- Prometheus metrics are available at `BAKER_METRICS_ADDRS/metrics`
- Static Configuration for those services that doesn't expose any config path

# Usage

First, we need to run Baker inside docker. The following `docker-compose.yml`

```yml
version: "3.5"

services:
baker:
image: ellato/baker:latest
Expand Down Expand Up @@ -61,15 +60,13 @@ services:

networks:
baker:
name: baker_net
name: baker
driver: bridge
```
Then for each service, the following `docker-compose` can be used. The only requirements are labels and networks. Make sure both baker and service have the same network interface

```yml
version: "3.5"
services:
service1:
image: service:latest
Expand All @@ -79,14 +76,16 @@ services:
- "baker.network=baker_net"
- "baker.service.port=8000"
- "baker.service.ping=/config"
- "baker.service.static.domain=xyz.example.com" # only define this if service is not dyanmic
- "baker.service.static.path=/*" # only define this if service is not dyanmic
networks:
- baker
networks:
baker:
external:
name: baker_net
name: baker
external: true
```

The service should expose a REST endpoint that returns a configuration. This endpoint acts as a health check and provides real-time configuration.
Expand Down
11 changes: 6 additions & 5 deletions action.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (
type Event struct {
Type EventType
Container *Container
Meta *MetaData
Endpoint *Endpoint
Result chan struct {
Container *Container
Expand All @@ -28,7 +29,7 @@ type Event struct {

type ActionRunner struct {
pingerCallback func()
addCallback func(*Container)
addCallback func(*Container, *MetaData)
updateCallback func(*Container, *Endpoint)
removeCallback func(*Container)
getCallback func(string, string) (*Container, *Endpoint)
Expand All @@ -43,8 +44,8 @@ func (ar *ActionRunner) Pinger() {
ar.push(&Event{Type: pingerEvent})
}

func (ar *ActionRunner) Add(container *Container) {
ar.push(&Event{Type: addEvent, Container: container})
func (ar *ActionRunner) Add(container *Container, meta *MetaData) {
ar.push(&Event{Type: addEvent, Container: container, Meta: meta})
}

func (ar *ActionRunner) Update(container *Container, endpoint *Endpoint) {
Expand Down Expand Up @@ -97,7 +98,7 @@ func WithPingerCallback(callback func()) func(*ActionRunner) {
}
}

func WithAddCallback(callback func(*Container)) func(*ActionRunner) {
func WithAddCallback(callback func(*Container, *MetaData)) func(*ActionRunner) {
return func(ar *ActionRunner) {
ar.addCallback = callback
}
Expand Down Expand Up @@ -149,7 +150,7 @@ func NewActionRunner(bufferSize int, cbs ...ActionCallback) *ActionRunner {
case pingerEvent:
ar.pingerCallback()
case addEvent:
ar.addCallback(event.Container)
ar.addCallback(event.Container, event.Meta)
case updateEvent:
ar.updateCallback(event.Container, event.Endpoint)
case removeEvent:
Expand Down
7 changes: 6 additions & 1 deletion data.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ type Service struct {
Endpoint *Endpoint
}

type MetaData struct {
StaticDomain string
StaticPath string
}

type Driver interface {
Add(*Container)
Add(*Container, *MetaData)
Remove(*Container)
}
4 changes: 1 addition & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: "3.5"

services:
baker:
#image: ella-to/baker:latest
Expand Down Expand Up @@ -30,5 +28,5 @@ services:

networks:
baker:
name: baker_net
name: baker
driver: bridge
43 changes: 24 additions & 19 deletions driver/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,22 @@ type Docker struct {
close chan struct{}
}

func (d *Docker) loadContainerById(ctx context.Context, id string) (*baker.Container, error) {
func (d *Docker) loadContainerById(ctx context.Context, id string) (*baker.Container, *baker.MetaData, error) {
r, _, err := d.getter(ctx, "/containers/"+id+"/json")
if err != nil {
return nil, err
return nil, nil, err
}
defer r.Close()

payload := struct {
Config struct {
Labels struct {
Enable string `json:"baker.enable"`
Network string `json:"baker.network"`
ServicePort string `json:"baker.service.port"`
ServicePing string `json:"baker.service.ping"`
Enable string `json:"baker.enable"`
Network string `json:"baker.network"`
ServicePort string `json:"baker.service.port"`
ServicePing string `json:"baker.service.ping"`
StaticDomain string `json:"baker.service.static.domain"`
StaticPath string `json:"baker.service.static.path"`
} `json:"Labels"`
} `json:"Config"`
NetworkSettings struct {
Expand All @@ -44,40 +46,43 @@ func (d *Docker) loadContainerById(ctx context.Context, id string) (*baker.Conta

err = json.NewDecoder(r).Decode(&payload)
if err != nil {
return nil, err
return nil, nil, err
}

if payload.Config.Labels.Enable != "true" {
return nil, fmt.Errorf("label 'baker.enable' is not set to true")
return nil, nil, fmt.Errorf("label 'baker.enable' is not set to true")
}

network, ok := payload.NetworkSettings.Networks[payload.Config.Labels.Network]
if !ok {
fmt.Println(payload.NetworkSettings.Networks)
return nil, fmt.Errorf("network '%s' not exists in labels", payload.Config.Labels.Network)
return nil, nil, fmt.Errorf("network '%s' not exists in labels", payload.Config.Labels.Network)
}

port, err := strconv.ParseInt(payload.Config.Labels.ServicePort, 10, 32)
if err != nil {
return nil, fmt.Errorf("failed to parse port for container '%s' because %s", id, err)
return nil, nil, fmt.Errorf("failed to parse port for container '%s' because %s", id, err)
}

var addr netip.AddrPort

if network.IPAddress != "" {
addr, err = netip.ParseAddrPort(fmt.Sprintf("%s:%d", network.IPAddress, port))
if err != nil {
return nil, fmt.Errorf("failed to parse address for container '%s' because %s", id, err)
return nil, nil, fmt.Errorf("failed to parse address for container '%s' because %s", id, err)
}
}

slog.Debug("docker driver loaded container", "id", id, "addr", addr, "config", payload.Config.Labels.ServicePing)

return &baker.Container{
Id: id,
Addr: addr,
ConfigPath: payload.Config.Labels.ServicePing,
}, nil
Id: id,
Addr: addr,
ConfigPath: payload.Config.Labels.ServicePing,
}, &baker.MetaData{
StaticDomain: payload.Config.Labels.StaticDomain,
StaticPath: payload.Config.Labels.StaticPath,
}, nil
}

func (d *Docker) loadCurrentContainers(ctx context.Context) {
Expand Down Expand Up @@ -108,13 +113,13 @@ func (d *Docker) loadCurrentContainers(ctx context.Context) {

slog.Debug("docker driver received current event", "id", event.ID, "state", event.State)

container, err := d.loadContainerById(ctx, event.ID)
container, meta, err := d.loadContainerById(ctx, event.ID)
if err != nil {
slog.Error("failed to load container", "id", event.ID, "error", err)
continue
}

d.driver.Add(container)
d.driver.Add(container, meta)
}
}

Expand Down Expand Up @@ -152,13 +157,13 @@ func (d *Docker) loadFutureContainers(ctx context.Context) {
continue
}

container, err := d.loadContainerById(ctx, event.ID)
container, meta, err := d.loadContainerById(ctx, event.ID)
if err != nil {
slog.Error("failed to load container", "id", event.ID, "error", err)
continue
}

d.driver.Add(container)
d.driver.Add(container, meta)
}
}

Expand Down
8 changes: 3 additions & 5 deletions examples/hello-world/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
version: "3.5"

services:
example-service:
build: .

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

Expand All @@ -15,5 +13,5 @@ services:

networks:
baker:
name: baker_net
external: true
name: baker
external: true
4 changes: 4 additions & 0 deletions examples/static/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.
!go.mod
!go.sum
!main.go
13 changes: 13 additions & 0 deletions examples/static/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
### build stage ###
FROM golang:1.22-alpine AS builder

WORKDIR /helloworld

COPY . .

RUN go build -o server /helloworld/main.go

### run stage ###
FROM alpine:latest
COPY --from=builder /helloworld/server ./server
CMD ["./server"]
21 changes: 21 additions & 0 deletions examples/static/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Run the example

Firs, go to the root of the baker project and run the following command to start the baker

```
docker-compose up
```

Second, navigate to `examples/static` folder and run the following command

```
docker-compose up --build --scale example-service=3
```

There should be some logs prints out in Baker terminal indicating that services are registering

Finally, run the following curl command which demonstrate the dynamic reverse proxy of Baker

```
curl -H "Host: example.com" http://localhost:80/api/v1
```
18 changes: 18 additions & 0 deletions examples/static/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
services:
example-static-service:
build: .

labels:
- "baker.enable=true"
- "baker.network=baker"
- "baker.service.port=8000"
- "baker.service.static.domain=example.com"
- "baker.service.static.path=/*"

networks:
- baker

networks:
baker:
name: baker
external: true
3 changes: 3 additions & 0 deletions examples/static/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module helloworld

go 1.22
36 changes: 36 additions & 0 deletions examples/static/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package main

import (
"fmt"
"log"
"math/rand"
"net/http"
"time"
)

const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

func init() {
rand.Seed(time.Now().UnixNano())
}

func generateRandomString(length int) string {
b := make([]byte, length)
for i := range b {
b[i] = charset[rand.Intn(len(charset))]
}
return string(b)
}
func main() {
id := generateRandomString(8)

http.HandleFunc("/api/v1", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Received from %s: %s\n", id, r.URL.Path)
})

fmt.Printf("Starting server at port 8000\n")

if err := http.ListenAndServe(":8000", nil); err != nil {
log.Fatal(err)
}
}
Loading

0 comments on commit 8ac57fa

Please sign in to comment.