├── .dockerignore ├── .gitignore ├── Dockerfile ├── Jenkinsfile ├── LICENSE ├── Makefile ├── README.md ├── build └── 0-protoc.mk ├── cmd ├── greeter │ ├── client.go │ ├── init.go │ └── main.go └── user │ ├── add.go │ ├── find.go │ ├── init.go │ ├── list.go │ └── main.go ├── deploy └── go-project-skeleton │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── deployment.yaml │ ├── ingress.yaml │ ├── service.yaml │ └── tests │ │ └── test-connection.yaml │ └── values.yaml ├── gen.go ├── go.mod ├── go.sum ├── pkg ├── api │ ├── greeter │ │ ├── api.pb.go │ │ ├── api.proto │ │ ├── mock │ │ │ └── api.go │ │ └── service.go │ └── user │ │ ├── api.pb.go │ │ ├── api.proto │ │ ├── mock │ │ └── api.go │ │ ├── service.go │ │ └── service_options.go └── app │ ├── actors.go │ ├── grpc_server.go │ ├── run.go │ ├── signal.go │ └── types.go └── tools ├── mockgen └── protoc-gen-go /.dockerignore: -------------------------------------------------------------------------------- 1 | README.md 2 | LICENSE 3 | /build 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | /build/*/ 15 | !/build/*.mk -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # First stage container 2 | FROM golang:1.12-alpine as builder 3 | 4 | ENV GO111MODULE=on 5 | RUN apk add --no-cache make git 6 | 7 | RUN mkdir -p /src 8 | WORKDIR /src 9 | 10 | COPY go.mod . 11 | COPY go.sum . 12 | 13 | RUN go mod download 14 | COPY . . 15 | 16 | ARG APP 17 | RUN make ${APP} && mkdir -p /build/bin && mv build/bin/* /build/bin 18 | 19 | # Second stage container 20 | FROM alpine:3.9 21 | 22 | RUN apk add --no-cache ca-certificates 23 | COPY --from=builder /build/bin/* /usr/local/bin/ 24 | 25 | # Define your entrypoint or command 26 | # ENTRYPOINT [""] 27 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alanchchen/go-project-skeleton/cb46a2d383b671f7e7f7315b7ca5902d345ac8a6/Jenkinsfile -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Alan Chen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERBOSE_ORIGINS := "command line" "environment" 2 | ifdef V 3 | ifeq ($(filter $(VERBOSE_ORIGINS),$(origin V)),) 4 | BUILD_VERBOSE := $(V) 5 | endif 6 | endif 7 | 8 | ifndef BUILD_VERBOSE 9 | BUILD_VERBOSE := 0 10 | endif 11 | 12 | ifeq ($(BUILD_VERBOSE),1) 13 | Q := 14 | else 15 | Q := @ 16 | endif 17 | 18 | PHONY += all test clean docker docker-push dockers dockers-push 19 | CURDIR := $(shell pwd) 20 | BUILD_DIR ?= $(CURDIR)/build 21 | GOBIN_DIR := $(BUILD_DIR)/bin 22 | HOST_DIR := $(BUILD_DIR)/host 23 | HOSTBIN_DIR := $(HOST_DIR)/bin 24 | GOTOOLSBIN_DIR := $(HOSTBIN_DIR) 25 | GOTOOLS_DIR := $(CURDIR)/tools 26 | TMP_DIR := $(BUILD_DIR)/tmp 27 | DIRS := \ 28 | $(GOBIN_DIR) \ 29 | $(HOST_DIR) \ 30 | $(HOSTBIN_DIR) \ 31 | $(TMP_DIR) 32 | 33 | HOST_OS := $(shell uname -s) 34 | 35 | # Define your docker repository 36 | DOCKER_REPOSITORY ?= quay.io/alan/$(notdir $(CURDIR)) 37 | REV ?= $(shell git rev-parse --short HEAD 2> /dev/null) 38 | GOPATH ?= $(shell go env GOPATH) 39 | 40 | export PATH:=$(GOTOOLS_DIR):$(HOSTBIN_DIR):$(PATH) 41 | export REV 42 | 43 | define app-docker-image-name 44 | $(if $(filter-out all,$(1)), \ 45 | $(DOCKER_REPOSITORY)-$(1):$(REV), \ 46 | $(DOCKER_REPOSITORY):$(REV)\ 47 | ) 48 | endef 49 | 50 | define find-subdir 51 | $(shell find $(1) -maxdepth 1 -mindepth 1 -type d -o -type l) 52 | endef 53 | 54 | APPS := $(sort $(notdir $(call find-subdir,cmd))) 55 | PHONY += $(APPS) 56 | 57 | all: $(APPS) 58 | 59 | .SECONDEXPANSION: 60 | $(APPS): $(addprefix $(GOBIN_DIR)/,$$@) 61 | 62 | $(DIRS) : 63 | $(Q)mkdir -p $@ 64 | 65 | $(GOBIN_DIR)/%: $(GOBIN_DIR) FORCE 66 | $(Q)go build -o $@ ./cmd/$(notdir $@) 67 | @echo "Done building." 68 | @echo "Run \"$(subst $(CURDIR),.,$@)\" to launch $(notdir $@)." 69 | 70 | include $(wildcard build/*.mk) 71 | 72 | CODEGEN_DEPS := \ 73 | $(GOTOOLS_DIR)/mockgen \ 74 | $(GOTOOLS_DIR)/protoc-gen-go \ 75 | $(PROTOC) 76 | 77 | .PHONY: gen 78 | gen: $(CODEGEN_DEPS) 79 | $(Q)go generate -x ./... 80 | 81 | dockers: $(addsuffix -docker,$(APPS)) 82 | %-docker: 83 | $(eval APP=$(subst -docker,,$@)) 84 | $(Q)docker build --build-arg APP=$(APP) -t $(call app-docker-image-name,$(APP)) . 85 | 86 | docker: 87 | $(Q)docker build -t $(DOCKER_REPOSITORY):$(REV) . 88 | 89 | dockers-push: $(addsuffix -docker-push,$(APPS)) 90 | %-docker-push: 91 | $(eval APP=$(subst -docker,,$@)) 92 | $(Q)docker push $(call app-docker-image-name,$(APP)) 93 | 94 | docker-push: 95 | $(Q)docker push $(DOCKER_REPOSITORY):$(REV) 96 | 97 | test: 98 | $(Q)go test -v ./... 99 | 100 | clean: 101 | $(Q)rm -fr $(GOBIN_DIR) $(HOST_DIR) 102 | 103 | .PHONY: help 104 | help: 105 | @echo 'Generic targets:' 106 | @echo ' all - Build all targets marked with [*]' 107 | @for app in $(APPS); do \ 108 | printf "* %s\n" $$app; done 109 | @echo '' 110 | @echo 'Code generation targets:' 111 | @echo ' gen - Generate API code from .proto files and mocks' 112 | @echo '' 113 | @echo 'Docker targets:' 114 | @echo ' dockers - Build docker images marked with [*]' 115 | @for app in $(APPS); do \ 116 | printf "* %-20s - Build %s\n" $$app-docker $(call app-docker-image-name,$$app); done 117 | @echo ' docker - Build single docker image which includes all executables' 118 | @echo '' 119 | @echo ' dockers-push - Push docker images marked with [*]' 120 | @for app in $(APPS); do \ 121 | printf "* %-20s - Push %s\n" $$app-docker-push $(call app-docker-image-name,$$app); done 122 | @echo ' docker-push - Push $(DOCKER_REPOSITORY):$(REV)' 123 | @echo '' 124 | @echo 'Test targets:' 125 | @echo ' test - Run all tests' 126 | @echo '' 127 | @echo 'Cleaning targets:' 128 | @echo ' clean - Remove built executables' 129 | @echo '' 130 | @echo 'Execute "make" or "make all" to build all targets marked with [*] ' 131 | @echo 'For further info see the ./README.md file' 132 | 133 | .PHONY: $(PHONY) 134 | 135 | .PHONY: FORCE 136 | FORCE: 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-project-skeleton 2 | My Go project skeleton 3 | -------------------------------------------------------------------------------- /build/0-protoc.mk: -------------------------------------------------------------------------------- 1 | protoc_version = 3.6.1 2 | 3 | ifeq ($(HOST_OS), Darwin) 4 | protoc_suffix = osx 5 | else 6 | ifeq ($(HOST_OS), Linux) 7 | protoc_suffix = linux 8 | else 9 | $(error Unsupported Host OS) 10 | endif 11 | endif 12 | 13 | remote_protoc_zip = https://github.com/protocolbuffers/protobuf/releases/download/v$(protoc_version)/protoc-$(protoc_version)-$(protoc_suffix)-x86_64.zip 14 | local_protoc_zip = $(TMP_DIR)/$(notdir $(remote_protoc_zip)) 15 | 16 | $(local_protoc_zip): $(TMP_DIR) 17 | @curl -sL $(remote_protoc_zip) -o $(local_protoc_zip) 18 | 19 | PROTOC := $(HOSTBIN_DIR)/protoc 20 | $(PROTOC): $(local_protoc_zip) $(HOSTBIN_DIR) 21 | @unzip -oXq $(local_protoc_zip) -d $(dir $(HOSTBIN_DIR)) 22 | 23 | PROTOC_INCLUDE_DIR := $(HOST_DIR)/include 24 | -------------------------------------------------------------------------------- /cmd/greeter/client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | _ "github.com/joho/godotenv/autoload" 8 | "google.golang.org/grpc" 9 | 10 | "github.com/alanchchen/go-project-skeleton/pkg/api/greeter" 11 | "github.com/alanchchen/go-project-skeleton/pkg/app" 12 | ) 13 | 14 | func init() { 15 | rootCmd.AddCommand(clientCmd) 16 | 17 | clientCmd.Flags().String("name", "", "Tell the server who you are") 18 | } 19 | 20 | var clientCmd = &app.Command{ 21 | Use: "client", 22 | Short: "client is a greeter client", 23 | Long: "client is a greeter client", 24 | RunE: func(cmd *app.Command, args []string) error { 25 | return app.RunCustom(cmd, args, func(cfg EndpointConfig, appCfg *app.Config) error { 26 | conn, err := grpc.Dial(cfg.Endpoint(), grpc.WithInsecure()) 27 | if err != nil { 28 | return err 29 | } 30 | 31 | client := greeter.NewServiceClient(conn) 32 | 33 | resp, err := client.SayHello(context.Background(), &greeter.HelloRequest{ 34 | Name: appCfg.GetString("name"), 35 | }) 36 | if err != nil { 37 | return err 38 | } 39 | 40 | fmt.Println("Server says:", resp.Message) 41 | return nil 42 | }) 43 | }, 44 | } 45 | -------------------------------------------------------------------------------- /cmd/greeter/init.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | "google.golang.org/grpc" 8 | 9 | "github.com/alanchchen/go-project-skeleton/pkg/api/greeter" 10 | "github.com/alanchchen/go-project-skeleton/pkg/app" 11 | ) 12 | 13 | type EndpointConfig struct { 14 | app.Input 15 | 16 | Host string `name:"api.host"` 17 | Port int `name:"api.port"` 18 | } 19 | 20 | func (cfg EndpointConfig) Endpoint() string { 21 | return net.JoinHostPort(cfg.Host, fmt.Sprintf("%d", cfg.Port)) 22 | } 23 | 24 | func NewTCPSocket(cfg EndpointConfig) (net.Listener, error) { 25 | return net.Listen("tcp", cfg.Endpoint()) 26 | } 27 | 28 | func NewRPCServer() *grpc.Server { 29 | svc := greeter.NewService() 30 | server := grpc.NewServer() 31 | svc.Bind(server) 32 | return server 33 | } 34 | -------------------------------------------------------------------------------- /cmd/greeter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | 8 | "github.com/alanchchen/go-project-skeleton/pkg/app" 9 | ) 10 | 11 | func init() { 12 | // Setup flags 13 | rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", fmt.Sprintf("config file (default is ./%s.yaml)", appName)) 14 | 15 | rootCmd.PersistentFlags().String("api.host", "", "the grpc server listening host") 16 | rootCmd.PersistentFlags().Int("api.port", 8088, "the grpc server listening port") 17 | } 18 | 19 | const ( 20 | appName = "greeter" 21 | ) 22 | 23 | var cfgFile string 24 | var logger = log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile) 25 | 26 | // rootCmd is the root command 27 | var rootCmd = &app.Command{ 28 | Use: appName, 29 | Short: appName + " is an RPC server", 30 | Long: appName + " is an RPC server", 31 | RunE: func(cmd *app.Command, args []string) error { 32 | initializers := []interface{}{ 33 | // actors 34 | app.NewGRPCServerActor, 35 | app.NewSignalActor, 36 | 37 | // actors' dependencies 38 | NewRPCServer, 39 | NewTCPSocket, 40 | func() app.Logger { 41 | return logger 42 | }, 43 | } 44 | 45 | return app.Run(cmd, args, initializers...) 46 | }, 47 | } 48 | 49 | func main() { 50 | if err := rootCmd.Execute(); err != nil { 51 | logger.Println(err) 52 | os.Exit(1) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /cmd/user/add.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | 8 | _ "github.com/joho/godotenv/autoload" 9 | 10 | "github.com/alanchchen/go-project-skeleton/pkg/api/user" 11 | "github.com/alanchchen/go-project-skeleton/pkg/app" 12 | ) 13 | 14 | func init() { 15 | rootCmd.AddCommand(addUserCommand) 16 | 17 | addUserCommand.Flags().String("name", "", "the user name") 18 | } 19 | 20 | var addUserCommand = &app.Command{ 21 | Use: "add", 22 | Short: "adds an new user", 23 | Long: "adds an new user", 24 | RunE: func(cmd *app.Command, args []string) error { 25 | initializers := []interface{}{ 26 | NewConnection, 27 | NewClient, 28 | } 29 | 30 | return app.RunCustom(cmd, args, func(client user.ServiceClient, cfg *app.Config) error { 31 | resp, err := client.AddUser(context.Background(), &user.AddUserRequest{ 32 | Name: cfg.GetString("name"), 33 | }) 34 | if err != nil { 35 | return err 36 | } 37 | 38 | rawData, err := json.MarshalIndent(resp, "", " ") 39 | if err != nil { 40 | return err 41 | } 42 | 43 | fmt.Println(string(rawData)) 44 | 45 | return nil 46 | }, initializers...) 47 | }, 48 | } 49 | -------------------------------------------------------------------------------- /cmd/user/find.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | 8 | _ "github.com/joho/godotenv/autoload" 9 | 10 | "github.com/alanchchen/go-project-skeleton/pkg/api/user" 11 | "github.com/alanchchen/go-project-skeleton/pkg/app" 12 | ) 13 | 14 | func init() { 15 | rootCmd.AddCommand(findUserCommand) 16 | 17 | findUserCommand.Flags().String("name", "", "the user name") 18 | findUserCommand.Flags().Int64("id", -1, "the user id") 19 | } 20 | 21 | var findUserCommand = &app.Command{ 22 | Use: "find", 23 | Short: "finds an new user by ID or name", 24 | Long: "finds an new user by ID or name", 25 | RunE: func(cmd *app.Command, args []string) error { 26 | initializers := []interface{}{ 27 | NewConnection, 28 | NewClient, 29 | } 30 | 31 | return app.RunCustom(cmd, args, func(client user.ServiceClient, cfg *app.Config) error { 32 | var resp *user.Users 33 | var err error 34 | 35 | if name := cfg.GetString("name"); name != "" { 36 | resp, err = client.FindUserByName(context.Background(), &user.FindUserByNameRequest{ 37 | Name: name, 38 | }) 39 | } 40 | if id := cfg.GetInt64("id"); id >= 0 { 41 | resp, err = client.FindUserById(context.Background(), &user.FindUserByIdRequest{ 42 | Id: id, 43 | }) 44 | } 45 | 46 | if err != nil { 47 | return err 48 | } 49 | 50 | rawData, err := json.MarshalIndent(resp, "", " ") 51 | if err != nil { 52 | return err 53 | } 54 | 55 | fmt.Println(string(rawData)) 56 | 57 | return nil 58 | }, initializers...) 59 | }, 60 | } 61 | -------------------------------------------------------------------------------- /cmd/user/init.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | "google.golang.org/grpc" 8 | 9 | "github.com/alanchchen/go-project-skeleton/pkg/api/user" 10 | "github.com/alanchchen/go-project-skeleton/pkg/app" 11 | ) 12 | 13 | type EndpointConfig struct { 14 | app.Input 15 | 16 | Host string `name:"api.host"` 17 | Port int `name:"api.port"` 18 | } 19 | 20 | func (cfg EndpointConfig) Endpoint() string { 21 | return net.JoinHostPort(cfg.Host, fmt.Sprintf("%d", cfg.Port)) 22 | } 23 | 24 | func NewConnection(cfg EndpointConfig) (*grpc.ClientConn, error) { 25 | return grpc.Dial(cfg.Endpoint(), grpc.WithInsecure()) 26 | } 27 | 28 | func NewClient(conn *grpc.ClientConn) user.ServiceClient { 29 | return user.NewServiceClient(conn) 30 | } 31 | 32 | func NewTCPSocket(cfg EndpointConfig) (net.Listener, error) { 33 | return net.Listen("tcp", cfg.Endpoint()) 34 | } 35 | 36 | func NewRPCServer() *grpc.Server { 37 | svc := user.NewService() 38 | server := grpc.NewServer() 39 | svc.Bind(server) 40 | return server 41 | } 42 | -------------------------------------------------------------------------------- /cmd/user/list.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "fmt" 7 | 8 | "github.com/golang/protobuf/ptypes/empty" 9 | _ "github.com/joho/godotenv/autoload" 10 | 11 | "github.com/alanchchen/go-project-skeleton/pkg/api/user" 12 | "github.com/alanchchen/go-project-skeleton/pkg/app" 13 | ) 14 | 15 | func init() { 16 | rootCmd.AddCommand(listUsersCommand) 17 | } 18 | 19 | var listUsersCommand = &app.Command{ 20 | Use: "list", 21 | Short: "list all users", 22 | Long: "list all users", 23 | RunE: func(cmd *app.Command, args []string) error { 24 | initializers := []interface{}{ 25 | NewConnection, 26 | NewClient, 27 | } 28 | 29 | return app.RunCustom(cmd, args, func(client user.ServiceClient) error { 30 | resp, err := client.ListUsers(context.Background(), &empty.Empty{}) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | rawData, err := json.MarshalIndent(resp, "", " ") 36 | if err != nil { 37 | return err 38 | } 39 | 40 | fmt.Println(string(rawData)) 41 | 42 | return nil 43 | }, initializers...) 44 | }, 45 | } 46 | -------------------------------------------------------------------------------- /cmd/user/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | 8 | _ "github.com/joho/godotenv/autoload" 9 | 10 | "github.com/alanchchen/go-project-skeleton/pkg/app" 11 | ) 12 | 13 | func init() { 14 | // Setup flags 15 | rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", fmt.Sprintf("config file (default is ./%s.yaml)", appName)) 16 | 17 | rootCmd.PersistentFlags().String("api.host", "", "the grpc server listening host") 18 | rootCmd.PersistentFlags().Int("api.port", 8088, "the grpc server listening port") 19 | } 20 | 21 | const ( 22 | appName = "user" 23 | ) 24 | 25 | var cfgFile string 26 | var logger = log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile) 27 | 28 | // rootCmd is the root command 29 | var rootCmd = &app.Command{ 30 | Use: appName, 31 | Short: appName + " is an RPC server", 32 | Long: appName + " is an RPC server", 33 | RunE: func(cmd *app.Command, args []string) error { 34 | initializers := []interface{}{ 35 | // actors 36 | app.NewGRPCServerActor, 37 | app.NewSignalActor, 38 | 39 | // actors' dependencies 40 | NewRPCServer, 41 | NewTCPSocket, 42 | func() app.Logger { 43 | return logger 44 | }, 45 | } 46 | 47 | return app.Run(cmd, args, initializers...) 48 | }, 49 | } 50 | 51 | func main() { 52 | if err := rootCmd.Execute(); err != nil { 53 | logger.Println(err) 54 | os.Exit(1) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /deploy/go-project-skeleton/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | .vscode/ 23 | -------------------------------------------------------------------------------- /deploy/go-project-skeleton/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | appVersion: "1.0" 3 | description: A Helm chart for Kubernetes 4 | name: go-project-skeleton 5 | version: 0.1.0 6 | -------------------------------------------------------------------------------- /deploy/go-project-skeleton/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 1. Get the application URL by running these commands: 2 | {{- if .Values.ingress.enabled }} 3 | {{- range $host := .Values.ingress.hosts }} 4 | {{- range $.Values.ingress.paths }} 5 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host }}{{ . }} 6 | {{- end }} 7 | {{- end }} 8 | {{- else if contains "NodePort" .Values.service.type }} 9 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "go-project-skeleton.fullname" . }}) 10 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") 11 | echo http://$NODE_IP:$NODE_PORT 12 | {{- else if contains "LoadBalancer" .Values.service.type }} 13 | NOTE: It may take a few minutes for the LoadBalancer IP to be available. 14 | You can watch the status of by running 'kubectl get svc -w {{ include "go-project-skeleton.fullname" . }}' 15 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "go-project-skeleton.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') 16 | echo http://$SERVICE_IP:{{ .Values.service.port }} 17 | {{- else if contains "ClusterIP" .Values.service.type }} 18 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "go-project-skeleton.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") 19 | echo "Visit http://127.0.0.1:8080 to use your application" 20 | kubectl port-forward $POD_NAME 8080:80 21 | {{- end }} 22 | -------------------------------------------------------------------------------- /deploy/go-project-skeleton/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "go-project-skeleton.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "go-project-skeleton.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "go-project-skeleton.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | -------------------------------------------------------------------------------- /deploy/go-project-skeleton/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ include "go-project-skeleton.fullname" . }} 5 | labels: 6 | app.kubernetes.io/name: {{ include "go-project-skeleton.name" . }} 7 | helm.sh/chart: {{ include "go-project-skeleton.chart" . }} 8 | app.kubernetes.io/instance: {{ .Release.Name }} 9 | app.kubernetes.io/managed-by: {{ .Release.Service }} 10 | spec: 11 | replicas: {{ .Values.replicaCount }} 12 | selector: 13 | matchLabels: 14 | app.kubernetes.io/name: {{ include "go-project-skeleton.name" . }} 15 | app.kubernetes.io/instance: {{ .Release.Name }} 16 | template: 17 | metadata: 18 | labels: 19 | app.kubernetes.io/name: {{ include "go-project-skeleton.name" . }} 20 | app.kubernetes.io/instance: {{ .Release.Name }} 21 | spec: 22 | containers: 23 | - name: {{ .Chart.Name }} 24 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" 25 | imagePullPolicy: {{ .Values.image.pullPolicy }} 26 | ports: 27 | - name: http 28 | containerPort: 80 29 | protocol: TCP 30 | livenessProbe: 31 | httpGet: 32 | path: / 33 | port: http 34 | readinessProbe: 35 | httpGet: 36 | path: / 37 | port: http 38 | resources: 39 | {{- toYaml .Values.resources | nindent 12 }} 40 | {{- with .Values.nodeSelector }} 41 | nodeSelector: 42 | {{- toYaml . | nindent 8 }} 43 | {{- end }} 44 | {{- with .Values.affinity }} 45 | affinity: 46 | {{- toYaml . | nindent 8 }} 47 | {{- end }} 48 | {{- with .Values.tolerations }} 49 | tolerations: 50 | {{- toYaml . | nindent 8 }} 51 | {{- end }} 52 | -------------------------------------------------------------------------------- /deploy/go-project-skeleton/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "go-project-skeleton.fullname" . -}} 3 | {{- $ingressPaths := .Values.ingress.paths -}} 4 | apiVersion: extensions/v1beta1 5 | kind: Ingress 6 | metadata: 7 | name: {{ $fullName }} 8 | labels: 9 | app.kubernetes.io/name: {{ include "go-project-skeleton.name" . }} 10 | helm.sh/chart: {{ include "go-project-skeleton.chart" . }} 11 | app.kubernetes.io/instance: {{ .Release.Name }} 12 | app.kubernetes.io/managed-by: {{ .Release.Service }} 13 | {{- with .Values.ingress.annotations }} 14 | annotations: 15 | {{- toYaml . | nindent 4 }} 16 | {{- end }} 17 | spec: 18 | {{- if .Values.ingress.tls }} 19 | tls: 20 | {{- range .Values.ingress.tls }} 21 | - hosts: 22 | {{- range .hosts }} 23 | - {{ . | quote }} 24 | {{- end }} 25 | secretName: {{ .secretName }} 26 | {{- end }} 27 | {{- end }} 28 | rules: 29 | {{- range .Values.ingress.hosts }} 30 | - host: {{ . | quote }} 31 | http: 32 | paths: 33 | {{- range $ingressPaths }} 34 | - path: {{ . }} 35 | backend: 36 | serviceName: {{ $fullName }} 37 | servicePort: http 38 | {{- end }} 39 | {{- end }} 40 | {{- end }} 41 | -------------------------------------------------------------------------------- /deploy/go-project-skeleton/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "go-project-skeleton.fullname" . }} 5 | labels: 6 | app.kubernetes.io/name: {{ include "go-project-skeleton.name" . }} 7 | helm.sh/chart: {{ include "go-project-skeleton.chart" . }} 8 | app.kubernetes.io/instance: {{ .Release.Name }} 9 | app.kubernetes.io/managed-by: {{ .Release.Service }} 10 | spec: 11 | type: {{ .Values.service.type }} 12 | ports: 13 | - port: {{ .Values.service.port }} 14 | targetPort: http 15 | protocol: TCP 16 | name: http 17 | selector: 18 | app.kubernetes.io/name: {{ include "go-project-skeleton.name" . }} 19 | app.kubernetes.io/instance: {{ .Release.Name }} 20 | -------------------------------------------------------------------------------- /deploy/go-project-skeleton/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: "{{ include "go-project-skeleton.fullname" . }}-test-connection" 5 | labels: 6 | app.kubernetes.io/name: {{ include "go-project-skeleton.name" . }} 7 | helm.sh/chart: {{ include "go-project-skeleton.chart" . }} 8 | app.kubernetes.io/instance: {{ .Release.Name }} 9 | app.kubernetes.io/managed-by: {{ .Release.Service }} 10 | annotations: 11 | "helm.sh/hook": test-success 12 | spec: 13 | containers: 14 | - name: wget 15 | image: busybox 16 | command: ['wget'] 17 | args: ['{{ include "go-project-skeleton.fullname" . }}:{{ .Values.service.port }}'] 18 | restartPolicy: Never 19 | -------------------------------------------------------------------------------- /deploy/go-project-skeleton/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for go-project-skeleton. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | replicaCount: 1 6 | 7 | image: 8 | repository: nginx 9 | tag: stable 10 | pullPolicy: IfNotPresent 11 | 12 | nameOverride: "" 13 | fullnameOverride: "" 14 | 15 | service: 16 | type: ClusterIP 17 | port: 80 18 | 19 | ingress: 20 | enabled: false 21 | annotations: {} 22 | # kubernetes.io/ingress.class: nginx 23 | # kubernetes.io/tls-acme: "true" 24 | paths: [] 25 | hosts: 26 | - chart-example.local 27 | tls: [] 28 | # - secretName: chart-example-tls 29 | # hosts: 30 | # - chart-example.local 31 | 32 | resources: {} 33 | # We usually recommend not to specify default resources and to leave this as a conscious 34 | # choice for the user. This also increases chances charts run on environments with little 35 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 36 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 37 | # limits: 38 | # cpu: 100m 39 | # memory: 128Mi 40 | # requests: 41 | # cpu: 100m 42 | # memory: 128Mi 43 | 44 | nodeSelector: {} 45 | 46 | tolerations: [] 47 | 48 | affinity: {} 49 | -------------------------------------------------------------------------------- /gen.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | //go:generate protoc -I. --go_out=plugins=grpc:. pkg/api/greeter/api.proto 4 | //go:generate protoc -I. --go_out=plugins=grpc:. pkg/api/user/api.proto 5 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/alanchchen/go-project-skeleton 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/golang/mock v1.3.1 7 | github.com/golang/protobuf v1.3.1 8 | github.com/joho/godotenv v1.3.0 9 | github.com/oklog/run v1.0.0 10 | github.com/spf13/cobra v0.0.5 11 | github.com/spf13/viper v1.4.0 12 | go.uber.org/dig v1.7.0 13 | google.golang.org/grpc v1.21.1 14 | ) 15 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 3 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 4 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 5 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 6 | github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= 7 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 8 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 9 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 10 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 11 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 12 | github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 13 | github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= 14 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 15 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 16 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 17 | github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= 18 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 19 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 20 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 21 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 22 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 23 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 24 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 25 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 26 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 27 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 28 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 29 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 30 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 31 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 32 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 33 | github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= 34 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 35 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 36 | github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= 37 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 38 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 39 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 40 | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 41 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 42 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 43 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 44 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 45 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 46 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 47 | github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= 48 | github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= 49 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 50 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 51 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 52 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 53 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 54 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 55 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 56 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 57 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 58 | github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= 59 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 60 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 61 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 62 | github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= 63 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 64 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 65 | github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= 66 | github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= 67 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 68 | github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= 69 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 70 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 71 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 72 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 73 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 74 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 75 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 76 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 77 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 78 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 79 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 80 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 81 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 82 | github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= 83 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 84 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 85 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 86 | github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= 87 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 88 | github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= 89 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 90 | github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= 91 | github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= 92 | github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= 93 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 94 | github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= 95 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 96 | github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= 97 | github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= 98 | github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= 99 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 100 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 101 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 102 | github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= 103 | github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= 104 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 105 | github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= 106 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 107 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 108 | go.uber.org/dig v1.7.0 h1:E5/L92iQTNJTjfgJF2KgU+/JpMaiuvK2DHLBj0+kSZk= 109 | go.uber.org/dig v1.7.0/go.mod h1:z+dSd2TP9Usi48jL8M3v63iSBVkiwtVyMKxMZYYauPg= 110 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 111 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 112 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 113 | golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 114 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 115 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 116 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 117 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 118 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 119 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 120 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 121 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco= 122 | golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 123 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 124 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 125 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 126 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 127 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 128 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 129 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 130 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 131 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 132 | golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 133 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= 134 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 135 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= 136 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 137 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 138 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 139 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 140 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 141 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262 h1:qsl9y/CJx34tuA7QCPNp86JNJe4spst6Ff8MjvPUdPg= 142 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 143 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 144 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= 145 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 146 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 147 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 148 | google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8= 149 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 150 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 151 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 152 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 153 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 154 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 155 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 156 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 157 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 158 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 159 | -------------------------------------------------------------------------------- /pkg/api/greeter/api.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: pkg/api/greeter/api.proto 3 | 4 | package greeter 5 | 6 | import ( 7 | context "context" 8 | fmt "fmt" 9 | proto "github.com/golang/protobuf/proto" 10 | grpc "google.golang.org/grpc" 11 | math "math" 12 | ) 13 | 14 | // Reference imports to suppress errors if they are not otherwise used. 15 | var _ = proto.Marshal 16 | var _ = fmt.Errorf 17 | var _ = math.Inf 18 | 19 | // This is a compile-time assertion to ensure that this generated file 20 | // is compatible with the proto package it is being compiled against. 21 | // A compilation error at this line likely means your copy of the 22 | // proto package needs to be updated. 23 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 24 | 25 | // The request message containing the user's name. 26 | type HelloRequest struct { 27 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 28 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 29 | XXX_unrecognized []byte `json:"-"` 30 | XXX_sizecache int32 `json:"-"` 31 | } 32 | 33 | func (m *HelloRequest) Reset() { *m = HelloRequest{} } 34 | func (m *HelloRequest) String() string { return proto.CompactTextString(m) } 35 | func (*HelloRequest) ProtoMessage() {} 36 | func (*HelloRequest) Descriptor() ([]byte, []int) { 37 | return fileDescriptor_3872d9850a7378a2, []int{0} 38 | } 39 | 40 | func (m *HelloRequest) XXX_Unmarshal(b []byte) error { 41 | return xxx_messageInfo_HelloRequest.Unmarshal(m, b) 42 | } 43 | func (m *HelloRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 44 | return xxx_messageInfo_HelloRequest.Marshal(b, m, deterministic) 45 | } 46 | func (m *HelloRequest) XXX_Merge(src proto.Message) { 47 | xxx_messageInfo_HelloRequest.Merge(m, src) 48 | } 49 | func (m *HelloRequest) XXX_Size() int { 50 | return xxx_messageInfo_HelloRequest.Size(m) 51 | } 52 | func (m *HelloRequest) XXX_DiscardUnknown() { 53 | xxx_messageInfo_HelloRequest.DiscardUnknown(m) 54 | } 55 | 56 | var xxx_messageInfo_HelloRequest proto.InternalMessageInfo 57 | 58 | func (m *HelloRequest) GetName() string { 59 | if m != nil { 60 | return m.Name 61 | } 62 | return "" 63 | } 64 | 65 | // The response message containing the greetings 66 | type HelloReply struct { 67 | Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` 68 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 69 | XXX_unrecognized []byte `json:"-"` 70 | XXX_sizecache int32 `json:"-"` 71 | } 72 | 73 | func (m *HelloReply) Reset() { *m = HelloReply{} } 74 | func (m *HelloReply) String() string { return proto.CompactTextString(m) } 75 | func (*HelloReply) ProtoMessage() {} 76 | func (*HelloReply) Descriptor() ([]byte, []int) { 77 | return fileDescriptor_3872d9850a7378a2, []int{1} 78 | } 79 | 80 | func (m *HelloReply) XXX_Unmarshal(b []byte) error { 81 | return xxx_messageInfo_HelloReply.Unmarshal(m, b) 82 | } 83 | func (m *HelloReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 84 | return xxx_messageInfo_HelloReply.Marshal(b, m, deterministic) 85 | } 86 | func (m *HelloReply) XXX_Merge(src proto.Message) { 87 | xxx_messageInfo_HelloReply.Merge(m, src) 88 | } 89 | func (m *HelloReply) XXX_Size() int { 90 | return xxx_messageInfo_HelloReply.Size(m) 91 | } 92 | func (m *HelloReply) XXX_DiscardUnknown() { 93 | xxx_messageInfo_HelloReply.DiscardUnknown(m) 94 | } 95 | 96 | var xxx_messageInfo_HelloReply proto.InternalMessageInfo 97 | 98 | func (m *HelloReply) GetMessage() string { 99 | if m != nil { 100 | return m.Message 101 | } 102 | return "" 103 | } 104 | 105 | func init() { 106 | proto.RegisterType((*HelloRequest)(nil), "greeter.HelloRequest") 107 | proto.RegisterType((*HelloReply)(nil), "greeter.HelloReply") 108 | } 109 | 110 | func init() { proto.RegisterFile("pkg/api/greeter/api.proto", fileDescriptor_3872d9850a7378a2) } 111 | 112 | var fileDescriptor_3872d9850a7378a2 = []byte{ 113 | // 156 bytes of a gzipped FileDescriptorProto 114 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2c, 0xc8, 0x4e, 0xd7, 115 | 0x4f, 0x2c, 0xc8, 0xd4, 0x4f, 0x2f, 0x4a, 0x4d, 0x2d, 0x49, 0x2d, 0x02, 0xb1, 0xf5, 0x0a, 0x8a, 116 | 0xf2, 0x4b, 0xf2, 0x85, 0xd8, 0xa1, 0x42, 0x4a, 0x4a, 0x5c, 0x3c, 0x1e, 0xa9, 0x39, 0x39, 0xf9, 117 | 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25, 0x42, 0x42, 0x5c, 0x2c, 0x79, 0x89, 0xb9, 0xa9, 0x12, 118 | 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x60, 0xb6, 0x92, 0x1a, 0x17, 0x17, 0x54, 0x4d, 0x41, 0x4e, 119 | 0xa5, 0x90, 0x04, 0x17, 0x7b, 0x6e, 0x6a, 0x71, 0x71, 0x62, 0x3a, 0x4c, 0x11, 0x8c, 0x6b, 0xe4, 120 | 0xcc, 0xc5, 0x1e, 0x9c, 0x5a, 0x54, 0x96, 0x99, 0x9c, 0x2a, 0x64, 0xc1, 0xc5, 0x11, 0x9c, 0x58, 121 | 0x09, 0xd6, 0x25, 0x24, 0xaa, 0x07, 0xb5, 0x4c, 0x0f, 0xd9, 0x26, 0x29, 0x61, 0x74, 0xe1, 0x82, 122 | 0x9c, 0x4a, 0x25, 0x06, 0x27, 0xce, 0x28, 0x98, 0xdb, 0x92, 0xd8, 0xc0, 0x6e, 0x35, 0x06, 0x04, 123 | 0x00, 0x00, 0xff, 0xff, 0x68, 0x1e, 0xfa, 0x72, 0xc8, 0x00, 0x00, 0x00, 124 | } 125 | 126 | // Reference imports to suppress errors if they are not otherwise used. 127 | var _ context.Context 128 | var _ grpc.ClientConn 129 | 130 | // This is a compile-time assertion to ensure that this generated file 131 | // is compatible with the grpc package it is being compiled against. 132 | const _ = grpc.SupportPackageIsVersion4 133 | 134 | // ServiceClient is the client API for Service service. 135 | // 136 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 137 | type ServiceClient interface { 138 | // Sends a greeting 139 | SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) 140 | } 141 | 142 | type serviceClient struct { 143 | cc *grpc.ClientConn 144 | } 145 | 146 | func NewServiceClient(cc *grpc.ClientConn) ServiceClient { 147 | return &serviceClient{cc} 148 | } 149 | 150 | func (c *serviceClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) { 151 | out := new(HelloReply) 152 | err := c.cc.Invoke(ctx, "/greeter.Service/SayHello", in, out, opts...) 153 | if err != nil { 154 | return nil, err 155 | } 156 | return out, nil 157 | } 158 | 159 | // ServiceServer is the server API for Service service. 160 | type ServiceServer interface { 161 | // Sends a greeting 162 | SayHello(context.Context, *HelloRequest) (*HelloReply, error) 163 | } 164 | 165 | func RegisterServiceServer(s *grpc.Server, srv ServiceServer) { 166 | s.RegisterService(&_Service_serviceDesc, srv) 167 | } 168 | 169 | func _Service_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 170 | in := new(HelloRequest) 171 | if err := dec(in); err != nil { 172 | return nil, err 173 | } 174 | if interceptor == nil { 175 | return srv.(ServiceServer).SayHello(ctx, in) 176 | } 177 | info := &grpc.UnaryServerInfo{ 178 | Server: srv, 179 | FullMethod: "/greeter.Service/SayHello", 180 | } 181 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 182 | return srv.(ServiceServer).SayHello(ctx, req.(*HelloRequest)) 183 | } 184 | return interceptor(ctx, in, info, handler) 185 | } 186 | 187 | var _Service_serviceDesc = grpc.ServiceDesc{ 188 | ServiceName: "greeter.Service", 189 | HandlerType: (*ServiceServer)(nil), 190 | Methods: []grpc.MethodDesc{ 191 | { 192 | MethodName: "SayHello", 193 | Handler: _Service_SayHello_Handler, 194 | }, 195 | }, 196 | Streams: []grpc.StreamDesc{}, 197 | Metadata: "pkg/api/greeter/api.proto", 198 | } 199 | -------------------------------------------------------------------------------- /pkg/api/greeter/api.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package greeter; 4 | option go_package = "greeter"; 5 | 6 | // The request message containing the user's name. 7 | message HelloRequest { 8 | string name = 1; 9 | } 10 | 11 | // The response message containing the greetings 12 | message HelloReply { 13 | string message = 1; 14 | } 15 | 16 | // The greeter service definition. 17 | service Service { 18 | // Sends a greeting 19 | rpc SayHello (HelloRequest) returns (HelloReply) {} 20 | } -------------------------------------------------------------------------------- /pkg/api/greeter/mock/api.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: api.pb.go 3 | 4 | // Package mock is a generated GoMock package. 5 | package mock 6 | 7 | import ( 8 | context "context" 9 | greeter "github.com/alanchchen/go-project-skeleton/pkg/api/greeter" 10 | gomock "github.com/golang/mock/gomock" 11 | grpc "google.golang.org/grpc" 12 | reflect "reflect" 13 | ) 14 | 15 | // MockServiceClient is a mock of ServiceClient interface 16 | type MockServiceClient struct { 17 | ctrl *gomock.Controller 18 | recorder *MockServiceClientMockRecorder 19 | } 20 | 21 | // MockServiceClientMockRecorder is the mock recorder for MockServiceClient 22 | type MockServiceClientMockRecorder struct { 23 | mock *MockServiceClient 24 | } 25 | 26 | // NewMockServiceClient creates a new mock instance 27 | func NewMockServiceClient(ctrl *gomock.Controller) *MockServiceClient { 28 | mock := &MockServiceClient{ctrl: ctrl} 29 | mock.recorder = &MockServiceClientMockRecorder{mock} 30 | return mock 31 | } 32 | 33 | // EXPECT returns an object that allows the caller to indicate expected use 34 | func (m *MockServiceClient) EXPECT() *MockServiceClientMockRecorder { 35 | return m.recorder 36 | } 37 | 38 | // SayHello mocks base method 39 | func (m *MockServiceClient) SayHello(ctx context.Context, in *greeter.HelloRequest, opts ...grpc.CallOption) (*greeter.HelloReply, error) { 40 | m.ctrl.T.Helper() 41 | varargs := []interface{}{ctx, in} 42 | for _, a := range opts { 43 | varargs = append(varargs, a) 44 | } 45 | ret := m.ctrl.Call(m, "SayHello", varargs...) 46 | ret0, _ := ret[0].(*greeter.HelloReply) 47 | ret1, _ := ret[1].(error) 48 | return ret0, ret1 49 | } 50 | 51 | // SayHello indicates an expected call of SayHello 52 | func (mr *MockServiceClientMockRecorder) SayHello(ctx, in interface{}, opts ...interface{}) *gomock.Call { 53 | mr.mock.ctrl.T.Helper() 54 | varargs := append([]interface{}{ctx, in}, opts...) 55 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SayHello", reflect.TypeOf((*MockServiceClient)(nil).SayHello), varargs...) 56 | } 57 | 58 | // MockServiceServer is a mock of ServiceServer interface 59 | type MockServiceServer struct { 60 | ctrl *gomock.Controller 61 | recorder *MockServiceServerMockRecorder 62 | } 63 | 64 | // MockServiceServerMockRecorder is the mock recorder for MockServiceServer 65 | type MockServiceServerMockRecorder struct { 66 | mock *MockServiceServer 67 | } 68 | 69 | // NewMockServiceServer creates a new mock instance 70 | func NewMockServiceServer(ctrl *gomock.Controller) *MockServiceServer { 71 | mock := &MockServiceServer{ctrl: ctrl} 72 | mock.recorder = &MockServiceServerMockRecorder{mock} 73 | return mock 74 | } 75 | 76 | // EXPECT returns an object that allows the caller to indicate expected use 77 | func (m *MockServiceServer) EXPECT() *MockServiceServerMockRecorder { 78 | return m.recorder 79 | } 80 | 81 | // SayHello mocks base method 82 | func (m *MockServiceServer) SayHello(arg0 context.Context, arg1 *greeter.HelloRequest) (*greeter.HelloReply, error) { 83 | m.ctrl.T.Helper() 84 | ret := m.ctrl.Call(m, "SayHello", arg0, arg1) 85 | ret0, _ := ret[0].(*greeter.HelloReply) 86 | ret1, _ := ret[1].(error) 87 | return ret0, ret1 88 | } 89 | 90 | // SayHello indicates an expected call of SayHello 91 | func (mr *MockServiceServerMockRecorder) SayHello(arg0, arg1 interface{}) *gomock.Call { 92 | mr.mock.ctrl.T.Helper() 93 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SayHello", reflect.TypeOf((*MockServiceServer)(nil).SayHello), arg0, arg1) 94 | } 95 | -------------------------------------------------------------------------------- /pkg/api/greeter/service.go: -------------------------------------------------------------------------------- 1 | package greeter 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | grpc "google.golang.org/grpc" 8 | ) 9 | 10 | //go:generate mockgen -source=api.pb.go -destination=mock/api.go -package=mock 11 | 12 | func NewService() *GreeterService { 13 | return &GreeterService{} 14 | } 15 | 16 | type GreeterService struct { 17 | } 18 | 19 | func (s *GreeterService) Bind(server *grpc.Server) { 20 | RegisterServiceServer(server, s) 21 | } 22 | 23 | func (s *GreeterService) SayHello(ctx context.Context, req *HelloRequest) (*HelloReply, error) { 24 | msg := "Hello " + req.Name 25 | defer fmt.Println(msg) 26 | 27 | return &HelloReply{ 28 | Message: msg, 29 | }, nil 30 | } 31 | -------------------------------------------------------------------------------- /pkg/api/user/api.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: pkg/api/user/api.proto 3 | 4 | package user 5 | 6 | import ( 7 | context "context" 8 | fmt "fmt" 9 | proto "github.com/golang/protobuf/proto" 10 | empty "github.com/golang/protobuf/ptypes/empty" 11 | grpc "google.golang.org/grpc" 12 | math "math" 13 | ) 14 | 15 | // Reference imports to suppress errors if they are not otherwise used. 16 | var _ = proto.Marshal 17 | var _ = fmt.Errorf 18 | var _ = math.Inf 19 | 20 | // This is a compile-time assertion to ensure that this generated file 21 | // is compatible with the proto package it is being compiled against. 22 | // A compilation error at this line likely means your copy of the 23 | // proto package needs to be updated. 24 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 25 | 26 | type User struct { 27 | Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` 28 | Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` 29 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 30 | XXX_unrecognized []byte `json:"-"` 31 | XXX_sizecache int32 `json:"-"` 32 | } 33 | 34 | func (m *User) Reset() { *m = User{} } 35 | func (m *User) String() string { return proto.CompactTextString(m) } 36 | func (*User) ProtoMessage() {} 37 | func (*User) Descriptor() ([]byte, []int) { 38 | return fileDescriptor_de1e0d3bf8cb5a8f, []int{0} 39 | } 40 | 41 | func (m *User) XXX_Unmarshal(b []byte) error { 42 | return xxx_messageInfo_User.Unmarshal(m, b) 43 | } 44 | func (m *User) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 45 | return xxx_messageInfo_User.Marshal(b, m, deterministic) 46 | } 47 | func (m *User) XXX_Merge(src proto.Message) { 48 | xxx_messageInfo_User.Merge(m, src) 49 | } 50 | func (m *User) XXX_Size() int { 51 | return xxx_messageInfo_User.Size(m) 52 | } 53 | func (m *User) XXX_DiscardUnknown() { 54 | xxx_messageInfo_User.DiscardUnknown(m) 55 | } 56 | 57 | var xxx_messageInfo_User proto.InternalMessageInfo 58 | 59 | func (m *User) GetId() int64 { 60 | if m != nil { 61 | return m.Id 62 | } 63 | return 0 64 | } 65 | 66 | func (m *User) GetName() string { 67 | if m != nil { 68 | return m.Name 69 | } 70 | return "" 71 | } 72 | 73 | type AddUserRequest struct { 74 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 75 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 76 | XXX_unrecognized []byte `json:"-"` 77 | XXX_sizecache int32 `json:"-"` 78 | } 79 | 80 | func (m *AddUserRequest) Reset() { *m = AddUserRequest{} } 81 | func (m *AddUserRequest) String() string { return proto.CompactTextString(m) } 82 | func (*AddUserRequest) ProtoMessage() {} 83 | func (*AddUserRequest) Descriptor() ([]byte, []int) { 84 | return fileDescriptor_de1e0d3bf8cb5a8f, []int{1} 85 | } 86 | 87 | func (m *AddUserRequest) XXX_Unmarshal(b []byte) error { 88 | return xxx_messageInfo_AddUserRequest.Unmarshal(m, b) 89 | } 90 | func (m *AddUserRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 91 | return xxx_messageInfo_AddUserRequest.Marshal(b, m, deterministic) 92 | } 93 | func (m *AddUserRequest) XXX_Merge(src proto.Message) { 94 | xxx_messageInfo_AddUserRequest.Merge(m, src) 95 | } 96 | func (m *AddUserRequest) XXX_Size() int { 97 | return xxx_messageInfo_AddUserRequest.Size(m) 98 | } 99 | func (m *AddUserRequest) XXX_DiscardUnknown() { 100 | xxx_messageInfo_AddUserRequest.DiscardUnknown(m) 101 | } 102 | 103 | var xxx_messageInfo_AddUserRequest proto.InternalMessageInfo 104 | 105 | func (m *AddUserRequest) GetName() string { 106 | if m != nil { 107 | return m.Name 108 | } 109 | return "" 110 | } 111 | 112 | type FindUserByIdRequest struct { 113 | Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` 114 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 115 | XXX_unrecognized []byte `json:"-"` 116 | XXX_sizecache int32 `json:"-"` 117 | } 118 | 119 | func (m *FindUserByIdRequest) Reset() { *m = FindUserByIdRequest{} } 120 | func (m *FindUserByIdRequest) String() string { return proto.CompactTextString(m) } 121 | func (*FindUserByIdRequest) ProtoMessage() {} 122 | func (*FindUserByIdRequest) Descriptor() ([]byte, []int) { 123 | return fileDescriptor_de1e0d3bf8cb5a8f, []int{2} 124 | } 125 | 126 | func (m *FindUserByIdRequest) XXX_Unmarshal(b []byte) error { 127 | return xxx_messageInfo_FindUserByIdRequest.Unmarshal(m, b) 128 | } 129 | func (m *FindUserByIdRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 130 | return xxx_messageInfo_FindUserByIdRequest.Marshal(b, m, deterministic) 131 | } 132 | func (m *FindUserByIdRequest) XXX_Merge(src proto.Message) { 133 | xxx_messageInfo_FindUserByIdRequest.Merge(m, src) 134 | } 135 | func (m *FindUserByIdRequest) XXX_Size() int { 136 | return xxx_messageInfo_FindUserByIdRequest.Size(m) 137 | } 138 | func (m *FindUserByIdRequest) XXX_DiscardUnknown() { 139 | xxx_messageInfo_FindUserByIdRequest.DiscardUnknown(m) 140 | } 141 | 142 | var xxx_messageInfo_FindUserByIdRequest proto.InternalMessageInfo 143 | 144 | func (m *FindUserByIdRequest) GetId() int64 { 145 | if m != nil { 146 | return m.Id 147 | } 148 | return 0 149 | } 150 | 151 | type FindUserByNameRequest struct { 152 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 153 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 154 | XXX_unrecognized []byte `json:"-"` 155 | XXX_sizecache int32 `json:"-"` 156 | } 157 | 158 | func (m *FindUserByNameRequest) Reset() { *m = FindUserByNameRequest{} } 159 | func (m *FindUserByNameRequest) String() string { return proto.CompactTextString(m) } 160 | func (*FindUserByNameRequest) ProtoMessage() {} 161 | func (*FindUserByNameRequest) Descriptor() ([]byte, []int) { 162 | return fileDescriptor_de1e0d3bf8cb5a8f, []int{3} 163 | } 164 | 165 | func (m *FindUserByNameRequest) XXX_Unmarshal(b []byte) error { 166 | return xxx_messageInfo_FindUserByNameRequest.Unmarshal(m, b) 167 | } 168 | func (m *FindUserByNameRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 169 | return xxx_messageInfo_FindUserByNameRequest.Marshal(b, m, deterministic) 170 | } 171 | func (m *FindUserByNameRequest) XXX_Merge(src proto.Message) { 172 | xxx_messageInfo_FindUserByNameRequest.Merge(m, src) 173 | } 174 | func (m *FindUserByNameRequest) XXX_Size() int { 175 | return xxx_messageInfo_FindUserByNameRequest.Size(m) 176 | } 177 | func (m *FindUserByNameRequest) XXX_DiscardUnknown() { 178 | xxx_messageInfo_FindUserByNameRequest.DiscardUnknown(m) 179 | } 180 | 181 | var xxx_messageInfo_FindUserByNameRequest proto.InternalMessageInfo 182 | 183 | func (m *FindUserByNameRequest) GetName() string { 184 | if m != nil { 185 | return m.Name 186 | } 187 | return "" 188 | } 189 | 190 | type Users struct { 191 | Users []*User `protobuf:"bytes,1,rep,name=users,proto3" json:"users,omitempty"` 192 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 193 | XXX_unrecognized []byte `json:"-"` 194 | XXX_sizecache int32 `json:"-"` 195 | } 196 | 197 | func (m *Users) Reset() { *m = Users{} } 198 | func (m *Users) String() string { return proto.CompactTextString(m) } 199 | func (*Users) ProtoMessage() {} 200 | func (*Users) Descriptor() ([]byte, []int) { 201 | return fileDescriptor_de1e0d3bf8cb5a8f, []int{4} 202 | } 203 | 204 | func (m *Users) XXX_Unmarshal(b []byte) error { 205 | return xxx_messageInfo_Users.Unmarshal(m, b) 206 | } 207 | func (m *Users) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 208 | return xxx_messageInfo_Users.Marshal(b, m, deterministic) 209 | } 210 | func (m *Users) XXX_Merge(src proto.Message) { 211 | xxx_messageInfo_Users.Merge(m, src) 212 | } 213 | func (m *Users) XXX_Size() int { 214 | return xxx_messageInfo_Users.Size(m) 215 | } 216 | func (m *Users) XXX_DiscardUnknown() { 217 | xxx_messageInfo_Users.DiscardUnknown(m) 218 | } 219 | 220 | var xxx_messageInfo_Users proto.InternalMessageInfo 221 | 222 | func (m *Users) GetUsers() []*User { 223 | if m != nil { 224 | return m.Users 225 | } 226 | return nil 227 | } 228 | 229 | func init() { 230 | proto.RegisterType((*User)(nil), "user.User") 231 | proto.RegisterType((*AddUserRequest)(nil), "user.AddUserRequest") 232 | proto.RegisterType((*FindUserByIdRequest)(nil), "user.FindUserByIdRequest") 233 | proto.RegisterType((*FindUserByNameRequest)(nil), "user.FindUserByNameRequest") 234 | proto.RegisterType((*Users)(nil), "user.Users") 235 | } 236 | 237 | func init() { proto.RegisterFile("pkg/api/user/api.proto", fileDescriptor_de1e0d3bf8cb5a8f) } 238 | 239 | var fileDescriptor_de1e0d3bf8cb5a8f = []byte{ 240 | // 284 bytes of a gzipped FileDescriptorProto 241 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x91, 0x41, 0x4b, 0xc3, 0x40, 242 | 0x10, 0x85, 0xdd, 0x34, 0x6d, 0xe9, 0x54, 0x72, 0x18, 0xb5, 0xc4, 0xf6, 0x12, 0x82, 0x42, 0x54, 243 | 0xd8, 0x40, 0xbc, 0x78, 0xf0, 0x62, 0x41, 0x41, 0x10, 0x0f, 0x11, 0x2f, 0xde, 0x52, 0x33, 0x86, 244 | 0x45, 0xd3, 0xc4, 0x6c, 0x22, 0xf4, 0x07, 0xfb, 0x3f, 0x64, 0x77, 0xad, 0x4d, 0x83, 0xf4, 0x36, 245 | 0x93, 0xf9, 0x5e, 0xf6, 0x3d, 0x1e, 0x4c, 0xca, 0xf7, 0x2c, 0x4c, 0x4a, 0x11, 0x36, 0x92, 0x2a, 246 | 0x35, 0xf0, 0xb2, 0x2a, 0xea, 0x02, 0x6d, 0xb5, 0x4f, 0x67, 0x59, 0x51, 0x64, 0x1f, 0x14, 0xea, 247 | 0x6f, 0x8b, 0xe6, 0x2d, 0xa4, 0xbc, 0xac, 0x57, 0x06, 0xf1, 0xcf, 0xc1, 0x7e, 0x96, 0x54, 0xa1, 248 | 0x03, 0x96, 0x48, 0x5d, 0xe6, 0xb1, 0xa0, 0x17, 0x5b, 0x22, 0x45, 0x04, 0x7b, 0x99, 0xe4, 0xe4, 249 | 0x5a, 0x1e, 0x0b, 0x46, 0xb1, 0x9e, 0xfd, 0x13, 0x70, 0x6e, 0xd2, 0x54, 0xe1, 0x31, 0x7d, 0x36, 250 | 0x24, 0xeb, 0x3f, 0x8a, 0xb5, 0xa8, 0x53, 0x38, 0xb8, 0x13, 0x4b, 0x8d, 0xcd, 0x57, 0xf7, 0xe9, 251 | 0x1a, 0xed, 0x3c, 0xe0, 0x5f, 0xc0, 0xd1, 0x06, 0x7b, 0x4c, 0x72, 0xda, 0xf5, 0xcf, 0x33, 0xe8, 252 | 0x2b, 0x50, 0xa2, 0x07, 0x7d, 0x95, 0x49, 0xba, 0xcc, 0xeb, 0x05, 0xe3, 0x08, 0xb8, 0xda, 0xb8, 253 | 0xb6, 0x64, 0x0e, 0xd1, 0x37, 0x83, 0xe1, 0x13, 0x55, 0x5f, 0xe2, 0x95, 0x90, 0xc3, 0xf0, 0xd7, 254 | 0x30, 0x1e, 0x1a, 0x72, 0xdb, 0xff, 0x74, 0xbc, 0xd1, 0x4b, 0x7f, 0x0f, 0xaf, 0x60, 0xbf, 0x6d, 255 | 0x1d, 0x8f, 0xcd, 0xf9, 0x9f, 0x38, 0x5d, 0xe5, 0x35, 0x38, 0xdb, 0x69, 0x70, 0xd6, 0xd5, 0xb6, 256 | 0x32, 0x76, 0xd5, 0x11, 0x8c, 0x1e, 0x84, 0xac, 0x4d, 0xc4, 0x09, 0x37, 0x7d, 0xf1, 0x75, 0x5f, 257 | 0xfc, 0x56, 0xf5, 0xd5, 0xd1, 0xcc, 0x07, 0x2f, 0xba, 0xdd, 0xc5, 0x40, 0x63, 0x97, 0x3f, 0x01, 258 | 0x00, 0x00, 0xff, 0xff, 0xf1, 0xd8, 0x7e, 0x2f, 0x04, 0x02, 0x00, 0x00, 259 | } 260 | 261 | // Reference imports to suppress errors if they are not otherwise used. 262 | var _ context.Context 263 | var _ grpc.ClientConn 264 | 265 | // This is a compile-time assertion to ensure that this generated file 266 | // is compatible with the grpc package it is being compiled against. 267 | const _ = grpc.SupportPackageIsVersion4 268 | 269 | // ServiceClient is the client API for Service service. 270 | // 271 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 272 | type ServiceClient interface { 273 | AddUser(ctx context.Context, in *AddUserRequest, opts ...grpc.CallOption) (*Users, error) 274 | FindUserById(ctx context.Context, in *FindUserByIdRequest, opts ...grpc.CallOption) (*Users, error) 275 | FindUserByName(ctx context.Context, in *FindUserByNameRequest, opts ...grpc.CallOption) (*Users, error) 276 | ListUsers(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*Users, error) 277 | } 278 | 279 | type serviceClient struct { 280 | cc *grpc.ClientConn 281 | } 282 | 283 | func NewServiceClient(cc *grpc.ClientConn) ServiceClient { 284 | return &serviceClient{cc} 285 | } 286 | 287 | func (c *serviceClient) AddUser(ctx context.Context, in *AddUserRequest, opts ...grpc.CallOption) (*Users, error) { 288 | out := new(Users) 289 | err := c.cc.Invoke(ctx, "/user.Service/AddUser", in, out, opts...) 290 | if err != nil { 291 | return nil, err 292 | } 293 | return out, nil 294 | } 295 | 296 | func (c *serviceClient) FindUserById(ctx context.Context, in *FindUserByIdRequest, opts ...grpc.CallOption) (*Users, error) { 297 | out := new(Users) 298 | err := c.cc.Invoke(ctx, "/user.Service/FindUserById", in, out, opts...) 299 | if err != nil { 300 | return nil, err 301 | } 302 | return out, nil 303 | } 304 | 305 | func (c *serviceClient) FindUserByName(ctx context.Context, in *FindUserByNameRequest, opts ...grpc.CallOption) (*Users, error) { 306 | out := new(Users) 307 | err := c.cc.Invoke(ctx, "/user.Service/FindUserByName", in, out, opts...) 308 | if err != nil { 309 | return nil, err 310 | } 311 | return out, nil 312 | } 313 | 314 | func (c *serviceClient) ListUsers(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*Users, error) { 315 | out := new(Users) 316 | err := c.cc.Invoke(ctx, "/user.Service/ListUsers", in, out, opts...) 317 | if err != nil { 318 | return nil, err 319 | } 320 | return out, nil 321 | } 322 | 323 | // ServiceServer is the server API for Service service. 324 | type ServiceServer interface { 325 | AddUser(context.Context, *AddUserRequest) (*Users, error) 326 | FindUserById(context.Context, *FindUserByIdRequest) (*Users, error) 327 | FindUserByName(context.Context, *FindUserByNameRequest) (*Users, error) 328 | ListUsers(context.Context, *empty.Empty) (*Users, error) 329 | } 330 | 331 | func RegisterServiceServer(s *grpc.Server, srv ServiceServer) { 332 | s.RegisterService(&_Service_serviceDesc, srv) 333 | } 334 | 335 | func _Service_AddUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 336 | in := new(AddUserRequest) 337 | if err := dec(in); err != nil { 338 | return nil, err 339 | } 340 | if interceptor == nil { 341 | return srv.(ServiceServer).AddUser(ctx, in) 342 | } 343 | info := &grpc.UnaryServerInfo{ 344 | Server: srv, 345 | FullMethod: "/user.Service/AddUser", 346 | } 347 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 348 | return srv.(ServiceServer).AddUser(ctx, req.(*AddUserRequest)) 349 | } 350 | return interceptor(ctx, in, info, handler) 351 | } 352 | 353 | func _Service_FindUserById_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 354 | in := new(FindUserByIdRequest) 355 | if err := dec(in); err != nil { 356 | return nil, err 357 | } 358 | if interceptor == nil { 359 | return srv.(ServiceServer).FindUserById(ctx, in) 360 | } 361 | info := &grpc.UnaryServerInfo{ 362 | Server: srv, 363 | FullMethod: "/user.Service/FindUserById", 364 | } 365 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 366 | return srv.(ServiceServer).FindUserById(ctx, req.(*FindUserByIdRequest)) 367 | } 368 | return interceptor(ctx, in, info, handler) 369 | } 370 | 371 | func _Service_FindUserByName_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 372 | in := new(FindUserByNameRequest) 373 | if err := dec(in); err != nil { 374 | return nil, err 375 | } 376 | if interceptor == nil { 377 | return srv.(ServiceServer).FindUserByName(ctx, in) 378 | } 379 | info := &grpc.UnaryServerInfo{ 380 | Server: srv, 381 | FullMethod: "/user.Service/FindUserByName", 382 | } 383 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 384 | return srv.(ServiceServer).FindUserByName(ctx, req.(*FindUserByNameRequest)) 385 | } 386 | return interceptor(ctx, in, info, handler) 387 | } 388 | 389 | func _Service_ListUsers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 390 | in := new(empty.Empty) 391 | if err := dec(in); err != nil { 392 | return nil, err 393 | } 394 | if interceptor == nil { 395 | return srv.(ServiceServer).ListUsers(ctx, in) 396 | } 397 | info := &grpc.UnaryServerInfo{ 398 | Server: srv, 399 | FullMethod: "/user.Service/ListUsers", 400 | } 401 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 402 | return srv.(ServiceServer).ListUsers(ctx, req.(*empty.Empty)) 403 | } 404 | return interceptor(ctx, in, info, handler) 405 | } 406 | 407 | var _Service_serviceDesc = grpc.ServiceDesc{ 408 | ServiceName: "user.Service", 409 | HandlerType: (*ServiceServer)(nil), 410 | Methods: []grpc.MethodDesc{ 411 | { 412 | MethodName: "AddUser", 413 | Handler: _Service_AddUser_Handler, 414 | }, 415 | { 416 | MethodName: "FindUserById", 417 | Handler: _Service_FindUserById_Handler, 418 | }, 419 | { 420 | MethodName: "FindUserByName", 421 | Handler: _Service_FindUserByName_Handler, 422 | }, 423 | { 424 | MethodName: "ListUsers", 425 | Handler: _Service_ListUsers_Handler, 426 | }, 427 | }, 428 | Streams: []grpc.StreamDesc{}, 429 | Metadata: "pkg/api/user/api.proto", 430 | } 431 | -------------------------------------------------------------------------------- /pkg/api/user/api.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package user; 4 | option go_package = "user"; 5 | 6 | import "google/protobuf/empty.proto"; 7 | 8 | message User { 9 | int64 id = 1; 10 | string name = 2; 11 | } 12 | 13 | message AddUserRequest { 14 | string name = 1; 15 | } 16 | 17 | message FindUserByIdRequest { 18 | int64 id = 1; 19 | } 20 | 21 | message FindUserByNameRequest { 22 | string name = 1; 23 | } 24 | 25 | message Users { 26 | repeated User users = 1; 27 | } 28 | 29 | service Service { 30 | rpc AddUser(AddUserRequest) returns (Users) {} 31 | rpc FindUserById(FindUserByIdRequest) returns (Users) {} 32 | rpc FindUserByName(FindUserByNameRequest) returns (Users) {} 33 | rpc ListUsers(google.protobuf.Empty) returns (Users) {} 34 | } 35 | -------------------------------------------------------------------------------- /pkg/api/user/mock/api.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: api.pb.go 3 | 4 | // Package mock is a generated GoMock package. 5 | package mock 6 | 7 | import ( 8 | context "context" 9 | user "github.com/alanchchen/go-project-skeleton/pkg/api/user" 10 | gomock "github.com/golang/mock/gomock" 11 | empty "github.com/golang/protobuf/ptypes/empty" 12 | grpc "google.golang.org/grpc" 13 | reflect "reflect" 14 | ) 15 | 16 | // MockServiceClient is a mock of ServiceClient interface 17 | type MockServiceClient struct { 18 | ctrl *gomock.Controller 19 | recorder *MockServiceClientMockRecorder 20 | } 21 | 22 | // MockServiceClientMockRecorder is the mock recorder for MockServiceClient 23 | type MockServiceClientMockRecorder struct { 24 | mock *MockServiceClient 25 | } 26 | 27 | // NewMockServiceClient creates a new mock instance 28 | func NewMockServiceClient(ctrl *gomock.Controller) *MockServiceClient { 29 | mock := &MockServiceClient{ctrl: ctrl} 30 | mock.recorder = &MockServiceClientMockRecorder{mock} 31 | return mock 32 | } 33 | 34 | // EXPECT returns an object that allows the caller to indicate expected use 35 | func (m *MockServiceClient) EXPECT() *MockServiceClientMockRecorder { 36 | return m.recorder 37 | } 38 | 39 | // AddUser mocks base method 40 | func (m *MockServiceClient) AddUser(ctx context.Context, in *user.AddUserRequest, opts ...grpc.CallOption) (*user.Users, error) { 41 | m.ctrl.T.Helper() 42 | varargs := []interface{}{ctx, in} 43 | for _, a := range opts { 44 | varargs = append(varargs, a) 45 | } 46 | ret := m.ctrl.Call(m, "AddUser", varargs...) 47 | ret0, _ := ret[0].(*user.Users) 48 | ret1, _ := ret[1].(error) 49 | return ret0, ret1 50 | } 51 | 52 | // AddUser indicates an expected call of AddUser 53 | func (mr *MockServiceClientMockRecorder) AddUser(ctx, in interface{}, opts ...interface{}) *gomock.Call { 54 | mr.mock.ctrl.T.Helper() 55 | varargs := append([]interface{}{ctx, in}, opts...) 56 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddUser", reflect.TypeOf((*MockServiceClient)(nil).AddUser), varargs...) 57 | } 58 | 59 | // FindUserById mocks base method 60 | func (m *MockServiceClient) FindUserById(ctx context.Context, in *user.FindUserByIdRequest, opts ...grpc.CallOption) (*user.Users, error) { 61 | m.ctrl.T.Helper() 62 | varargs := []interface{}{ctx, in} 63 | for _, a := range opts { 64 | varargs = append(varargs, a) 65 | } 66 | ret := m.ctrl.Call(m, "FindUserById", varargs...) 67 | ret0, _ := ret[0].(*user.Users) 68 | ret1, _ := ret[1].(error) 69 | return ret0, ret1 70 | } 71 | 72 | // FindUserById indicates an expected call of FindUserById 73 | func (mr *MockServiceClientMockRecorder) FindUserById(ctx, in interface{}, opts ...interface{}) *gomock.Call { 74 | mr.mock.ctrl.T.Helper() 75 | varargs := append([]interface{}{ctx, in}, opts...) 76 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindUserById", reflect.TypeOf((*MockServiceClient)(nil).FindUserById), varargs...) 77 | } 78 | 79 | // FindUserByName mocks base method 80 | func (m *MockServiceClient) FindUserByName(ctx context.Context, in *user.FindUserByNameRequest, opts ...grpc.CallOption) (*user.Users, error) { 81 | m.ctrl.T.Helper() 82 | varargs := []interface{}{ctx, in} 83 | for _, a := range opts { 84 | varargs = append(varargs, a) 85 | } 86 | ret := m.ctrl.Call(m, "FindUserByName", varargs...) 87 | ret0, _ := ret[0].(*user.Users) 88 | ret1, _ := ret[1].(error) 89 | return ret0, ret1 90 | } 91 | 92 | // FindUserByName indicates an expected call of FindUserByName 93 | func (mr *MockServiceClientMockRecorder) FindUserByName(ctx, in interface{}, opts ...interface{}) *gomock.Call { 94 | mr.mock.ctrl.T.Helper() 95 | varargs := append([]interface{}{ctx, in}, opts...) 96 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindUserByName", reflect.TypeOf((*MockServiceClient)(nil).FindUserByName), varargs...) 97 | } 98 | 99 | // ListUsers mocks base method 100 | func (m *MockServiceClient) ListUsers(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*user.Users, error) { 101 | m.ctrl.T.Helper() 102 | varargs := []interface{}{ctx, in} 103 | for _, a := range opts { 104 | varargs = append(varargs, a) 105 | } 106 | ret := m.ctrl.Call(m, "ListUsers", varargs...) 107 | ret0, _ := ret[0].(*user.Users) 108 | ret1, _ := ret[1].(error) 109 | return ret0, ret1 110 | } 111 | 112 | // ListUsers indicates an expected call of ListUsers 113 | func (mr *MockServiceClientMockRecorder) ListUsers(ctx, in interface{}, opts ...interface{}) *gomock.Call { 114 | mr.mock.ctrl.T.Helper() 115 | varargs := append([]interface{}{ctx, in}, opts...) 116 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListUsers", reflect.TypeOf((*MockServiceClient)(nil).ListUsers), varargs...) 117 | } 118 | 119 | // MockServiceServer is a mock of ServiceServer interface 120 | type MockServiceServer struct { 121 | ctrl *gomock.Controller 122 | recorder *MockServiceServerMockRecorder 123 | } 124 | 125 | // MockServiceServerMockRecorder is the mock recorder for MockServiceServer 126 | type MockServiceServerMockRecorder struct { 127 | mock *MockServiceServer 128 | } 129 | 130 | // NewMockServiceServer creates a new mock instance 131 | func NewMockServiceServer(ctrl *gomock.Controller) *MockServiceServer { 132 | mock := &MockServiceServer{ctrl: ctrl} 133 | mock.recorder = &MockServiceServerMockRecorder{mock} 134 | return mock 135 | } 136 | 137 | // EXPECT returns an object that allows the caller to indicate expected use 138 | func (m *MockServiceServer) EXPECT() *MockServiceServerMockRecorder { 139 | return m.recorder 140 | } 141 | 142 | // AddUser mocks base method 143 | func (m *MockServiceServer) AddUser(arg0 context.Context, arg1 *user.AddUserRequest) (*user.Users, error) { 144 | m.ctrl.T.Helper() 145 | ret := m.ctrl.Call(m, "AddUser", arg0, arg1) 146 | ret0, _ := ret[0].(*user.Users) 147 | ret1, _ := ret[1].(error) 148 | return ret0, ret1 149 | } 150 | 151 | // AddUser indicates an expected call of AddUser 152 | func (mr *MockServiceServerMockRecorder) AddUser(arg0, arg1 interface{}) *gomock.Call { 153 | mr.mock.ctrl.T.Helper() 154 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddUser", reflect.TypeOf((*MockServiceServer)(nil).AddUser), arg0, arg1) 155 | } 156 | 157 | // FindUserById mocks base method 158 | func (m *MockServiceServer) FindUserById(arg0 context.Context, arg1 *user.FindUserByIdRequest) (*user.Users, error) { 159 | m.ctrl.T.Helper() 160 | ret := m.ctrl.Call(m, "FindUserById", arg0, arg1) 161 | ret0, _ := ret[0].(*user.Users) 162 | ret1, _ := ret[1].(error) 163 | return ret0, ret1 164 | } 165 | 166 | // FindUserById indicates an expected call of FindUserById 167 | func (mr *MockServiceServerMockRecorder) FindUserById(arg0, arg1 interface{}) *gomock.Call { 168 | mr.mock.ctrl.T.Helper() 169 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindUserById", reflect.TypeOf((*MockServiceServer)(nil).FindUserById), arg0, arg1) 170 | } 171 | 172 | // FindUserByName mocks base method 173 | func (m *MockServiceServer) FindUserByName(arg0 context.Context, arg1 *user.FindUserByNameRequest) (*user.Users, error) { 174 | m.ctrl.T.Helper() 175 | ret := m.ctrl.Call(m, "FindUserByName", arg0, arg1) 176 | ret0, _ := ret[0].(*user.Users) 177 | ret1, _ := ret[1].(error) 178 | return ret0, ret1 179 | } 180 | 181 | // FindUserByName indicates an expected call of FindUserByName 182 | func (mr *MockServiceServerMockRecorder) FindUserByName(arg0, arg1 interface{}) *gomock.Call { 183 | mr.mock.ctrl.T.Helper() 184 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindUserByName", reflect.TypeOf((*MockServiceServer)(nil).FindUserByName), arg0, arg1) 185 | } 186 | 187 | // ListUsers mocks base method 188 | func (m *MockServiceServer) ListUsers(arg0 context.Context, arg1 *empty.Empty) (*user.Users, error) { 189 | m.ctrl.T.Helper() 190 | ret := m.ctrl.Call(m, "ListUsers", arg0, arg1) 191 | ret0, _ := ret[0].(*user.Users) 192 | ret1, _ := ret[1].(error) 193 | return ret0, ret1 194 | } 195 | 196 | // ListUsers indicates an expected call of ListUsers 197 | func (mr *MockServiceServerMockRecorder) ListUsers(arg0, arg1 interface{}) *gomock.Call { 198 | mr.mock.ctrl.T.Helper() 199 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListUsers", reflect.TypeOf((*MockServiceServer)(nil).ListUsers), arg0, arg1) 200 | } 201 | -------------------------------------------------------------------------------- /pkg/api/user/service.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "io/ioutil" 7 | "log" 8 | "math/rand" 9 | "sync" 10 | "time" 11 | 12 | empty "github.com/golang/protobuf/ptypes/empty" 13 | grpc "google.golang.org/grpc" 14 | ) 15 | 16 | //go:generate mockgen -source=api.pb.go -destination=mock/api.go -package=mock 17 | 18 | func init() { 19 | rand.Seed(time.Now().UnixNano()) 20 | } 21 | 22 | func NewService(opts ...Option) *UserService { 23 | s := &UserService{ 24 | mu: new(sync.RWMutex), 25 | logger: log.New(ioutil.Discard, "", 0), 26 | } 27 | 28 | for _, opt := range opts { 29 | opt(s) 30 | } 31 | 32 | return s 33 | } 34 | 35 | type UserService struct { 36 | users []*User 37 | mu *sync.RWMutex 38 | logger *log.Logger 39 | } 40 | 41 | func (s *UserService) Bind(server *grpc.Server) { 42 | RegisterServiceServer(server, s) 43 | } 44 | 45 | func (s *UserService) AddUser(ctx context.Context, req *AddUserRequest) (*Users, error) { 46 | if req.Name == "" { 47 | s.logger.Println("the given name is empty") 48 | return nil, errors.New("please tell me your name") 49 | } 50 | 51 | user := &User{ 52 | Name: req.Name, 53 | Id: rand.Int63(), 54 | } 55 | 56 | s.mu.Lock() 57 | s.users = append(s.users, user) 58 | s.mu.Unlock() 59 | 60 | return &Users{ 61 | Users: []*User{ 62 | user, 63 | }, 64 | }, nil 65 | } 66 | 67 | func (s *UserService) FindUserById(ctx context.Context, req *FindUserByIdRequest) (*Users, error) { 68 | s.mu.RLock() 69 | defer s.mu.RUnlock() 70 | 71 | for _, u := range s.users { 72 | if u.Id == req.Id { 73 | return &Users{ 74 | Users: []*User{ 75 | u, 76 | }, 77 | }, nil 78 | } 79 | } 80 | 81 | return nil, errors.New("not found") 82 | } 83 | 84 | func (s *UserService) FindUserByName(ctx context.Context, req *FindUserByNameRequest) (*Users, error) { 85 | s.mu.RLock() 86 | defer s.mu.RUnlock() 87 | 88 | for _, u := range s.users { 89 | if u.Name == req.Name { 90 | return &Users{ 91 | Users: []*User{ 92 | u, 93 | }, 94 | }, nil 95 | } 96 | } 97 | 98 | return nil, errors.New("not found") 99 | } 100 | 101 | func (s *UserService) ListUsers(ctx context.Context, _ *empty.Empty) (*Users, error) { 102 | return &Users{ 103 | Users: s.users, 104 | }, nil 105 | } 106 | 107 | // ---------------------------------------------------------------------------- 108 | 109 | func randomID(length int) string { 110 | b := make([]byte, length) 111 | _, err := rand.Read(b) 112 | if err != nil { 113 | return "" 114 | } 115 | 116 | return string(b) 117 | } 118 | -------------------------------------------------------------------------------- /pkg/api/user/service_options.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import "log" 4 | 5 | type Option func(*UserService) 6 | 7 | func UseLogger(logger *log.Logger) Option { 8 | return func(s *UserService) { 9 | s.logger = logger 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /pkg/app/actors.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "go.uber.org/dig" 5 | ) 6 | 7 | type Logger interface { 8 | Println(args ...interface{}) 9 | } 10 | 11 | type Actor interface { 12 | Run() error 13 | Interrupt(error) 14 | } 15 | 16 | type ActorResult struct { 17 | dig.Out 18 | Actor Actor `group:"actors"` 19 | } 20 | 21 | type ActorsResult struct { 22 | dig.In 23 | Actors []Actor `group:"actors"` 24 | } 25 | -------------------------------------------------------------------------------- /pkg/app/grpc_server.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "net" 5 | 6 | "google.golang.org/grpc" 7 | ) 8 | 9 | func NewGRPCServerActor(server *grpc.Server, socket net.Listener, logger Logger) ActorResult { 10 | return ActorResult{ 11 | Actor: &rpcServerActor{ 12 | server: server, 13 | socket: socket, 14 | logger: logger, 15 | }, 16 | } 17 | } 18 | 19 | type rpcServerActor struct { 20 | server *grpc.Server 21 | socket net.Listener 22 | logger Logger 23 | } 24 | 25 | func (r *rpcServerActor) Run() error { 26 | r.logger.Println("Starting grpc server at", r.socket.Addr()) 27 | return r.server.Serve(r.socket) 28 | } 29 | 30 | func (r *rpcServerActor) Interrupt(err error) { 31 | r.server.GracefulStop() 32 | } 33 | -------------------------------------------------------------------------------- /pkg/app/run.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "strings" 5 | "time" 6 | 7 | "github.com/oklog/run" 8 | "github.com/spf13/cobra" 9 | "github.com/spf13/viper" 10 | "go.uber.org/dig" 11 | ) 12 | 13 | type Runner struct { 14 | container *dig.Container 15 | config *viper.Viper 16 | } 17 | 18 | func Run(app *cobra.Command, args []string, initializers ...interface{}) error { 19 | runner, err := newRunner(app, args) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | runnable := func(r ActorsResult) error { 25 | runGroup := run.Group{} 26 | 27 | for _, actor := range r.Actors { 28 | runGroup.Add(actor.Run, actor.Interrupt) 29 | } 30 | 31 | // Run blocks until all the actors return. In the normal case, that’ll be when someone hits ctrl-C, 32 | // triggering the signal handler. If something breaks, its error will be propegated through. In all 33 | // cases, the first returned error triggers the interrupt function for all actors. And in this way, 34 | // we can reliably and coherently ensure that every goroutine that’s Added to the group is stopped, 35 | // when Run returns. 36 | return runGroup.Run() 37 | } 38 | 39 | return runner.execute(runnable, initializers...) 40 | } 41 | 42 | func RunCustom(app *cobra.Command, args []string, runnable interface{}, initializers ...interface{}) error { 43 | runner, err := newRunner(app, args) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | return runner.execute(runnable, initializers...) 49 | } 50 | 51 | func newRunner(app *cobra.Command, args []string) (*Runner, error) { 52 | r := &Runner{ 53 | container: dig.New(), 54 | config: viper.New(), 55 | } 56 | 57 | if err := r.bindCobraCommand(app, args); err != nil { 58 | return nil, err 59 | } 60 | 61 | return r, nil 62 | } 63 | 64 | func (r *Runner) execute(runnable interface{}, initializers ...interface{}) error { 65 | container := r.container 66 | 67 | initializers = append(initializers, func() *Config { 68 | return r.config 69 | }) 70 | 71 | for _, initFn := range initializers { 72 | if err := container.Provide(initFn); err != nil { 73 | return err 74 | } 75 | } 76 | 77 | // Invoke actors 78 | return dig.RootCause(container.Invoke(runnable)) 79 | } 80 | 81 | func (r *Runner) bindCobraCommand(app *cobra.Command, args []string) (err error) { 82 | cfg := r.config 83 | appName := app.Use 84 | var cfgFile string 85 | if cfgFlag := app.Flags().Lookup("config"); cfgFlag != nil { 86 | cfgFile = cfgFlag.Value.String() 87 | } 88 | 89 | if cfgFile != "" { // enable ability to specify config file via flag 90 | cfg.SetConfigFile(cfgFile) 91 | } else { 92 | cfg.SetConfigName(appName) 93 | cfg.SetConfigType("yaml") 94 | cfg.AddConfigPath(".") 95 | cfg.AddConfigPath("$HOME/." + appName) 96 | cfg.AddConfigPath("$HOME") 97 | cfg.AddConfigPath("/") 98 | } 99 | 100 | if err = cfg.BindPFlags(app.Flags()); err != nil { 101 | return err 102 | } 103 | 104 | cfg.AutomaticEnv() // read in environment variables that match 105 | cfg.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_")) 106 | 107 | // If a config file is found, read it. 108 | if err = cfg.ReadInConfig(); err == nil { 109 | app.Println("Using config file:", cfg.ConfigFileUsed()) 110 | // Ignore the error if config file is not found 111 | } 112 | 113 | return r.bindContainer() 114 | } 115 | 116 | func (r *Runner) bindContainer() error { 117 | cfg := r.config 118 | container := r.container 119 | 120 | for _, key := range cfg.AllKeys() { 121 | k := key 122 | val := cfg.Get(k) 123 | 124 | var getter interface{} 125 | switch val.(type) { 126 | case bool: 127 | getter = func() bool { 128 | return cfg.GetBool(k) 129 | } 130 | case int: 131 | getter = func() int { 132 | return cfg.GetInt(k) 133 | } 134 | case int32: 135 | getter = func() int32 { 136 | return cfg.GetInt32(k) 137 | } 138 | case int64: 139 | getter = func() int64 { 140 | return cfg.GetInt64(k) 141 | } 142 | case float64: 143 | getter = func() float64 { 144 | return cfg.GetFloat64(k) 145 | } 146 | case string: 147 | getter = func() string { 148 | return cfg.GetString(k) 149 | } 150 | case []string: 151 | getter = func() []string { 152 | return cfg.GetStringSlice(k) 153 | } 154 | case map[string]string: 155 | getter = func() map[string]string { 156 | return cfg.GetStringMapString(k) 157 | } 158 | case map[string][]string: 159 | getter = func() map[string][]string { 160 | return cfg.GetStringMapStringSlice(k) 161 | } 162 | case map[string]interface{}: 163 | getter = func() map[string]interface{} { 164 | return cfg.GetStringMap(k) 165 | } 166 | case time.Time: 167 | getter = func() time.Time { 168 | return cfg.GetTime(k) 169 | } 170 | case time.Duration: 171 | getter = func() time.Duration { 172 | return cfg.GetDuration(k) 173 | } 174 | default: 175 | continue 176 | } 177 | 178 | if err := container.Provide(getter, dig.Name(k)); err != nil { 179 | return err 180 | } 181 | } 182 | 183 | return nil 184 | } 185 | -------------------------------------------------------------------------------- /pkg/app/signal.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | "syscall" 7 | ) 8 | 9 | func NewSignalActor(logger Logger) ActorResult { 10 | return ActorResult{ 11 | Actor: &signalActor{ 12 | sigs: make(chan os.Signal), 13 | logger: logger, 14 | }, 15 | } 16 | } 17 | 18 | type signalActor struct { 19 | sigs chan os.Signal 20 | logger Logger 21 | } 22 | 23 | func (r *signalActor) Run() error { 24 | signal.Notify(r.sigs, syscall.SIGTERM, syscall.SIGINT) 25 | defer signal.Stop(r.sigs) 26 | if sig := <-r.sigs; sig != nil { 27 | r.logger.Println("Received signal", sig, "... shutting down") 28 | } 29 | return nil 30 | } 31 | 32 | func (r *signalActor) Interrupt(err error) { 33 | close(r.sigs) 34 | } 35 | -------------------------------------------------------------------------------- /pkg/app/types.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | "github.com/spf13/viper" 6 | "go.uber.org/dig" 7 | ) 8 | 9 | type Input = dig.In 10 | 11 | type Config = viper.Viper 12 | 13 | type Command = cobra.Command 14 | -------------------------------------------------------------------------------- /tools/mockgen: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | go run `go env GOPATH`/pkg/mod/github.com/golang/mock@v1.3.1/mockgen $@ 4 | -------------------------------------------------------------------------------- /tools/protoc-gen-go: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | go run `go env GOPATH`/pkg/mod/github.com/golang/protobuf@v1.3.1/protoc-gen-go $@ 4 | --------------------------------------------------------------------------------