-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Feature]: Initial implementation of supergood proxy (#1)
* initial implementation * cleaning comments. adding env vars * go fmt * removing unused config * adding support for loading env vars from config file * . * fixing config loading * adding docker file, make file * remove secrets * adding datadog to dockerfile for serverless * . * adding build + push + deploy github action workflow * updating * fixing schema for remote proxy config * adding configs for higher envs
- Loading branch information
Showing
18 changed files
with
675 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
name: Build, Push, Deploy | ||
|
||
on: | ||
workflow_dispatch: | ||
inputs: | ||
environment: | ||
description: "Deployment Environment" | ||
required: true | ||
default: "staging" | ||
type: choice | ||
options: | ||
- staging | ||
- production | ||
|
||
jobs: | ||
build-push-deploy: | ||
runs-on: ubuntu-latest | ||
environment: | ||
name: ${{ github.event.inputs.environment == 'production' && 'production' || 'staging' }} | ||
strategy: | ||
fail-fast: false | ||
matrix: | ||
include: | ||
- dockerfile: Dockerfile | ||
serviceName: ${{ github.event.inputs.environment == 'production' && 'proxy-production' || 'proxy-staging' }} | ||
image: ${{ github.event.inputs.environment == 'production' && 'us-west1-docker.pkg.dev/supergood-373204/proxy/proxy:latest' || 'us-west1-docker.pkg.dev/supergood-staging-410621/proxy/proxy:latest' }} | ||
|
||
steps: | ||
- name: Checkout | ||
uses: "actions/checkout@v3" | ||
|
||
- name: Auth | ||
uses: "google-github-actions/auth@v1" | ||
with: | ||
credentials_json: ${{ github.event.inputs.environment == 'production' && secrets.GCP_PRODUCTION_GITHUB_ACTIONS_SERVICE_ACCOUNT_KEY_JSON || secrets.GCP_STAGING_GITHUB_ACTIONS_SERVICE_ACCOUNT_KEY_JSON }} | ||
|
||
- name: Set up Cloud SDK | ||
uses: "google-github-actions/setup-gcloud@v1" | ||
|
||
- name: Use gcloud CLI | ||
run: gcloud info | ||
|
||
- name: Docker auth | ||
run: |- | ||
gcloud auth configure-docker us-west1-docker.pkg.dev --quiet | ||
- name: Set up Docker Buildx | ||
uses: docker/setup-buildx-action@v2 | ||
|
||
- name: Build and push | ||
uses: docker/build-push-action@v3 | ||
with: | ||
file: ${{ matrix.dockerfile }} | ||
context: . | ||
push: true | ||
tags: ${{ matrix.image }} | ||
|
||
- name: Post Deployment Start to Slack | ||
uses: slackapi/[email protected] | ||
with: | ||
channel-id: "C04TB9BTHJA" | ||
slack-message: "Deploying: ${{ matrix.serviceName }} to ${{ github.event.inputs.environment == 'production' && 'production' || 'staging'}}" | ||
env: | ||
SLACK_BOT_TOKEN: ${{ secrets.SLACK_TOKEN }} | ||
|
||
- name: Post Deployment Failure to Slack | ||
if: ${{ failure() }} | ||
uses: slackapi/[email protected] | ||
with: | ||
channel-id: "C04TB9BTHJA" | ||
slack-message: "Failed to deploy ${{ matrix.serviceName }} to ${{ github.event.inputs.environment == 'production' && 'production' || 'staging'}}" | ||
env: | ||
SLACK_BOT_TOKEN: ${{ secrets.SLACK_TOKEN }} | ||
|
||
- name: Post Deployment Success to Slack | ||
if: ${{ success() }} | ||
uses: slackapi/[email protected] | ||
with: | ||
channel-id: "C04TB9BTHJA" | ||
slack-message: "Successfully deployed ${{ matrix.serviceName }} to ${{ github.event.inputs.environment == 'production' && 'production' || 'staging'}}" | ||
env: | ||
SLACK_BOT_TOKEN: ${{ secrets.SLACK_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
FROM golang:1.20.6-alpine3.18 AS base | ||
|
||
WORKDIR /var/code | ||
COPY ./ ./ | ||
|
||
RUN \ | ||
CGO_ENABLED=0 GOOS=linux go build \ | ||
-installsuffix "static" \ | ||
-o /usr/local/bin/supergood-proxy \ | ||
./cmd/main.go | ||
|
||
FROM alpine:3.18.2 AS app | ||
COPY _config/ /var/_config/ | ||
COPY --from=base /usr/local/bin/supergood-proxy /usr/local/bin/supergood-proxy | ||
COPY --from=datadog/serverless-init:1 /datadog-init /app/datadog-init | ||
ENTRYPOINT ["/app/datadog-init"] | ||
CMD ["/usr/local/bin/supergood-proxy"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
.PHONY: help | ||
help: | ||
@echo 'Makefile for `supergood-proxy` project' | ||
@echo '' | ||
@echo 'Development supergood-proxy targets:' | ||
@echo ' make run-local Run `supergood-proxy` on the host' | ||
|
||
################################################################################ | ||
# Development supergood-proxy targets | ||
################################################################################ | ||
|
||
.PHONY: run-local | ||
run-local: | ||
@go run \ | ||
./cmd/ \ | ||
--path ./_config/dev.yml \ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
remoteWorkerConfig: | ||
baseURL: "http://localhost:3001" | ||
fetchInterval: "60s" | ||
proxyConfig: | ||
port: "8080" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
remoteWorkerConfig: | ||
baseURL: "https://api.supergood.ai" | ||
fetchInterval: "60s" | ||
proxyConfig: | ||
port: "8080" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
remoteWorkerConfig: | ||
baseURL: "https://api-staging.supergood.ai" | ||
fetchInterval: "60s" | ||
proxyConfig: | ||
port: "8080" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package cache | ||
|
||
import "sync" | ||
|
||
func New() Cache { | ||
return Cache{ | ||
cache: map[string]*CacheVal{}, | ||
mutex: new(sync.RWMutex), | ||
} | ||
} | ||
|
||
func (c *Cache) Get(key string) *CacheVal { | ||
c.mutex.RLock() | ||
defer c.mutex.RUnlock() | ||
val := c.cache[key] | ||
return val | ||
} | ||
|
||
func (c *Cache) Set(key string, val *CacheVal) { | ||
c.mutex.Lock() | ||
defer c.mutex.Unlock() | ||
c.cache[key] = val | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package cache | ||
|
||
import "sync" | ||
|
||
type Cache struct { | ||
cache map[string]*CacheVal | ||
mutex *sync.RWMutex | ||
} | ||
|
||
type CacheVal struct { | ||
ClientID string | ||
ClientSecret string | ||
Vendors map[string]VendorConfig | ||
} | ||
|
||
type VendorConfig struct { | ||
Credentials []Credential | ||
} | ||
|
||
type Credential struct { | ||
Key string | ||
Value string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/* | ||
Copyright (c) 2021 - Present. Blend Labs, Inc. All rights reserved | ||
Blend Confidential - Restricted | ||
*/ | ||
|
||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"os" | ||
"os/signal" | ||
"syscall" | ||
"time" | ||
|
||
"github.com/spf13/cobra" | ||
"github.com/supergoodsystems/supergood-proxy/cache" | ||
"github.com/supergoodsystems/supergood-proxy/config" | ||
"github.com/supergoodsystems/supergood-proxy/proxy" | ||
"github.com/supergoodsystems/supergood-proxy/remoteconfigworker" | ||
) | ||
|
||
func run() error { | ||
path := "" | ||
cmd := &cobra.Command{ | ||
Use: "supergood-proxy", | ||
Short: "Run Supergood Proxy", | ||
SilenceErrors: true, | ||
SilenceUsage: true, | ||
RunE: func(_ *cobra.Command, _ []string) error { | ||
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGINT, syscall.SIGTERM) | ||
defer cancel() | ||
|
||
cfg, err := config.GetConfig(path) | ||
if err != nil { | ||
log.Fatalf("%v", err) | ||
} | ||
projectCache := cache.New() | ||
|
||
rcw := remoteconfigworker.New(cfg.RemoteWorkerConfig, &projectCache) | ||
rp := proxy.New(proxy.ProxyOpts{ | ||
Port: cfg.ProxyConfig.Port, | ||
Handler: proxy.NewProxyHandler(&projectCache), | ||
}) | ||
|
||
err = rcw.Start(ctx) | ||
if err != nil { | ||
log.Fatalf("Failed to start remote config worker with error: %v", err) | ||
} | ||
rp.Start(ctx) | ||
|
||
<-ctx.Done() | ||
log.Println("Shutting down server...") | ||
|
||
// TODO: Add wait groups to account for both server and worker | ||
shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second) | ||
defer shutdownCancel() | ||
|
||
rp.Stop(shutdownCtx) | ||
log.Println("Server exiting") | ||
return nil | ||
}, | ||
} | ||
|
||
cmd.PersistentFlags().StringVar( | ||
&path, | ||
"path", | ||
path, | ||
"Path to a file where '.yml' configuration is stored", | ||
) | ||
|
||
return cmd.Execute() | ||
} | ||
|
||
func main() { | ||
err := run() | ||
if err != nil { | ||
fmt.Fprintf(os.Stderr, "%v\n", err) | ||
os.Exit(1) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package config | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"time" | ||
|
||
"github.com/supergoodsystems/supergood-proxy/proxy" | ||
"github.com/supergoodsystems/supergood-proxy/remoteconfigworker" | ||
) | ||
|
||
type Config struct { | ||
RemoteWorkerConfig remoteconfigworker.RemoteConfigOpts `yaml:"remoteWorkerConfig"` | ||
ProxyConfig proxy.ProxyOpts `yaml:"proxyConfig"` | ||
} | ||
|
||
func GetConfig(path string) (Config, error) { | ||
cfg := Config{} | ||
var err error | ||
if path == "" { | ||
if path, err = resolvePathFromEnv(); err != nil { | ||
return cfg, err | ||
} | ||
} | ||
|
||
resolveConfigWithPath(path, &cfg) | ||
err = resolveConfigWithEnv(&cfg) | ||
return cfg, err | ||
} | ||
|
||
func resolvePathFromEnv() (string, error) { | ||
env := os.Getenv("ENV") | ||
if env == "" { | ||
return "", fmt.Errorf("cannot have env path undefined as well as ENV var undefined") | ||
} | ||
if env == "development" { | ||
return "_config/dev.yml", nil | ||
} | ||
if env == "staging" { | ||
return "/var/_config/staging.yml", nil | ||
} | ||
if env == "production" { | ||
return "/var/_config/production.yml", nil | ||
} | ||
return "", fmt.Errorf("cannot resolve path from environment. Invalid ENV var") | ||
} | ||
|
||
func resolveConfigWithEnv(config *Config) error { | ||
if config.RemoteWorkerConfig.BaseURL == "" { | ||
config.RemoteWorkerConfig.BaseURL = os.Getenv("SUPERGOOD_BASE_URL") | ||
if config.RemoteWorkerConfig.BaseURL == "" { | ||
config.RemoteWorkerConfig.BaseURL = "http://localhost:3001" | ||
} | ||
} | ||
|
||
if config.RemoteWorkerConfig.AdminClientKey == "" { | ||
config.RemoteWorkerConfig.AdminClientKey = os.Getenv("ADMIN_CLIENT_KEY") | ||
if config.RemoteWorkerConfig.AdminClientKey == "" { | ||
return fmt.Errorf("ADMIN_CLIENT_KEY missing from env vars") | ||
} | ||
} | ||
|
||
if config.RemoteWorkerConfig.FetchInterval == 0 { | ||
config.RemoteWorkerConfig.FetchInterval = 1 * time.Second | ||
} | ||
|
||
if config.ProxyConfig.Port == "" { | ||
config.ProxyConfig.Port = os.Getenv("PROXY_HTTP_PORT") | ||
if config.ProxyConfig.Port == "" { | ||
config.ProxyConfig.Port = "8080" | ||
} | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package config | ||
|
||
import ( | ||
"io" | ||
"os" | ||
|
||
"gopkg.in/yaml.v3" | ||
) | ||
|
||
func resolveConfigWithPath(path string, out interface{}) (err error) { | ||
var f *os.File | ||
defer func() { | ||
if f == nil { | ||
return | ||
} | ||
closeErr := f.Close() | ||
err = closeErr | ||
}() | ||
|
||
f, err = os.Open(path) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = UnmarshalYAMLStrict(f, out) | ||
return | ||
} | ||
|
||
// UnmarshalYAMLStrict provides a YAML decoder that does not allow unknown | ||
// fields. | ||
func UnmarshalYAMLStrict(r io.Reader, out interface{}) error { | ||
d := yaml.NewDecoder(r) | ||
d.KnownFields(true) | ||
return d.Decode(out) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
module github.com/supergoodsystems/supergood-proxy | ||
|
||
go 1.19 | ||
|
||
require ( | ||
github.com/inconshreveable/mousetrap v1.1.0 // indirect | ||
github.com/spf13/cobra v1.8.1 // indirect | ||
github.com/spf13/pflag v1.0.5 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
Oops, something went wrong.