Skip to content

Commit d1ed963

Browse files
authored
Merge pull request #9 from drone/FFM-778_go_test_wrapper
(FFM-778) Adds go test wrapper
2 parents 1dd9e5f + 9946eb2 commit d1ed963

File tree

11 files changed

+983
-0
lines changed

11 files changed

+983
-0
lines changed

test_wrapper/Makefile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
install:
2+
go get github.com/deepmap/oapi-codegen/cmd/[email protected]
3+
4+
generate:
5+
# generate admin
6+
oapi-codegen -generate server,spec -package=restapi ./test_wrapper_openapi/sdk_wrapper-v1.yaml > ./restapi/test_wrapper_service.gen.go
7+
oapi-codegen -generate types -package=restapi ./test_wrapper_openapi/sdk_wrapper-v1.yaml > ./restapi/test_wrapper_types.gen.go
8+
9+
build:
10+
docker build -t go_sdk_wrapper:latest -f ./docker/Dockerfile .

test_wrapper/docker/Dockerfile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
FROM golang:1.14-alpine as builder
2+
RUN apk update && apk add --no-cache make gcc musl-dev git ca-certificates && update-ca-certificates
3+
WORKDIR /app
4+
5+
# Fetch dependencies.
6+
COPY ./go.mod .
7+
RUN go mod download
8+
9+
COPY . .
10+
RUN go mod verify
11+
RUN make install
12+
RUN make generate
13+
14+
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /app/go_wrapper .
15+
16+
FROM alpine:latest
17+
RUN apk update && apk add --no-cache bash
18+
COPY --from=builder /app/go_wrapper /app/go_wrapper
19+
20+
CMD ["/app/go_wrapper"]

test_wrapper/ffgosdk/ff_go_sdk.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package ffgosdk
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
harness "github.com/drone/ff-golang-server-sdk/client"
8+
"github.com/drone/ff-golang-server-sdk/evaluation"
9+
"github.com/drone/ff-golang-server-sdk/test_wrapper/wrapperconfig"
10+
"github.com/drone/ff-golang-server-sdk/types"
11+
)
12+
13+
// SDK .
14+
type SDK struct {
15+
sdkClient *harness.CfClient
16+
}
17+
18+
// NewSDK .
19+
func NewSDK(config wrapperconfig.Config) (SDK, error) {
20+
client, err := harness.NewCfClient(config.SdkKey,
21+
harness.WithURL(config.BaseURL),
22+
harness.WithStreamEnabled(config.EnableStreaming),
23+
)
24+
25+
if err != nil {
26+
return SDK{}, err
27+
}
28+
29+
return SDK{
30+
sdkClient: client,
31+
}, nil
32+
}
33+
34+
// Close .
35+
func (s SDK) Close() error {
36+
return s.sdkClient.Close()
37+
}
38+
39+
// GetVariant .
40+
func (s SDK) GetVariant(kind string, flagKey string, targetMap *map[string]string) (string, error) {
41+
out := ""
42+
t := &evaluation.Target{}
43+
if targetMap != nil {
44+
t.Attributes = make(map[string]interface{})
45+
for k, v := range *targetMap {
46+
t.Attributes[k] = v
47+
}
48+
}
49+
50+
var err error
51+
switch kind {
52+
case "boolean":
53+
var variation bool
54+
variation, err = s.sdkClient.BoolVariation(flagKey, t, false)
55+
out = fmt.Sprintf("%t", variation)
56+
case "string":
57+
var variation string
58+
variation, err = s.sdkClient.StringVariation(flagKey, t, "")
59+
out = fmt.Sprintf("%s", variation)
60+
case "int":
61+
var variation int64
62+
variation, err = s.sdkClient.IntVariation(flagKey, t, -1)
63+
out = fmt.Sprintf("%d", variation)
64+
case "number":
65+
var variation float64
66+
variation, err = s.sdkClient.NumberVariation(flagKey, t, -1)
67+
out = fmt.Sprintf("%g", variation)
68+
case "json":
69+
var variation types.JSON
70+
variation, err = s.sdkClient.JSONVariation(flagKey, t, types.JSON{})
71+
data, err := json.Marshal(variation)
72+
if err != nil {
73+
out = fmt.Sprintf("{}")
74+
} else {
75+
out = fmt.Sprintf("%s", string(data))
76+
}
77+
}
78+
79+
if err != nil {
80+
return out, err
81+
}
82+
return out, nil
83+
}

test_wrapper/go.mod

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module github.com/drone/ff-golang-server-sdk/test_wrapper
2+
3+
go 1.16
4+
5+
require (
6+
github.com/deepmap/oapi-codegen v1.6.1 // indirect
7+
github.com/drone/ff-golang-server-sdk v0.0.4
8+
github.com/getkin/kin-openapi v0.53.0
9+
github.com/labstack/echo/v4 v4.2.2
10+
github.com/sirupsen/logrus v1.8.1
11+
github.com/spf13/viper v1.7.1
12+
)

test_wrapper/go.sum

Lines changed: 422 additions & 0 deletions
Large diffs are not rendered by default.

test_wrapper/handlers/handlers.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package handlers
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/drone/ff-golang-server-sdk/test_wrapper/ffgosdk"
7+
restapi "github.com/drone/ff-golang-server-sdk/test_wrapper/restapi"
8+
"github.com/drone/ff-golang-server-sdk/test_wrapper/wrapperconfig"
9+
"github.com/labstack/echo/v4"
10+
log "github.com/sirupsen/logrus"
11+
)
12+
13+
// ServerImpl .
14+
type ServerImpl struct {
15+
sdk ffgosdk.SDK
16+
}
17+
18+
// NewServer .
19+
func NewServer(config wrapperconfig.Config) ServerImpl {
20+
sdkClient, err := ffgosdk.NewSDK(config)
21+
if err != nil {
22+
panic(err)
23+
}
24+
return ServerImpl{
25+
sdk: sdkClient,
26+
}
27+
}
28+
29+
// Ping .
30+
func (s *ServerImpl) Ping(ctx echo.Context) error {
31+
return ctx.JSON(http.StatusOK, restapi.PongResponse{Pong: true})
32+
}
33+
34+
// GetFlagValue .
35+
func (s *ServerImpl) GetFlagValue(ctx echo.Context) error {
36+
log.Infof("GetFlagValue %+v", ctx.Request())
37+
flagBody := new(restapi.FlagCheckBody)
38+
err := ctx.Bind(flagBody)
39+
if err != nil {
40+
log.Error(err)
41+
return ctx.JSON(http.StatusInternalServerError, restapi.ErrorResponse{ErrorMessage: err.Error()})
42+
}
43+
44+
var targetMap map[string]string
45+
if flagBody.Target != nil {
46+
if flagBody.Target.Name != nil {
47+
targetMap["name"] = *flagBody.Target.Name
48+
}
49+
if flagBody.Target.Email != nil {
50+
targetMap["email"] = *flagBody.Target.Email
51+
}
52+
if flagBody.Target.Region != nil {
53+
targetMap["region"] = *flagBody.Target.Region
54+
}
55+
}
56+
57+
variantion, err := s.sdk.GetVariant(flagBody.FlagKind, flagBody.FlagKey, &targetMap)
58+
if err != nil {
59+
log.Error(err)
60+
return ctx.JSON(http.StatusInternalServerError, restapi.ErrorResponse{ErrorMessage: err.Error()})
61+
}
62+
63+
flagValue := restapi.FlagCheckResponse{FlagKey: flagBody.FlagKey, FlagValue: variantion}
64+
return ctx.JSON(http.StatusOK, flagValue)
65+
}

test_wrapper/main.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
"os"
8+
"os/signal"
9+
"syscall"
10+
"time"
11+
12+
"github.com/drone/ff-golang-server-sdk/test_wrapper/handlers"
13+
"github.com/drone/ff-golang-server-sdk/test_wrapper/restapi"
14+
"github.com/drone/ff-golang-server-sdk/test_wrapper/wrapperconfig"
15+
"github.com/labstack/echo/v4"
16+
"github.com/labstack/echo/v4/middleware"
17+
log "github.com/sirupsen/logrus"
18+
"github.com/spf13/viper"
19+
)
20+
21+
func main() {
22+
log.SetFormatter(&log.JSONFormatter{})
23+
24+
config := wrapperconfig.Config{}
25+
26+
viper.SetDefault("WrapperHostname", "")
27+
viper.SetDefault("WrapperPort", "4000")
28+
viper.SetDefault("BaseURL", "http://localhost/api/1.0")
29+
30+
viper.SetDefault("SdkKey", "1bca46aa-0abe-4b22-a5f0-904422db288b")
31+
viper.SetDefault("EnableStreaming", false)
32+
viper.BindEnv("WrapperHostname", "WRAPPER_HOSTNAME")
33+
viper.BindEnv("WrapperPort", "WRAPPER_PORT")
34+
viper.BindEnv("BaseURL", "SDK_BASE_URL")
35+
viper.BindEnv("SdkKey", "SDK_KEY")
36+
viper.BindEnv("EnableStreaming", "ENABLE_STREAMING")
37+
38+
err := viper.Unmarshal(&config)
39+
if err != nil {
40+
log.Fatalf("unable to viper decode into struct, %v", err)
41+
}
42+
43+
log.Infof("Starting up with config: \n%+v\n", config)
44+
setupHandlers(config)
45+
}
46+
47+
func setupHandlers(config wrapperconfig.Config) {
48+
myServer := handlers.NewServer(config)
49+
e := echo.New()
50+
51+
apiGroup := e.Group("/api/1.0")
52+
//apiGroup.Use(oapimiddleware.OapiRequestValidatorWithOptions(swagger, &oapimiddleware.Options{}))
53+
e.Use(middleware.Recover())
54+
e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
55+
AllowOrigins: []string{"*"},
56+
AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept, "Cache-Control"},
57+
}))
58+
59+
restapi.RegisterHandlers(apiGroup, &myServer)
60+
61+
go func() {
62+
serverAddress := fmt.Sprintf("%s:%s", config.WrapperHostname, config.WrapperPort)
63+
fmt.Printf("Starting api wrapper on: %s", serverAddress)
64+
err := e.Start(serverAddress)
65+
if err != nil && err != http.ErrServerClosed {
66+
log.Fatal("shutting down http server")
67+
}
68+
}()
69+
70+
waitForShutdown(e)
71+
}
72+
73+
func waitForShutdown(e *echo.Echo) {
74+
// Handle sigterm and shutdown gracefully
75+
termChan := make(chan os.Signal)
76+
signal.Notify(termChan, syscall.SIGINT, syscall.SIGTERM)
77+
log.Debug("Waiting for Shutdown")
78+
79+
<-termChan // Blocks here until interrupted
80+
81+
// Handle shutdown
82+
log.Info("Shutdown signal received")
83+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
84+
defer cancel()
85+
if err := e.Shutdown(ctx); err != nil {
86+
log.Fatal(err)
87+
}
88+
}

0 commit comments

Comments
 (0)