Skip to content

Commit

Permalink
Add chmod 777 option - Closes #12 (#13)
Browse files Browse the repository at this point in the history
* Add chmod 777 option

* Return nil for errors instead of bubbling up

* Return nil for errors instead of bubbling up

* Set file permissions mask instead of forcing to 777

* Fix tests

* Make sure proxy socket file has been created before chmoding it.

* Add logging to file option and fix tests
  • Loading branch information
JamieSinn authored Feb 13, 2024
1 parent 6eef874 commit 50e506f
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 39 deletions.
22 changes: 16 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,21 @@ 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 with the proxy 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 used by all SDKs in cloud bucketing mode, or can be used directly without an SDK as an API.
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

The docker image published here is the base runtime version - expecting to be used as a base image for you to extend.
The docker image expects that you use the environment variables to configure the proxy, but can be modified and extended to use a configuration
file instead.
The docker image expects that you use the environment variables to configure the proxy, but can be modified and extended
to use a configuration
file instead.

We also provide the raw application binary to wrap in your own daemon manager, or tie into your existing application lifecycle.
We also provide the raw application binary to wrap in your own daemon manager, or tie into your existing application
lifecycle.

## Options

Expand All @@ -30,6 +34,11 @@ used to configure the proxy.

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

We recommend setting the file permissions for the unix socket to be as restrictive as possible. However, as a workaround
for deployment issues, you can set the permissions to your own custom mask via the
`DVC_LB_PROXY_UNIX_SOCKET_PERMISSIONS` environment variable, or the unixSocketPermissions option in the config file. The
default is 0755

### Command Line Arguments

| ARGUMENT | TYPE | DEFAULT | REQUIRED | DESCRIPTION |
Expand All @@ -41,10 +50,11 @@ A simple healthcheck for each proxy instance can be performed by sending a GET r

| KEY | TYPE | DEFAULT | REQUIRED | DESCRIPTION |
|--------------------------------------------------------|---------------|---------|----------|---------------------------------------------------------------------------------|
| DVC_LB_PROXY_CONFIG | String | | | The path to a JSON configuration file. |
| 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. |
| DVC_LB_PROXY_UNIX_SOCKET_PERMISSIONS | String | 0755 | | The permissions to set on the Unix socket. Defaults to 0755 |
| DVC_LB_PROXY_HTTP_ENABLED | True or False | true | | Whether to enable the HTTP server. Defaults to true. |
| DVC_LB_PROXY_SDK_KEY | String | | true | The Server SDK key to use for this instance. |
| DVC_LB_PROXY_PLATFORMDATA_SDKTYPE | String | | | |
Expand Down
1 change: 1 addition & 0 deletions config.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"unixSocketEnabled": false,
"httpEnabled": true,
"sdkKey": "dvc_YOUR_KEY_HERE",
"logFile": "/var/log/devcycle.log",
"platformData": {
"sdkType": "server",
"sdkVersion": "2.10.2",
Expand Down
42 changes: 27 additions & 15 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@ type ProxyConfig struct {
}

type ProxyInstance struct {
UnixSocketPath string `json:"unixSocketPath" envconfig:"UNIX_SOCKET_PATH" desc:"The path to the Unix socket."`
HTTPPort int `json:"httpPort" envconfig:"HTTP_PORT" default:"8080" desc:"The port to listen on for HTTP requests. Defaults to 8080."`
UnixSocketEnabled bool `json:"unixSocketEnabled" envconfig:"UNIX_SOCKET_ENABLED" default:"false" desc:"Whether to enable the Unix socket. Defaults to false."`
HTTPEnabled bool `json:"httpEnabled" envconfig:"HTTP_ENABLED" default:"true" desc:"Whether to enable the HTTP server. Defaults to true."`
SDKKey string `json:"sdkKey" required:"true" envconfig:"SDK_KEY" desc:"The Server SDK key to use for this instance."`
PlatformData devcycle.PlatformData `json:"platformData" required:"true"`
SDKConfig SDKConfig `json:"sdkConfig" required:"true"`
dvcClient *devcycle.Client
UnixSocketPath string `json:"unixSocketPath" envconfig:"UNIX_SOCKET_PATH" desc:"The path to the Unix socket."`
UnixSocketPermissions int `json:"unixSocketPermissions" envconfig:"UNIX_SOCKET_PERMISSIONS" default:"755" desc:"The permissions to set on the Unix socket. Defaults to 0755"`
UnixSocketEnabled bool `json:"unixSocketEnabled" envconfig:"UNIX_SOCKET_ENABLED" default:"false" desc:"Whether to enable the Unix socket. Defaults to false."`
HTTPPort int `json:"httpPort" envconfig:"HTTP_PORT" default:"8080" desc:"The port to listen on for HTTP requests. Defaults to 8080."`
HTTPEnabled bool `json:"httpEnabled" envconfig:"HTTP_ENABLED" default:"true" desc:"Whether to enable the HTTP server. Defaults to true."`
SDKKey string `json:"sdkKey" required:"true" envconfig:"SDK_KEY" desc:"The Server SDK key to use for this instance."`
LogFile string `json:"logFile" default:"/var/log/devcycle.log" envconfig:"LOG_FILE" desc:"The path to the log file. Defaults to /var/log/devcycle.log"`
PlatformData devcycle.PlatformData `json:"platformData" required:"true"`
SDKConfig SDKConfig `json:"sdkConfig" required:"true"`
dvcClient *devcycle.Client
}

type SDKConfig struct {
Expand Down Expand Up @@ -76,8 +78,16 @@ func (i *ProxyInstance) Default() {
if i.HTTPEnabled && i.HTTPPort == 0 {
i.HTTPPort = 8080
}
if i.UnixSocketEnabled && i.UnixSocketPath == "" {
i.UnixSocketPath = "/tmp/devcycle.sock"
if i.LogFile == "" {
i.LogFile = "/var/log/devcycle.log"
}
if i.UnixSocketEnabled {
if i.UnixSocketPath == "" {
i.UnixSocketPath = "/tmp/devcycle.sock"
}
if i.UnixSocketPermissions == 0 {
i.UnixSocketPermissions = 755
}
}
}
func (c *ProxyConfig) Default() {
Expand Down Expand Up @@ -165,6 +175,7 @@ func ParseConfig(configPath string) (*ProxyConfig, error) {
if err != nil {
return nil, fmt.Errorf("failed to parse config from JSON: %w", err)
}
proxyConfig.Default()
}

if !initialConfig.Debug {
Expand All @@ -182,11 +193,12 @@ func SampleProxyConfig() ProxyConfig {

proxyConfig := ProxyConfig{
Instances: []*ProxyInstance{{
UnixSocketPath: "/tmp/devcycle.sock",
HTTPPort: 8080,
UnixSocketEnabled: false,
HTTPEnabled: true,
SDKKey: "",
UnixSocketPath: "/tmp/devcycle.sock",
HTTPPort: 8080,
UnixSocketEnabled: false,
UnixSocketPermissions: 755,
HTTPEnabled: true,
SDKKey: "",
PlatformData: devcycle.PlatformData{
SdkType: "server",
SdkVersion: devcycle.VERSION,
Expand Down
38 changes: 24 additions & 14 deletions options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
)

func TestParseConfig(t *testing.T) {
defaultSDKConfig := SDKConfig{}
defaultSDKConfig.Default()
tests := []struct {
name string
flag string
Expand All @@ -33,13 +35,15 @@ func TestParseConfig(t *testing.T) {
expected: &ProxyConfig{
Instances: []*ProxyInstance{
{
UnixSocketPath: "",
HTTPPort: 8080,
UnixSocketEnabled: false,
HTTPEnabled: true,
SDKKey: "dvc-test-key",
PlatformData: api.PlatformData{},
SDKConfig: SDKConfig{},
UnixSocketPath: "",
UnixSocketPermissions: 755,
HTTPPort: 8080,
UnixSocketEnabled: false,
HTTPEnabled: true,
SDKKey: "dvc-test-key",
LogFile: "/var/log/devcycle.log",
PlatformData: api.PlatformData{},
SDKConfig: SDKConfig{},
},
},
},
Expand Down Expand Up @@ -72,11 +76,13 @@ func TestParseConfig(t *testing.T) {
expected: &ProxyConfig{
Instances: []*ProxyInstance{
{
UnixSocketPath: "/tmp/dvc2.sock",
HTTPPort: 1234,
UnixSocketEnabled: true,
HTTPEnabled: false,
SDKKey: "dvc-test-key",
UnixSocketPath: "/tmp/dvc2.sock",
HTTPPort: 1234,
UnixSocketEnabled: true,
UnixSocketPermissions: 755,
HTTPEnabled: false,
SDKKey: "dvc-test-key",
LogFile: "/var/log/devcycle.log",
PlatformData: api.PlatformData{
SdkType: "sdk type",
SdkVersion: "v1.2.3",
Expand Down Expand Up @@ -118,8 +124,9 @@ func TestParseConfig(t *testing.T) {
UnixSocketEnabled: false,
HTTPEnabled: false,
SDKKey: "dvc-sample-key",
LogFile: "/var/log/devcycle.log",
PlatformData: api.PlatformData{},
SDKConfig: SDKConfig{},
SDKConfig: defaultSDKConfig,
},
},
},
Expand All @@ -137,8 +144,9 @@ func TestParseConfig(t *testing.T) {
UnixSocketEnabled: false,
HTTPEnabled: false,
SDKKey: "dvc-sample-key",
LogFile: "/var/log/devcycle.log",
PlatformData: api.PlatformData{},
SDKConfig: SDKConfig{},
SDKConfig: defaultSDKConfig,
},
},
},
Expand All @@ -155,6 +163,8 @@ func TestParseConfig(t *testing.T) {
UnixSocketEnabled: false,
HTTPEnabled: true,
SDKKey: "dvc_YOUR_KEY_HERE",
LogFile: "/var/log/devcycle.log",

PlatformData: api.PlatformData{
SdkType: "server",
SdkVersion: "2.10.2",
Expand Down
29 changes: 25 additions & 4 deletions proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,27 @@ package local_bucketing_proxy

import (
"fmt"
"io"
"log"
"os"
"strconv"
"time"

devcycle "github.com/devcyclehq/go-server-sdk/v2"
"github.com/gin-gonic/gin"
)

func NewBucketingProxyInstance(instance *ProxyInstance) (*ProxyInstance, error) {
gin.DisableConsoleColor()
logFile, err := os.OpenFile(instance.LogFile, os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
if err != nil {
_ = fmt.Errorf("error opening log file: %s", err)
return nil, err
}
mw := io.MultiWriter(os.Stdout, logFile)
log.SetOutput(mw)
gin.DefaultWriter = mw

options := instance.BuildDevCycleOptions()
client, err := devcycle.NewClient(instance.SDKKey, options)
instance.dvcClient = client
Expand Down Expand Up @@ -41,24 +53,33 @@ func NewBucketingProxyInstance(instance *ProxyInstance) (*ProxyInstance, error)
return nil, fmt.Errorf("HTTP port must be set")
}
go func() {
err := r.Run(":" + strconv.Itoa(instance.HTTPPort))
err = r.Run(":" + strconv.Itoa(instance.HTTPPort))
if err != nil {
log.Printf("Error running HTTP server: %s", err)
}
}()
log.Printf("HTTP server started on port %d", instance.HTTPPort)
}
if instance.UnixSocketEnabled {
if _, err := os.Stat(instance.UnixSocketPath); err == nil {
if _, err = os.Stat(instance.UnixSocketPath); err == nil {
return nil, fmt.Errorf("unix socket path %s already exists. Skipping instance creation", instance.UnixSocketPath)
}
err = nil
go func() {
err := r.RunUnix(instance.UnixSocketPath)
err = r.RunUnix(instance.UnixSocketPath)
if err != nil {
log.Printf("Error running Unix socket server: %s", err)
}
}()
log.Printf("Running on unix socket: %s", instance.UnixSocketPath)
fileMode := os.FileMode(instance.UnixSocketPermissions)
_, err = os.Stat(instance.UnixSocketPath)
for ; err != nil; _, err = os.Stat(instance.UnixSocketPath) {
time.Sleep(1 * time.Second)
}
if err = os.Chmod(instance.UnixSocketPath, fileMode); err != nil {
log.Printf("Error setting Unix socket permissions: %s", err)
}
log.Printf("Running on unix socket: %s with file permissions %d", instance.UnixSocketPath, instance.UnixSocketPermissions)
}
return instance, err
}

0 comments on commit 50e506f

Please sign in to comment.