Skip to content

Commit e2f2d32

Browse files
authored
feat: add secret service interface (#164)
1 parent 79609ce commit e2f2d32

25 files changed

+1676
-255
lines changed

.github/testing/core.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,9 @@ items:
6767
request:
6868
api: /GetVersion
6969
method: POST
70+
- name: secrets
71+
request:
72+
api: /GetSecrets
73+
method: POST
74+
expect:
75+
statusCode: 500

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ grpc-gw:
6868
--grpc-gateway_opt paths=source_relative \
6969
--grpc-gateway_opt generate_unbound_methods=true \
7070
pkg/server/server.proto
71+
grpc-java:
72+
protoc --plugin=protoc-gen-grpc-java \
73+
--grpc-java_out=bin --proto_path=. \
74+
pkg/server/server.proto \
75+
pkg/testing/remote/loader.proto
7176
grpc-js:
7277
protoc -I=pkg/server server.proto \
7378
--js_out=import_style=commonjs:bin \

cmd/server.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
_ "embed"
1616

1717
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
18+
template "github.com/linuxsuren/api-testing/pkg/render"
1819
"github.com/linuxsuren/api-testing/pkg/server"
1920
"github.com/linuxsuren/api-testing/pkg/testing"
2021
"github.com/linuxsuren/api-testing/pkg/testing/remote"
@@ -44,6 +45,7 @@ func createServerCmd(execer fakeruntime.Execer, gRPCServer gRPCServer, httpServe
4445
flags.StringArrayVarP(&opt.localStorage, "local-storage", "", []string{"*.yaml"}, "The local storage path")
4546
flags.StringVarP(&opt.consolePath, "console-path", "", "", "The path of the console")
4647
flags.StringVarP(&opt.configDir, "config-dir", "", "$HOME/.config/atest", "The config directory")
48+
flags.StringVarP(&opt.secretServer, "secret-server", "", "", "The secret server URL")
4749
return
4850
}
4951

@@ -57,6 +59,7 @@ type serverOption struct {
5759
printProto bool
5860
localStorage []string
5961
consolePath string
62+
secretServer string
6063
configDir string
6164
}
6265

@@ -95,7 +98,16 @@ func (o *serverOption) runE(cmd *cobra.Command, args []string) (err error) {
9598
}
9699
}
97100

98-
removeServer := server.NewRemoteServer(loader, remote.NewGRPCloaderFromStore(), o.configDir)
101+
var secretServer remote.SecretServiceServer
102+
if o.secretServer != "" {
103+
if secretServer, err = remote.NewGRPCSecretFrom(o.secretServer); err != nil {
104+
return
105+
}
106+
107+
template.SetSecretGetter(remote.NewGRPCSecretGetter(secretServer))
108+
}
109+
110+
removeServer := server.NewRemoteServer(loader, remote.NewGRPCloaderFromStore(), secretServer, o.configDir)
99111
s := o.gRPCServer
100112
go func() {
101113
if gRPCServer, ok := s.(reflection.GRPCServer); ok {

cmd/server_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ func TestPrintProto(t *testing.T) {
3232
},
3333
}, {
3434
name: "random port",
35-
args: []string{"server", "-p=0", "--http-port=0", "--local-storage=./*"},
35+
args: []string{"server", "-p=0", "--http-port=0",
36+
"--local-storage=./*", "--secret-server=localhost:7073"},
3637
verify: func(t *testing.T, buf *bytes.Buffer, err error) {
3738
assert.Nil(t, err)
3839
},

docs/README.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,16 @@ Currently, it supports the following kinds of services:
2727

2828
* Operate System services
2929
* Linux, and Darwin
30-
* Podman, and Docker
30+
* [Podman](https://github.com/containers/podman), and Docker
3131

3232
Please see the following example usage:
3333

3434
```shell
35-
atest service start -m podman --version master
35+
sudo atest service install -m podman --version master
3636
```
3737

38+
the default web server port is `8080`. So you can visit it via: http://localhost:8080
39+
3840
## Run in k3s
3941

4042
```shell
@@ -51,6 +53,7 @@ There are multiple storage backends supported. See the status from the list:
5153
| Name | Status |
5254
|---|---|
5355
| Local Storage | Ready |
56+
| S3 | Ready |
5457
| ORM DataBase | Developing |
5558
| Etcd DataBase | Developing |
5659

@@ -108,7 +111,7 @@ podman run -p 7071:7071 \
108111
ghcr.io/linuxsuren/api-testing:master atest-store-orm
109112
```
110113

111-
### ORM S3 Storage
114+
### S3 Storage
112115
You can use a S3 compatible storage as the storage backend.
113116

114117
```shell
@@ -134,8 +137,17 @@ See also the expected configuration below:
134137
region: cn
135138
```
136139
140+
## Secret Server
141+
You can put the sensitive information into a secret server. For example, [Vault](https://www.github.com/hashicorp/vault).
142+
143+
Connect to [a vault extension](https://github.com/LinuxSuRen/api-testing-secret-extension) via flag: `--secret-server`. Such as:
144+
145+
```shell
146+
atest server --secret-server localhost:7073
147+
```
148+
137149
## Extensions
138-
Developers could have a storage extension. Implement a gRPC server according to [loader.proto](../pkg/testing/remote/loader.proto) is required.
150+
Developers could have storage, secret extensions. Implement a gRPC server according to [loader.proto](../pkg/testing/remote/loader.proto) is required.
139151

140152
## Official Images
141153
You could find the official images from both [Docker Hub](https://hub.docker.com/r/linuxsuren/api-testing) and [GitHub Images](https://github.com/users/LinuxSuRen/packages/container/package/api-testing). See the image path:

pkg/render/secret.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package render
2+
3+
import (
4+
"github.com/linuxsuren/api-testing/pkg/secret"
5+
)
6+
7+
type nonSecretGetter struct {
8+
value string
9+
err error
10+
}
11+
12+
func (n *nonSecretGetter) GetSecret(name string) (s secret.Secret, err error) {
13+
s.Value = n.value
14+
s.Name = name
15+
err = n.err
16+
return
17+
}

pkg/render/template.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,22 @@ import (
1010
"strings"
1111

1212
"github.com/Masterminds/sprig/v3"
13+
"github.com/linuxsuren/api-testing/pkg/secret"
1314
"github.com/linuxsuren/api-testing/pkg/util"
1415
)
1516

17+
var secretGetter secret.SecretGetter
18+
19+
// SetSecretGetter set the secret getter
20+
func SetSecretGetter(getter secret.SecretGetter) {
21+
if getter == nil {
22+
getter = &nonSecretGetter{
23+
err: fmt.Errorf("no secret server"),
24+
}
25+
}
26+
secretGetter = getter
27+
}
28+
1629
// Render render then return the result
1730
func Render(name, text string, ctx interface{}) (result string, err error) {
1831
var tpl *template.Template
@@ -69,6 +82,15 @@ var advancedFuncs = []AdvancedFunc{{
6982
writeWithContext(ctx, `{{randAlpha `+fields+`}}`)
7083
return
7184
},
85+
}, {
86+
FuncName: "secretValue",
87+
Func: func(name string) string {
88+
val, err := secretGetter.GetSecret(name)
89+
if err == nil {
90+
return val.Value
91+
}
92+
return err.Error()
93+
},
7294
}}
7395

7496
// GetAdvancedFuncs returns all the advanced functions
@@ -112,7 +134,6 @@ func writeWithContext(ctx context.Context, text string) {
112134
if ok && writer != nil {
113135
_, _ = writer.Write([]byte(text))
114136
}
115-
return
116137
}
117138

118139
// AdvancedFunc represents an advanced function

pkg/render/template_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,3 +154,18 @@ func TestGenerateJSONString(t *testing.T) {
154154
result := generateJSONString([]string{"name", "age"})
155155
assert.Equal(t, `{"age":"random","name":"random"}`, result)
156156
}
157+
158+
func TestSecret(t *testing.T) {
159+
SetSecretGetter(nil)
160+
result, err := Render("", `{{secretValue "pass"}}`, nil)
161+
assert.NoError(t, err)
162+
assert.Equal(t, "no secret server", result)
163+
164+
expected := "password"
165+
SetSecretGetter(&nonSecretGetter{
166+
value: expected,
167+
})
168+
result, err = Render("", `{{secretValue "pass"}}`, nil)
169+
assert.NoError(t, err)
170+
assert.Equal(t, expected, result)
171+
}

pkg/secret/types.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
MIT License
3+
Copyright (c) 2023 API Testing Authors.
4+
Permission is hereby granted, free of charge, to any person obtaining a copy
5+
of this software and associated documentation files (the "Software"), to deal
6+
in the Software without restriction, including without limitation the rights
7+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
copies of the Software, and to permit persons to whom the Software is
9+
furnished to do so, subject to the following conditions:
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18+
SOFTWARE.
19+
*/
20+
21+
package secret
22+
23+
type Secret struct {
24+
Name string
25+
Value string
26+
}
27+
28+
type SecretGetter interface {
29+
GetSecret(name string) (secret Secret, err error)
30+
}

pkg/server/remote_server.go

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,56 @@ type server struct {
3131
loader testing.Writer
3232
storeWriterFactory testing.StoreWriterFactory
3333
configDir string
34+
35+
secretServer SecretServiceServer
36+
}
37+
38+
type SecretServiceServer interface {
39+
GetSecrets(context.Context, *Empty) (*Secrets, error)
40+
CreateSecret(context.Context, *Secret) (*CommonResult, error)
41+
DeleteSecret(context.Context, *Secret) (*CommonResult, error)
42+
UpdateSecret(context.Context, *Secret) (*CommonResult, error)
43+
}
44+
45+
type SecertServiceGetable interface {
46+
GetSecret(context.Context, *Secret) (*Secret, error)
47+
}
48+
49+
type fakeSecretServer struct{}
50+
51+
var errNoSecretService = errors.New("no secret service found")
52+
53+
func (f *fakeSecretServer) GetSecrets(ctx context.Context, in *Empty) (reply *Secrets, err error) {
54+
err = errNoSecretService
55+
return
56+
}
57+
58+
func (f *fakeSecretServer) CreateSecret(ctx context.Context, in *Secret) (reply *CommonResult, err error) {
59+
err = errNoSecretService
60+
return
61+
}
62+
63+
func (f *fakeSecretServer) DeleteSecret(ctx context.Context, in *Secret) (reply *CommonResult, err error) {
64+
err = errNoSecretService
65+
return
66+
}
67+
68+
func (f *fakeSecretServer) UpdateSecret(ctx context.Context, in *Secret) (reply *CommonResult, err error) {
69+
err = errNoSecretService
70+
return
3471
}
3572

3673
// NewRemoteServer creates a remote server instance
37-
func NewRemoteServer(loader testing.Writer, storeWriterFactory testing.StoreWriterFactory, configDir string) RunnerServer {
74+
func NewRemoteServer(loader testing.Writer, storeWriterFactory testing.StoreWriterFactory, secretServer SecretServiceServer, configDir string) RunnerServer {
75+
if secretServer == nil {
76+
secretServer = &fakeSecretServer{}
77+
}
78+
3879
return &server{
3980
loader: loader,
4081
storeWriterFactory: storeWriterFactory,
4182
configDir: configDir,
83+
secretServer: secretServer,
4284
}
4385
}
4486

@@ -645,6 +687,20 @@ func (s *server) VerifyStore(ctx context.Context, in *SimpleQuery) (reply *Commo
645687
return
646688
}
647689

690+
// secret related interfaces
691+
func (s *server) GetSecrets(ctx context.Context, in *Empty) (reply *Secrets, err error) {
692+
return s.secretServer.GetSecrets(ctx, in)
693+
}
694+
func (s *server) CreateSecret(ctx context.Context, in *Secret) (reply *CommonResult, err error) {
695+
return s.secretServer.CreateSecret(ctx, in)
696+
}
697+
func (s *server) DeleteSecret(ctx context.Context, in *Secret) (reply *CommonResult, err error) {
698+
return s.secretServer.DeleteSecret(ctx, in)
699+
}
700+
func (s *server) UpdateSecret(ctx context.Context, in *Secret) (reply *CommonResult, err error) {
701+
return s.secretServer.UpdateSecret(ctx, in)
702+
}
703+
648704
func (s *server) getLoaderByStoreName(storeName string) (loader testing.Writer, err error) {
649705
var store *testing.Store
650706
store, err = testing.NewStoreFactory(s.configDir).GetStore(storeName)

0 commit comments

Comments
 (0)