Skip to content
This repository was archived by the owner on Dec 6, 2024. It is now read-only.

Commit 62c0184

Browse files
author
Krish Chowdhary
committed
fake client: code gen added
1 parent 6415249 commit 62c0184

File tree

4 files changed

+267
-8
lines changed

4 files changed

+267
-8
lines changed

Diff for: .gitignore

+1-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
.build
44
*.swp
55
cosi.proto.tmp
6-
protoc
7-
protoc-gen-go
8-
protoc-gen-go-json
6+
protoc*
97
cosi.a
108
.protoc
119
.idea

Diff for: Makefile

+31-5
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ $(GENPROTO_BUILD_GO):
107107
(cd $(GOPATH)/src/$(GENPROTO_GO_PKG) && go get -v -d $$(go list -f '{{ .ImportPath }}' ./...))
108108

109109

110-
111110
########################################################################
112111
## GRPC-GO ##
113112
########################################################################
@@ -127,6 +126,16 @@ $(GRPC_BUILD_GO):
127126
go build -o "$@" $(GRPC_GO_PKG))
128127

129128

129+
########################################################################
130+
## PROTOC-GEN-GO-FAKE ##
131+
########################################################################
132+
133+
# This is the recipe for getting and installing the grpc go
134+
PROTOC_GEN_GO_FAKE_SRC := ./hack/fake-gen
135+
PROTOC_GEN_GO_FAKE := protoc-gen-gofake
136+
$(PROTOC_GEN_GO_FAKE):
137+
go build -o $(PROTOC_GEN_GO_FAKE) $(PROTOC_GEN_GO_FAKE_SRC)
138+
130139

131140
########################################################################
132141
## PATH ##
@@ -148,9 +157,11 @@ COSI_PKG_SUB := .
148157
COSI_BUILD := $(COSI_PKG_SUB)/.build
149158
COSI_GO := $(COSI_PKG_SUB)/cosi.pb.go
150159
COSI_GO_JSON := $(COSI_PKG_SUB)/cosi.pb.json.go
160+
COSI_GO_FAKE := $(COSI_PKG_SUB)/fake/cosi.pb.fake.go
151161
COSI_A := cosi.a
152162
COSI_GO_TMP := $(COSI_BUILD)/$(COSI_PKG_ROOT)/cosi.pb.go
153163
COSI_GO_JSON_TMP := $(COSI_BUILD)/$(COSI_PKG_ROOT)/cosi.pb.json.go
164+
COSI_GO_FAKE_TMP := $(COSI_BUILD)/fake/$(COSI_PKG_ROOT)/cosi.pb.fake.go
154165

155166
# This recipe generates the go language bindings to a temp area.
156167
$(COSI_GO_TMP): HERE := $(shell pwd)
@@ -161,11 +172,15 @@ $(COSI_GO_TMP): GO_OUT := $(GO_OUT),Mgoogle/protobuf/wrappers.proto=$(PTYPES_PKG
161172
$(COSI_GO_TMP): GO_OUT := $(GO_OUT):"$(HERE)/$(COSI_BUILD)"
162173
$(COSI_GO_TMP): GO_JSON_OUT := emit_defaults
163174
$(COSI_GO_TMP): GO_JSON_OUT := $(GO_JSON_OUT):"$(HERE)/$(COSI_BUILD)"
175+
$(COSI_GO_TMP): GO_FAKE_OUT := emit_defaults
176+
$(COSI_GO_TMP): GO_FAKE_OUT := $(GO_FAKE_OUT),package=cosi,packagePath=sigs.k8s.io/container-object-storage-interface-spec
177+
$(COSI_GO_TMP): GO_FAKE_OUT := $(GO_FAKE_OUT):"$(HERE)/$(COSI_BUILD)"/fake
164178
$(COSI_GO_TMP): INCLUDE := -I$(GOPATH)/src -I$(HERE)/$(PROTOC_TMP_DIR)/include
165-
$(COSI_GO_TMP): $(COSI_PROTO) | $(PROTOC) $(PROTOC_GEN_GO) $(PROTOC_GEN_GO_JSON)
179+
$(COSI_GO_TMP): $(COSI_PROTO) | $(PROTOC) $(PROTOC_GEN_GO) $(PROTOC_GEN_GO_JSON) $(PROTOC_GEN_GO_FAKE)
166180
@mkdir -p "$(@D)"
181+
@mkdir -p "$(COSI_BUILD)/fake"
167182
(cd "$(GOPATH)/src" && \
168-
$(HERE)/$(PROTOC) $(INCLUDE) --go_out=$(GO_OUT) --go-json_out=$(GO_JSON_OUT) "$(COSI_PKG_ROOT)/$(<F)")
183+
$(HERE)/$(PROTOC) $(INCLUDE) --go_out=$(GO_OUT) --go-json_out=$(GO_JSON_OUT) --gofake_out=$(GO_FAKE_OUT) "$(COSI_PKG_ROOT)/$(<F)")
169184

170185
# The temp language bindings are compared to the ones that are
171186
# versioned. If they are different then it means the language
@@ -189,12 +204,23 @@ else
189204
diff "$@" "$?" > /dev/null 2>&1 || cp -f "$?" "$@"
190205
endif
191206

207+
# The temp language bindings are compared to the ones that are
208+
# versioned. If they are different then it means the language
209+
# bindings were not updated prior to being committed.
210+
$(COSI_GO_FAKE): $(COSI_GO_FAKE_TMP)
211+
ifeq (true,$(TRAVIS))
212+
diff "$@" "$?"
213+
else
214+
@mkdir -p "$(@D)"
215+
diff "$@" "$?" > /dev/null 2>&1 || cp -f "$?" "$@"
216+
endif
217+
192218
# This recipe builds the Go archive from the sources in three steps:
193219
#
194220
# 1. Go get any missing dependencies.
195221
# 2. Cache the packages.
196222
# 3. Build the archive file.
197-
$(COSI_A): $(COSI_GO) $(COSI_GO_JSON) $(GENPROTO_BUILD_GO) $(GRPC_BUILD_GO)
223+
$(COSI_A): $(COSI_GO) $(COSI_GO_JSON) $(COSI_GO_FAKE) $(GENPROTO_BUILD_GO) $(GRPC_BUILD_GO)
198224
go get -v -d ./...
199225
go install ./$(COSI_PKG_SUB)
200226
go build -o "$@" ./$(COSI_PKG_SUB)
@@ -210,6 +236,6 @@ clean:
210236
rm -rf "$(COSI_PROTO)" "$(COSI_A)" "$(COSI_GO)" "$(COSI_GO_JSON)" "$(COSI_BUILD)"
211237

212238
clobber: clean
213-
rm -fr "$(PROTOC)" "$(PROTOC_TMP_DIR)" "$(PROTOC_GEN_GO)" "$(PROTOC_GEN_GO_JSON)"
239+
rm -fr "$(PROTOC)" "$(PROTOC_TMP_DIR)" "$(PROTOC_GEN_GO)" "$(PROTOC_GEN_GO_JSON)" "$(PROTOC_GEN_GO_FAKE)"
214240

215241
.PHONY: clean clobber $(GRPC_BUILD_GO) $(GENPROTO_BUILD_GO)

Diff for: fake/cosi.pb.fake.go

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package fake
2+
3+
import (
4+
"context"
5+
"google.golang.org/grpc"
6+
cosi "sigs.k8s.io/container-object-storage-interface-spec"
7+
)
8+
9+
type FakeIdentityClient struct {
10+
FakeProvisionerGetInfo func(ctx context.Context, in *cosi.ProvisionerGetInfoRequest, opts ...grpc.CallOption) (*cosi.ProvisionerGetInfoResponse, error)
11+
}
12+
13+
func (f *FakeIdentityClient) ProvisionerGetInfo(ctx context.Context, in *cosi.ProvisionerGetInfoRequest, opts ...grpc.CallOption) (*cosi.ProvisionerGetInfoResponse, error) {
14+
return f.FakeProvisionerGetInfo(ctx, in, opts...)
15+
}
16+
17+
type FakeProvisionerClient struct {
18+
FakeProvisionerCreateBucket func(ctx context.Context, in *cosi.ProvisionerCreateBucketRequest, opts ...grpc.CallOption) (*cosi.ProvisionerCreateBucketResponse, error)
19+
FakeProvisionerDeleteBucket func(ctx context.Context, in *cosi.ProvisionerDeleteBucketRequest, opts ...grpc.CallOption) (*cosi.ProvisionerDeleteBucketResponse, error)
20+
FakeProvisionerGrantBucketAccess func(ctx context.Context, in *cosi.ProvisionerGrantBucketAccessRequest, opts ...grpc.CallOption) (*cosi.ProvisionerGrantBucketAccessResponse, error)
21+
FakeProvisionerRevokeBucketAccess func(ctx context.Context, in *cosi.ProvisionerRevokeBucketAccessRequest, opts ...grpc.CallOption) (*cosi.ProvisionerRevokeBucketAccessResponse, error)
22+
}
23+
24+
func (f *FakeProvisionerClient) ProvisionerCreateBucket(ctx context.Context, in *cosi.ProvisionerCreateBucketRequest, opts ...grpc.CallOption) (*cosi.ProvisionerCreateBucketResponse, error) {
25+
return f.FakeProvisionerCreateBucket(ctx, in, opts...)
26+
}
27+
28+
func (f *FakeProvisionerClient) ProvisionerDeleteBucket(ctx context.Context, in *cosi.ProvisionerDeleteBucketRequest, opts ...grpc.CallOption) (*cosi.ProvisionerDeleteBucketResponse, error) {
29+
return f.FakeProvisionerDeleteBucket(ctx, in, opts...)
30+
}
31+
32+
func (f *FakeProvisionerClient) ProvisionerGrantBucketAccess(ctx context.Context, in *cosi.ProvisionerGrantBucketAccessRequest, opts ...grpc.CallOption) (*cosi.ProvisionerGrantBucketAccessResponse, error) {
33+
return f.FakeProvisionerGrantBucketAccess(ctx, in, opts...)
34+
}
35+
36+
func (f *FakeProvisionerClient) ProvisionerRevokeBucketAccess(ctx context.Context, in *cosi.ProvisionerRevokeBucketAccessRequest, opts ...grpc.CallOption) (*cosi.ProvisionerRevokeBucketAccessResponse, error) {
37+
return f.FakeProvisionerRevokeBucketAccess(ctx, in, opts...)
38+
}
39+

Diff for: hack/fake-gen/main.go

+196
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
/*
2+
If this program is on the path of your machine you can invoke it in the following way:
3+
4+
protoc --plugin protoc-gen-gofake --goexample_out=package=cosi,packagePath=sigs.k8s.io/container-object-storage-interface-spec:fake cosi.proto
5+
6+
Requires package, and packagePath to be specified
7+
*/
8+
package main
9+
10+
import (
11+
"bytes"
12+
"fmt"
13+
"io"
14+
"io/ioutil"
15+
"os"
16+
"strings"
17+
"text/tabwriter"
18+
19+
"github.com/golang/protobuf/proto"
20+
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
21+
)
22+
23+
type GoFake struct {
24+
Request *plugin.CodeGeneratorRequest
25+
Response *plugin.CodeGeneratorResponse
26+
Parameters map[string]string
27+
}
28+
29+
type FakeService struct {
30+
Name string
31+
Methods []Method
32+
}
33+
34+
type Method struct {
35+
Name string
36+
Input string
37+
Output string
38+
}
39+
40+
func (runner *GoFake) PrintParameters(w io.Writer) {
41+
const padding = 3
42+
tw := tabwriter.NewWriter(w, 0, 0, padding, ' ', tabwriter.TabIndent)
43+
fmt.Fprintf(tw, "Parameters:\n")
44+
for k, v := range runner.Parameters {
45+
fmt.Fprintf(tw, "%s:\t%s\n", k, v)
46+
}
47+
fmt.Fprintln(tw, "")
48+
tw.Flush()
49+
}
50+
51+
func cleanInput(val string) string {
52+
spl := strings.Split(val, ".")
53+
return spl[len(spl)-1]
54+
}
55+
56+
func (runner *GoFake) getLocationMessage() map[string][]*FakeService {
57+
ret := make(map[string][]*FakeService)
58+
for _, protoFile := range runner.Request.ProtoFile {
59+
_, _ = fmt.Fprintf(os.Stderr, "%s\n", *protoFile.Name)
60+
fakeServices := make([]*FakeService, 0)
61+
_, _ = fmt.Fprintf(os.Stderr, "%+v\n", protoFile.GetService())
62+
svcs := protoFile.GetService()
63+
for _, svc := range svcs {
64+
_, _ = fmt.Fprintf(os.Stderr, "service: %+v\n", svc)
65+
current := &FakeService{
66+
Name: fmt.Sprintf("%sClient", *svc.Name),
67+
}
68+
methods := make([]Method, 0)
69+
for _, mtd := range svc.Method {
70+
method := Method{Name: *mtd.Name}
71+
if mtd.InputType != nil {
72+
method.Input = cleanInput(*mtd.InputType)
73+
}
74+
if mtd.OutputType != nil {
75+
method.Output = cleanInput(*mtd.OutputType)
76+
}
77+
methods = append(methods, method)
78+
}
79+
current.Methods = methods
80+
81+
fakeServices = append(fakeServices, current)
82+
}
83+
ret[*protoFile.Name] = fakeServices
84+
}
85+
return ret
86+
}
87+
88+
func (runner *GoFake) WriteImports(buf *bytes.Buffer, imports... string) {
89+
for _, i := range imports {
90+
buf.WriteString(fmt.Sprintf("\t\"%s\"\n", i))
91+
}
92+
}
93+
94+
func (runner *GoFake) CreateFakeFile(filename string, fakeSVC []*FakeService) error {
95+
var outfileName string
96+
var content string
97+
outfileName = strings.Replace(filename, ".proto", ".pb.fake.go", -1)
98+
var mdFile plugin.CodeGeneratorResponse_File
99+
mdFile.Name = &outfileName
100+
var buf bytes.Buffer
101+
102+
pkg := runner.Parameters["package"]
103+
pkgPath := runner.Parameters["packagePath"]
104+
105+
buf.WriteString("package fake\n\n")
106+
buf.WriteString("import (\n")
107+
runner.WriteImports(&buf, "context", "google.golang.org/grpc")
108+
buf.WriteString(fmt.Sprintf("\t%s \"%s\"\n", pkg, pkgPath))
109+
buf.WriteString(")\n\n")
110+
for _, fakeSVC := range fakeSVC {
111+
buf.WriteString(fmt.Sprintf("type Fake%s struct {\n", fakeSVC.Name))
112+
for _, mtd := range fakeSVC.Methods {
113+
buf.WriteString(fmt.Sprintf("\tFake%s func(ctx context.Context, in *%s.%s, opts ...grpc.CallOption) (*%s.%s, error)\n",
114+
mtd.Name, pkg, mtd.Input, pkg, mtd.Output))
115+
}
116+
buf.WriteString("}\n\n")
117+
for _, mtd := range fakeSVC.Methods {
118+
buf.WriteString(fmt.Sprintf("func (f *Fake%s) %s(ctx context.Context, in *%s.%s, opts ...grpc.CallOption) (*%s.%s, error) {\n",
119+
fakeSVC.Name, mtd.Name, pkg, mtd.Input, pkg, mtd.Output))
120+
buf.WriteString(fmt.Sprintf("\treturn f.Fake%s(ctx, in, opts...)\n",
121+
mtd.Name))
122+
buf.WriteString("}\n\n")
123+
}
124+
}
125+
content = buf.String()
126+
mdFile.Content = &content
127+
runner.Response.File = append(runner.Response.File, &mdFile)
128+
return nil
129+
}
130+
131+
func (runner *GoFake) generateMessageMarkdown() error {
132+
// This convenience method will return a structure of some types that I use
133+
for filename, locationMessages := range runner.getLocationMessage() {
134+
runner.CreateFakeFile(filename, locationMessages)
135+
}
136+
return nil
137+
}
138+
139+
func (runner *GoFake) generateCode() error {
140+
// Initialize the output file slice
141+
files := make([]*plugin.CodeGeneratorResponse_File, 0)
142+
runner.Response.File = files
143+
144+
err := runner.generateMessageMarkdown()
145+
if err != nil {
146+
return err
147+
}
148+
return nil
149+
}
150+
151+
func main() {
152+
// os.Stdin will contain data which will unmarshal into the following object:
153+
// https://godoc.org/github.com/golang/protobuf/protoc-gen-go/plugin#CodeGeneratorRequest
154+
req := &plugin.CodeGeneratorRequest{}
155+
resp := &plugin.CodeGeneratorResponse{}
156+
157+
data, err := ioutil.ReadAll(os.Stdin)
158+
if err != nil {
159+
panic(err)
160+
}
161+
162+
// You must use the requests unmarshal method to handle this type
163+
if err := proto.Unmarshal(data, req); err != nil {
164+
panic(err)
165+
}
166+
167+
// You may require more data than what is in the proto files alone. There are a couple ways in which to do this.
168+
// The first is by parameters. Another may be using leading comments in the proto files which I will cover in generateCode.
169+
parameters := req.GetParameter()
170+
// =grpc,import_path=mypackage:.
171+
exampleRunner := &GoFake{
172+
Request: req,
173+
Response: resp,
174+
Parameters: make(map[string]string),
175+
}
176+
groupkv := strings.Split(parameters, ",")
177+
for _, element := range groupkv {
178+
kv := strings.Split(element, "=")
179+
if len(kv) > 1 {
180+
exampleRunner.Parameters[kv[0]] = kv[1]
181+
}
182+
}
183+
// Print the parameters for example
184+
exampleRunner.PrintParameters(os.Stderr)
185+
186+
err = exampleRunner.generateCode()
187+
if err != nil {
188+
panic(err)
189+
}
190+
191+
marshalled, err := proto.Marshal(resp)
192+
if err != nil {
193+
panic(err)
194+
}
195+
os.Stdout.Write(marshalled)
196+
}

0 commit comments

Comments
 (0)