Skip to content
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
29 changes: 29 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Go Lint
on:
push:
tags:
- v*
branches:
- master
- main
pull_request:
permissions:
contents: read
# Optional: allow read access to pull request. Use with `only-new-issues` option.
# pull-requests: read
jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/setup-go@v4
with:
go-version: '1.19'
cache: false
- uses: actions/checkout@v3
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.52.2
args: --sort-results --disable unused

21 changes: 21 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Test

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19

- name: Test
run: go test -v -race ./...
41 changes: 37 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,37 @@
dist/
*.json
.idea/
*.iml
/dist

# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so

go.work

# Folders
_obj
_test

# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out

*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*

_testmain.go

*.exe
*.test
*.prof

.idea/*
.DS_STORE
.vscode/*

*.wat

vendor/
.bin/
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This is an implementation that uses our Go Server SDK to initialize and start multiple servers that emulate the response
format of the
bucketing-api server. This allows SDK's where implementing the WebAssembly bucketing library as a core isn't possible to
Bucketing API server. This allows SDK's where implementing the WebAssembly bucketing library as a core isn't possible to
benefit from the Local Bucketing benefits of the DevCycle platform.

## Usage
Expand All @@ -11,12 +11,9 @@ The application is delivered in multiple formats - a Docker image, a deb, and RP
format for local building and implementation.

The proxy handles two modes of operation - you can expose the HTTP server over a TCP port, or over Unix domain sockets.
The latter is recommended for servers that will deploy this in a fashion where the proxy is running on the same machine
as the SDK,
preventing the need for network calls.
The latter is recommended for servers that will deploy this with the proxy running on the same machine as the SDK, preventing the need for network calls.

The HTTP server mode is a 1:1 replacement for the bucketing-api - and can be used in place where there is no SDK in use
as well.
The HTTP server mode is a 1:1 replacement for the Bucketing API used by all SDKs in cloud bucketing mode, or can be used directly without an SDK as an API.

### Docker

Expand All @@ -31,6 +28,8 @@ We also provide the raw application binary to wrap in your own daemon manager, o
Either a path to a config file which allows specifying multiple instances of a proxy, or environment variables can be
used to configure the proxy.

A simple healthcheck for each proxy instance can be performed by sending a GET request to the `/healthz` endpoint.

### Command Line Arguments

| ARGUMENT | TYPE | DEFAULT | REQUIRED | DESCRIPTION |
Expand All @@ -42,6 +41,7 @@ used to configure the proxy.

| KEY | TYPE | DEFAULT | REQUIRED | DESCRIPTION |
|--------------------------------------------------------|---------------|---------|----------|---------------------------------------------------------------------------------|
| DVC_LB_PROXY_CONFIG | String | | | The path to a JSON configuration file. |
| DVC_LB_PROXY_UNIX_SOCKET_PATH | String | | | The path to the Unix socket. |
| DVC_LB_PROXY_HTTP_PORT | Integer | 8080 | | The port to listen on for HTTP requests. Defaults to 8080. |
| DVC_LB_PROXY_UNIX_SOCKET_ENABLED | True or False | false | | Whether to enable the Unix socket. Defaults to false. |
Expand Down
154 changes: 47 additions & 107 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -1,146 +1,86 @@
package main

import (
"encoding/json"
"context"
"flag"
"fmt"
devcycle "github.com/devcyclehq/go-server-sdk/v2"
lbproxy "github.com/devcyclehq/local-bucketing-proxy"
"github.com/gin-gonic/gin"
"github.com/kelseyhightower/envconfig"
"log"
"os"
"os/signal"
"runtime"
"syscall"
)

var (
configJSONPath string
config lbproxy.ProxyConfig
lbproxy "github.com/devcyclehq/local-bucketing-proxy"
"github.com/kelseyhightower/envconfig"
)

const (
EnvVarPrefix = "DVC_LB_PROXY"
Version = "0.1.0"
)
Version = "0.1.0"
EnvConfigFormat = `
This application can also be configured via the environment. The following environment
variables can be used:

func init() {
{{printf "%-54s" "KEY"}} {{ printf "%-11s" "TYPE" }} DEFAULT REQUIRED DESCRIPTION
{{range .}}{{usage_key . | printf "%-54s"}} {{usage_type . | printf "%-11s"}} {{usage_default .}} {{usage_required . | printf "%5s" }} {{usage_description .}}
{{end}}`
)

if os.Getenv(EnvVarPrefix+"_CONFIG") != "" {
configJSONPath = os.Getenv(EnvVarPrefix + "_PROXY_CONFIG")
} else {
flag.StringVar(&configJSONPath, "config", "", "Path to config.json file")
}
func main() {
var configPath string
flag.StringVar(&configPath, "config", "", "The path to a JSON config file.")

if os.Getenv(EnvVarPrefix+"_DEBUG") == "" {
gin.SetMode(gin.ReleaseMode)
}
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "DevCycle Local Bucketing Proxy Version %s\n", Version)
log.Printf("DevCycle Local Bucketing Proxy Version %s\n", Version)

fmt.Fprintf(os.Stderr, "Usage: %s [options]\n", os.Args[0])
log.Printf("Usage: %s [options]\n", os.Args[0])
flag.PrintDefaults()
fmt.Printf("\nAlternatively, ")
_ = envconfig.Usagef(EnvVarPrefix, &lbproxy.ProxyInstance{}, os.Stderr, envconfig.DefaultTableFormat)
_ = envconfig.Usagef(lbproxy.EnvVarPrefix, &lbproxy.FullEnvConfig{}, os.Stderr, EnvConfigFormat)
}
flag.Parse()

// Load config
if configJSONPath == "" {
instance, err := initializeProxyInstanceFromEnv()
if err != nil {
log.Println("Failed to initialize proxy instance from environment variables. Please either set the config path or set the environment variables:")
log.Println(err)
err = envconfig.Usage(EnvVarPrefix, instance)
if err != nil {
log.Println(err)
}
return
}
defer func() {
err = os.Remove(instance.UnixSocketPath)
}()
config = lbproxy.ProxyConfig{Instances: []lbproxy.ProxyInstance{*instance}}
} else {
configFile, err := os.ReadFile(configJSONPath)
if err != nil {
log.Println("Failed to read config file, writing an empty configuration file to the specified path.")
hostname, err := os.Hostname()
if err != nil {
hostname = "unknown"
}
config := lbproxy.ProxyConfig{Instances: []lbproxy.ProxyInstance{{
UnixSocketPath: "/tmp/devcycle.sock",
HTTPPort: 8080,
UnixSocketEnabled: false,
HTTPEnabled: true,
SDKKey: "",
PlatformData: devcycle.PlatformData{
SdkType: "server",
SdkVersion: devcycle.VERSION,
PlatformVersion: runtime.Version(),
Platform: "Go",
Hostname: hostname,
},
SDKConfig: lbproxy.SDKConfig{
EventFlushIntervalMS: 0,
ConfigPollingIntervalMS: 0,
RequestTimeout: 0,
DisableAutomaticEventLogging: false,
DisableCustomEventLogging: false,
MaxEventQueueSize: 0,
FlushEventQueueSize: 0,
ConfigCDNURI: "",
EventsAPIURI: "",
},
}}}
config.Default()
bytes, err := json.Marshal(config)
if err != nil {
log.Println("Failed to marshal config to JSON.")
return
}
err = os.WriteFile(configJSONPath, bytes, 0644)
if err != nil {
log.Println("Failed to write config to file.")
return
}
}

err = json.Unmarshal(configFile, &config)
if err != nil {
return
}
config, err := lbproxy.ParseConfig(configPath)
if err != nil {
log.Printf("Failed to parse config: %s", err)
log.Fatal("Please either set the config path or set the environment variables")
}
}

func main() {

if len(config.Instances) == 0 {
fmt.Println("No instances found in config.")
log.Fatalf("No instances found in config. Use %s -config <path> to create a sample config file.", os.Args[0])
return
}
// Create router for each instance
for _, instance := range config.Instances {
log.Printf("Creating bucketing proxy instance: %+v", instance)

// Create client
_, err := lbproxy.NewBucketingProxyInstance(instance)
if err != nil {
fmt.Println(err)
return
log.Fatal(err)
}
defer func(path string) {
err = os.Remove(path)
}(instance.UnixSocketPath)
}

ctx, cancel := context.WithCancel(context.Background())

// Use a buffered channel, so we don't miss any signals
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGTERM)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)

// Block until a signal is received.
s := <-c
fmt.Println("Got signal:", s)
}
go func() {
// Block until a signal is received.
s := <-c
fmt.Printf("Received signal: %s, shutting down", s)

for _, instance := range config.Instances {
err := instance.Close()
if err != nil {
log.Printf("Failed to shut down instance: %s", err)
}
}

cancel()
}()

func initializeProxyInstanceFromEnv() (*lbproxy.ProxyInstance, error) {
instance := &lbproxy.ProxyInstance{}
err := envconfig.Process(EnvVarPrefix, instance)
return instance, err
<-ctx.Done()
}
4 changes: 2 additions & 2 deletions config.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"httpPort": 8080,
"unixSocketEnabled": false,
"httpEnabled": true,
"sdkKey": "",
"sdkKey": "dvc_YOUR_KEY_HERE",
"platformData": {
"sdkType": "server",
"sdkVersion": "2.10.2",
Expand All @@ -17,7 +17,7 @@
"sdkConfig": {
"eventFlushIntervalMS": 3000,
"configPollingIntervalMS": 30000,
"requestTimeout": 3000,
"requestTimeout": 60000,
"maxEventsPerFlush": 10000,
"minEventsPerFlush": 100,
"configCDNURI": "https://config-cdn.devcycle.com",
Expand Down
9 changes: 8 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ go 1.19
require (
github.com/devcyclehq/go-server-sdk/v2 v2.10.2
github.com/gin-gonic/gin v1.8.2
github.com/kelseyhightower/envconfig v1.4.0
github.com/kr/pretty v0.3.1
github.com/stretchr/testify v1.8.2
)

require (
github.com/bytecodealliance/wasmtime-go/v6 v6.0.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
Expand All @@ -18,13 +22,15 @@ require (
github.com/jarcoal/httpmock v1.2.0 // indirect
github.com/jolestar/go-commons-pool/v2 v2.1.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.2.2 // indirect
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/twmb/murmur3 v1.1.7 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
golang.org/x/crypto v0.7.0 // indirect
Expand All @@ -34,4 +40,5 @@ require (
golang.org/x/text v0.8.0 // indirect
google.golang.org/protobuf v1.29.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ github.com/jolestar/go-commons-pool/v2 v2.1.2 h1:E+XGo58F23t7HtZiC/W6jzO2Ux2IccS
github.com/jolestar/go-commons-pool/v2 v2.1.2/go.mod h1:r4NYccrkS5UqP1YQI1COyTZ9UjPJAAGTUxzcsK1kqhY=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
Expand Down
Loading