Split to server and CLI binaries

- introduce CLI binary
- move server binary to subdirectory to have a uniformed directory structure
- update goreleaser to build all binaries and create dist packages
- update ignore files accordingly
This commit is contained in:
Peter 2020-05-03 10:18:28 +02:00 committed by baez90
parent 13a38298ec
commit d89ab3a576
11 changed files with 287 additions and 21 deletions

View file

@ -3,8 +3,10 @@
############### ###############
.git/ .git/
.github/
.idea/ .idea/
.vscode/ .vscode/
api/
deploy/ deploy/
dist/ dist/
doc/ doc/

5
.gitignore vendored
View file

@ -7,8 +7,9 @@
**/*.so **/*.so
*.key *.key
*.pem *.pem
inetmock /inetmock
main /imctl
./main
############### ###############
# directories # # directories #

View file

@ -5,24 +5,49 @@ before:
# You may remove this if you don't use go modules. # You may remove this if you don't use go modules.
- make plugins - make plugins
builds: builds:
- id: "default" - id: "inetmock"
binary: inetmock
main: ./cmd/inetmock/main.go
ldflags:
- -w -s
env:
- CGO_ENABLED=0
goos:
- linux
- darwin
goarch:
- amd64
- id: "imctl"
binary: imctl
main: ./cmd/imctl/main.go
ldflags: ldflags:
- -w -s - -w -s
goos: goos:
- linux - linux
- freebsd
- darwin
- windows
goarch: goarch:
- amd64 - amd64
archives: archives:
- id: default - id: inetmock
builds: builds:
- default - inetmock
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}" name_template: "{{ .ProjectName }}_server_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
replacements: replacements:
amd64: x86_64 amd64: x86_64
wrap_in_directory: true wrap_in_directory: true
files: files:
- config.yaml - config.yaml
- "*.so" - "*.so"
- id: imctl
builds:
- imctl
name_template: "{{ .ProjectName }}_cli_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
replacements:
amd64: x86_64
wrap_in_directory: true
files: []
checksum: checksum:
name_template: 'checksums.txt' name_template: 'checksums.txt'
snapshot: snapshot:

View file

@ -1,50 +1,57 @@
VERSION = $(shell git describe --dirty --tags --always) VERSION = $(shell git describe --dirty --tags --always)
DIR = $(dir $(realpath $(firstword $(MAKEFILE_LIST)))) DIR = $(dir $(realpath $(firstword $(MAKEFILE_LIST))))
BUILD_PATH = $(DIR)/main.go SERVER_BUILD_PATH = github.com/baez90/inetmock/cmd/inetmock
CLI_BUILD_PATH = github.com/baez90/inetmock/cmd/imctl
PKGS = $(shell go list ./...) PKGS = $(shell go list ./...)
TEST_PKGS = $(shell go list ./...) TEST_PKGS = $(shell go list ./...)
PROTO_FILES = $(shell find $(DIR)api/ -type f -name "*.proto") PROTO_FILES = $(shell find $(DIR)api/ -type f -name "*.proto")
GOARGS = GOOS=linux GOARCH=amd64 GOARGS = GOOS=linux GOARCH=amd64
GO_BUILD_ARGS = -ldflags="-w -s" GO_BUILD_ARGS = -ldflags='-w -s'
GO_CONTAINER_BUILD_ARGS = -ldflags="-w -s" -a -installsuffix cgo GO_CONTAINER_BUILD_ARGS = -ldflags='-w -s' -a -installsuffix cgo
GO_DEBUG_BUILD_ARGS = -gcflags "all=-N -l" GO_DEBUG_BUILD_ARGS = -gcflags "all=-N -l"
BINARY_NAME = inetmock SERVER_BINARY_NAME = inetmock
CLI_BINARY_NAME = imctl
PLUGINS = $(wildcard $(DIR)plugins/*/.) PLUGINS = $(wildcard $(DIR)plugins/*/.)
DEBUG_PORT = 2345 DEBUG_PORT = 2345
DEBUG_ARGS?= --development-logs=true DEBUG_ARGS?= --development-logs=true
CONTAINER_BUILDER ?= podman CONTAINER_BUILDER ?= podman
DOCKER_IMAGE ?= inetmock DOCKER_IMAGE ?= inetmock
.PHONY: clean all format deps update-deps compile debug generate protoc snapshot-release test cli-cover-report html-cover-report plugins $(PLUGINS) $(GO_GEN_FILES) .PHONY: clean all format deps update-deps compile compile-server compile-cli debug generate protoc snapshot-release test cli-cover-report html-cover-report plugins $(PLUGINS) $(GO_GEN_FILES)
all: clean format compile test plugins all: clean format compile test plugins
clean: clean:
@find $(DIR) -type f \( -name "*.out" -or -name "*.so" \) -exec rm -f {} \; @find $(DIR) -type f \( -name "*.out" -or -name "*.so" \) -exec rm -f {} \;
@rm -rf $(DIR)*.so @rm -rf $(DIR)*.so
@rm -f $(DIR)$(BINARY_NAME) $(DIR)main @rm -f $(DIR)$(SERVER_BINARY_NAME) $(DIR)$(CLI_BINARY_NAME) $(DIR)main
format: format:
@go fmt $(PKGS) @go fmt $(PKGS)
deps: deps:
@go build -v $(BUILD_PATH) @go build -v $(SERVER_BUILD_PATH)
update-deps: update-deps:
@go mod tidy @go mod tidy
@go get -u @go get -u
compile: deps compile-server: deps
ifdef DEBUG ifdef DEBUG
@echo 'Compiling for debugging...' @echo 'Compiling for debugging...'
@$(GOARGS) go build $(GO_DEBUG_BUILD_ARGS) -o $(DIR)$(BINARY_NAME) $(BUILD_PATH) @$(GOARGS) go build $(GO_DEBUG_BUILD_ARGS) -o $(DIR)$(SERVER_BINARY_NAME) $(SERVER_BUILD_PATH)
else ifdef CONTAINER else ifdef CONTAINER
@echo 'Compiling for container usage...' @echo 'Compiling for container usage...'
@$(GOARGS) go build $(GO_CONTAINER_BUILD_ARGS) -o $(DIR)$(BINARY_NAME) $(BUILD_PATH) @$(GOARGS) go build $(GO_CONTAINER_BUILD_ARGS) -o $(DIR)$(SERVER_BINARY_NAME) $(SERVER_BUILD_PATH)
else else
@echo 'Compiling for normal Linux env...' @echo 'Compiling for normal Linux env...'
@$(GOARGS) go build $(GO_BUILD_ARGS) -o $(DIR)$(BINARY_NAME) $(BUILD_PATH) @$(GOARGS) go build $(GO_BUILD_ARGS) -o $(DIR)$(SERVER_BINARY_NAME) $(SERVER_BUILD_PATH)
endif endif
compile-cli: deps
@$(GOARGS) go build $(GO_BUILD_ARGS) -o $(CLI_BINARY_NAME) $(CLI_BUILD_PATH)
compile: compile-server compile-cli
debug: export INETMOCK_PLUGINS_DIRECTORY = $(DIR) debug: export INETMOCK_PLUGINS_DIRECTORY = $(DIR)
debug: debug:
dlv debug $(DIR) \ dlv debug $(DIR) \

9
cmd/imctl/main.go Normal file
View file

@ -0,0 +1,9 @@
package main
import "github.com/baez90/inetmock/internal/cmd"
func main() {
if err := cmd.ExecuteClientCommand(); err != nil {
panic(err)
}
}

View file

@ -10,7 +10,7 @@ func main() {
logger, _ := zap.NewProduction() logger, _ := zap.NewProduction()
defer logger.Sync() defer logger.Sync()
if err := cmd.ExecuteRootCommand(); err != nil { if err := cmd.ExecuteServerCommand(); err != nil {
logger.Error("Failed to run inetmock", logger.Error("Failed to run inetmock",
zap.Error(err), zap.Error(err),
) )

View file

@ -53,7 +53,7 @@ func init() {
generateCaCmd.Flags().Duration(generateCANotAfterRelative, 17520*time.Hour, "Relative time value until when in the future the CA certificate should be valid. The value has a time unit, the greatest time unit is h for hour.") generateCaCmd.Flags().Duration(generateCANotAfterRelative, 17520*time.Hour, "Relative time value until when in the future the CA certificate should be valid. The value has a time unit, the greatest time unit is h for hour.")
} }
func runGenerateCA(cmd *cobra.Command, args []string) { func runGenerateCA(_ *cobra.Command, _ []string) {
var certOutPath, curveName string var certOutPath, curveName string
var notBefore, notAfter time.Duration var notBefore, notAfter time.Duration
var err error var err error

27
internal/cmd/cli.go Normal file
View file

@ -0,0 +1,27 @@
package cmd
import (
"github.com/spf13/cobra"
"time"
)
var (
cliCmd = &cobra.Command{
Use: "",
Short: "IMCTL is the CLI app to interact with an INetMock server",
}
inetMockSocketPath string
outputFormat string
grpcTimeout time.Duration
)
func init() {
cliCmd.PersistentFlags().StringVar(&inetMockSocketPath, "socket-path", "./inetmock.sock", "Path to the INetMock socket file")
cliCmd.PersistentFlags().StringVarP(&outputFormat, "format", "f", "table", "Output format to use. Possible values: table, json, yaml")
cliCmd.PersistentFlags().DurationVar(&grpcTimeout, "grpc-timeout", 5*time.Second, "Timeout to connect to the gRPC API")
cliCmd.AddCommand(endpointsCmd, handlerCmd)
endpointsCmd.AddCommand(getEndpoints)
handlerCmd.AddCommand(getHandlersCmd)
}

72
internal/cmd/endpoints.go Normal file
View file

@ -0,0 +1,72 @@
package cmd
import (
"context"
"fmt"
"github.com/baez90/inetmock/internal/format"
"github.com/baez90/inetmock/internal/rpc"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"os"
)
var (
getEndpoints = &cobra.Command{
Use: "get",
Short: "Get all running endpoints",
Run: runGetEndpoints,
}
endpointsCmd = &cobra.Command{
Use: "endpoints",
Short: "endpoints is the entrypoint to all kind of commands to interact with endpoints",
Aliases: []string{"ep", "endpoint"},
}
)
type printableEndpoint struct {
Id string
Name string
Handler string
ListenAddress string
Port int
}
func fromEndpoint(ep *rpc.Endpoint) *printableEndpoint {
return &printableEndpoint{
Id: ep.Id,
Name: ep.Name,
Handler: ep.Handler,
ListenAddress: ep.ListenAddress,
Port: int(ep.Port),
}
}
func fromEndpoints(eps []*rpc.Endpoint) (out []*printableEndpoint) {
for idx := range eps {
out = append(out, fromEndpoint(eps[idx]))
}
return
}
func runGetEndpoints(_ *cobra.Command, _ []string) {
var err error
var conn *grpc.ClientConn
if conn, err = grpc.Dial(inetMockSocketPath, grpc.WithInsecure()); err != nil {
fmt.Printf("Failed to connecto INetMock socket: %v\n", err)
os.Exit(10)
}
endpointsClient := rpc.NewEndpointsClient(conn)
ctx, _ := context.WithTimeout(context.Background(), grpcTimeout)
var endpointsResp *rpc.GetEndpointsResponse
if endpointsResp, err = endpointsClient.GetEndpoints(ctx, &rpc.GetEndpointsRequest{}); err != nil {
fmt.Printf("Failed to get the endpoints: %v", err)
os.Exit(11)
}
writer := format.Writer(outputFormat, os.Stdout)
if err = writer.Write(fromEndpoints(endpointsResp.Endpoints)); err != nil {
fmt.Printf("Error occurred during writing response values: %v\n", err)
}
}

61
internal/cmd/handlers.go Normal file
View file

@ -0,0 +1,61 @@
package cmd
import (
"context"
"fmt"
"github.com/baez90/inetmock/internal/format"
"github.com/baez90/inetmock/internal/rpc"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"os"
)
var (
getHandlersCmd = &cobra.Command{
Use: "get",
Short: "Get all registered handlers",
Run: runGetHandlers,
}
handlerCmd = &cobra.Command{
Use: "handlers",
Short: "handlers is the entrypoint to all kind of commands to interact with handlers",
Aliases: []string{"handler"},
}
)
type printableHandler struct {
Handler string
}
func fromHandlers(hs []string) (handlers []*printableHandler) {
for idx := range hs {
handlers = append(handlers, &printableHandler{
Handler: hs[idx],
})
}
return
}
func runGetHandlers(_ *cobra.Command, _ []string) {
var err error
var conn *grpc.ClientConn
if conn, err = grpc.Dial(inetMockSocketPath, grpc.WithInsecure()); err != nil {
fmt.Printf("Failed to connecto INetMock socket: %v\n", err)
os.Exit(10)
}
handlersClient := rpc.NewHandlersClient(conn)
ctx, _ := context.WithTimeout(context.Background(), grpcTimeout)
var handlersResp *rpc.GetHandlersResponse
if handlersResp, err = handlersClient.GetHandlers(ctx, &rpc.GetHandlersRequest{}); err != nil {
fmt.Printf("Failed to get the endpoints: %v", err)
os.Exit(11)
}
writer := format.Writer(outputFormat, os.Stdout)
if err = writer.Write(fromHandlers(handlersResp.Handlers)); err != nil {
fmt.Printf("Error occurred during writing response values: %v\n", err)
}
}

View file

@ -2,13 +2,19 @@ package cmd
import ( import (
"github.com/baez90/inetmock/internal/endpoints" "github.com/baez90/inetmock/internal/endpoints"
"github.com/baez90/inetmock/internal/plugins"
"github.com/baez90/inetmock/internal/rpc"
"github.com/baez90/inetmock/pkg/api"
"github.com/baez90/inetmock/pkg/config" "github.com/baez90/inetmock/pkg/config"
"github.com/baez90/inetmock/pkg/logging"
"github.com/baez90/inetmock/pkg/path"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"go.uber.org/zap" "go.uber.org/zap"
"os" "os"
"os/signal" "os/signal"
"strings" "strings"
"syscall" "syscall"
"time"
) )
var ( var (
@ -21,9 +27,58 @@ var (
} }
) )
func onServerInit() {
logging.ConfigureLogging(
logging.ParseLevel(logLevel),
developmentLogs,
map[string]interface{}{"cwd": path.WorkingDirectory()},
)
logger, _ = logging.CreateLogger()
config.CreateConfig(serverCmd.Flags())
appConfig := config.Instance()
if err := appConfig.ReadConfig(configFilePath); err != nil {
logger.Error(
"failed to read config file",
zap.Error(err),
)
}
if err := api.InitServices(appConfig, logger); err != nil {
logger.Error(
"failed to initialize app services",
zap.Error(err),
)
}
registry := plugins.Registry()
cfg := config.Instance()
pluginLoadStartTime := time.Now()
if err := registry.LoadPlugins(cfg.PluginsDir()); err != nil {
logger.Error("Failed to load plugins",
zap.String("pluginsDirectory", cfg.PluginsDir()),
zap.Error(err),
)
}
pluginLoadDuration := time.Since(pluginLoadStartTime)
logger.Info(
"loading plugins completed",
zap.Duration("pluginLoadDuration", pluginLoadDuration),
)
}
func startINetMock(_ *cobra.Command, _ []string) { func startINetMock(_ *cobra.Command, _ []string) {
onServerInit()
endpointManager = endpoints.NewEndpointManager(logger) endpointManager = endpoints.NewEndpointManager(logger)
cfg := config.Instance() cfg := config.Instance()
rpcAPI := rpc.NewINetMockAPI(
cfg,
endpointManager,
plugins.Registry(),
)
for endpointName, endpointHandler := range cfg.EndpointConfigs() { for endpointName, endpointHandler := range cfg.EndpointConfigs() {
handlerSubConfig := cfg.Viper().Sub(strings.Join([]string{config.EndpointsKey, endpointName, config.OptionsKey}, ".")) handlerSubConfig := cfg.Viper().Sub(strings.Join([]string{config.EndpointsKey, endpointName, config.OptionsKey}, "."))
endpointHandler.Options = handlerSubConfig endpointHandler.Options = handlerSubConfig
@ -38,6 +93,12 @@ func startINetMock(_ *cobra.Command, _ []string) {
} }
endpointManager.StartEndpoints() endpointManager.StartEndpoints()
if err := rpcAPI.StartServer(); err != nil {
logger.Error(
"failed to start gRPC API",
zap.Error(err),
)
}
signalChannel := make(chan os.Signal, 1) signalChannel := make(chan os.Signal, 1)
signal.Notify(signalChannel, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) signal.Notify(signalChannel, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
@ -50,5 +111,6 @@ func startINetMock(_ *cobra.Command, _ []string) {
zap.String("signal", s.String()), zap.String("signal", s.String()),
) )
rpcAPI.StopServer()
endpointManager.ShutdownEndpoints() endpointManager.ShutdownEndpoints()
} }