Skip to content

Commit 31dda6a

Browse files
authored
feat: Add startup hooks (#405)
1 parent d74ff0e commit 31dda6a

32 files changed

+899
-130
lines changed

cmd/layotto/main.go

+1
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ func NewRuntimeGrpcServer(data json.RawMessage, opts ...grpc.ServerOption) (mgrp
197197
}
198198
// 2. new instance
199199
rt := runtime.NewMosnRuntime(cfg)
200+
rt.AppendInitRuntimeStage(runtime.DefaultInitRuntimeStage)
200201
// 3. run
201202
server, err := rt.Run(
202203
runtime.WithGrpcOptions(opts...),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//
2+
// Copyright 2021 Layotto Authors
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package component
15+
16+
import (
17+
"mosn.io/layotto/components/custom"
18+
)
19+
20+
type HelloWorld interface {
21+
custom.Component
22+
SayHello(name string) (string, error)
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//
2+
// Copyright 2021 Layotto Authors
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package component
15+
16+
import (
17+
"context"
18+
"mosn.io/layotto/components/custom"
19+
)
20+
21+
type inMemoryHelloWorld struct {
22+
ctx context.Context
23+
config *custom.Config
24+
}
25+
26+
func (i *inMemoryHelloWorld) Initialize(ctx context.Context, config custom.Config) error {
27+
i.ctx = ctx
28+
i.config = &config
29+
return nil
30+
}
31+
32+
func (i *inMemoryHelloWorld) SayHello(name string) (string, error) {
33+
return "Hello " + name, nil
34+
}
35+
36+
func NewInMemoryHelloWorld() custom.Component {
37+
return &inMemoryHelloWorld{}
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//
2+
// Copyright 2021 Layotto Authors
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package component
15+
16+
import (
17+
"context"
18+
"mosn.io/layotto/components/custom"
19+
)
20+
21+
type sayGoodbyeHelloWorld struct {
22+
ctx context.Context
23+
config *custom.Config
24+
}
25+
26+
func (s *sayGoodbyeHelloWorld) Initialize(ctx context.Context, config custom.Config) error {
27+
s.ctx = ctx
28+
s.config = &config
29+
return nil
30+
}
31+
32+
func (s *sayGoodbyeHelloWorld) SayHello(name string) (string, error) {
33+
return "Goodbye " + name, nil
34+
}
35+
36+
func NewSayGoodbyeHelloWorld() custom.Component {
37+
return &sayGoodbyeHelloWorld{}
38+
}

cmd/layotto_multiple_api/helloworld/grpc_api.go

+55-10
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,77 @@ package helloworld
1818

1919
import (
2020
"context"
21+
"fmt"
2122
rawGRPC "google.golang.org/grpc"
2223
pb "google.golang.org/grpc/examples/helloworld/helloworld"
24+
"mosn.io/layotto/cmd/layotto_multiple_api/helloworld/component"
25+
"mosn.io/layotto/components/lock"
2326
"mosn.io/layotto/pkg/grpc"
2427
grpc_api "mosn.io/layotto/pkg/grpc"
25-
mgrpc "mosn.io/mosn/pkg/filter/network/grpc"
28+
"mosn.io/pkg/log"
2629
)
2730

31+
const componentType = "helloworld"
32+
33+
// This demo will always use this component name.
34+
const componentName = "in-memory"
35+
2836
func NewHelloWorldAPI(ac *grpc_api.ApplicationContext) grpc.GrpcAPI {
29-
return &server{}
37+
// 1. convert custom components
38+
name2component := make(map[string]component.HelloWorld)
39+
if len(ac.CustomComponent) != 0 {
40+
// we only care about those components of type "helloworld"
41+
name2comp, ok := ac.CustomComponent[componentType]
42+
if ok && len(name2comp) > 0 {
43+
for name, v := range name2comp {
44+
// convert them using type assertion
45+
comp, ok := v.(component.HelloWorld)
46+
if !ok {
47+
errMsg := fmt.Sprintf("custom component %s does not implement HelloWorld interface", name)
48+
log.DefaultLogger.Errorf(errMsg)
49+
}
50+
name2component[name] = comp
51+
}
52+
}
53+
}
54+
// 2. construct your API implementation
55+
return &server{
56+
appId: ac.AppId,
57+
// Your API plugin can store and use all the components.
58+
// For example,this demo set all the LockStore components here.
59+
name2LockStore: ac.LockStores,
60+
// Custom components of type "helloworld"
61+
name2component: name2component,
62+
}
3063
}
3164

3265
// server is used to implement helloworld.GreeterServer.
3366
type server struct {
67+
appId string
68+
// custom components which implements the `HelloWorld` interface
69+
name2component map[string]component.HelloWorld
70+
// LockStore components. They are not used in this demo, we put them here as a demo.
71+
name2LockStore map[string]lock.LockStore
3472
pb.UnimplementedGreeterServer
3573
}
3674

37-
func (s *server) Init(conn *rawGRPC.ClientConn) error {
38-
return nil
75+
// SayHello implements helloworld.GreeterServer.SayHello
76+
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
77+
if _, ok := s.name2component[componentName]; !ok {
78+
return &pb.HelloReply{Message: "We don't want to talk with you!"}, nil
79+
}
80+
message, err := s.name2component[componentName].SayHello(in.GetName())
81+
if err != nil {
82+
return nil, err
83+
}
84+
return &pb.HelloReply{Message: message}, nil
3985
}
4086

41-
func (s *server) Register(grpcServer *rawGRPC.Server, registeredServer mgrpc.RegisteredServer) (mgrpc.RegisteredServer, error) {
42-
pb.RegisterGreeterServer(grpcServer, s)
43-
return registeredServer, nil
87+
func (s *server) Init(conn *rawGRPC.ClientConn) error {
88+
return nil
4489
}
4590

46-
// SayHello implements helloworld.GreeterServer
47-
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
48-
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
91+
func (s *server) Register(rawGrpcServer *rawGRPC.Server) error {
92+
pb.RegisterGreeterServer(rawGrpcServer, s)
93+
return nil
4994
}

cmd/layotto_multiple_api/main.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import (
2020
"encoding/json"
2121
"mosn.io/api"
2222
helloworld_api "mosn.io/layotto/cmd/layotto_multiple_api/helloworld"
23+
"mosn.io/layotto/cmd/layotto_multiple_api/helloworld/component"
24+
"mosn.io/layotto/components/custom"
2325
component_actuators "mosn.io/layotto/components/pkg/actuators"
2426
l8_grpc "mosn.io/layotto/pkg/grpc"
2527
"mosn.io/layotto/pkg/grpc/dapr"
@@ -188,6 +190,7 @@ func NewRuntimeGrpcServer(data json.RawMessage, opts ...grpc.ServerOption) (mgrp
188190
}
189191
// 2. new instance
190192
rt := runtime.NewMosnRuntime(cfg)
193+
rt.AppendInitRuntimeStage(runtime.DefaultInitRuntimeStage)
191194
// 3. run
192195
server, err := rt.Run(
193196
runtime.WithGrpcOptions(opts...),
@@ -206,7 +209,7 @@ func NewRuntimeGrpcServer(data json.RawMessage, opts ...grpc.ServerOption) (mgrp
206209
// a demo to show how to register your own gRPC API
207210
helloworld_api.NewHelloWorldAPI,
208211
// support Dapr API
209-
// Currently it only support Dapr's InvokeService and InvokeBinding API.
212+
// Currently it only support Dapr's InvokeService,secret API,state API and InvokeBinding API.
210213
// Note: this feature is still in Alpha state and we don't recommend that you use it in your production environment.
211214
dapr.NewDaprAPI_Alpha,
212215
),
@@ -378,7 +381,13 @@ func NewRuntimeGrpcServer(data json.RawMessage, opts ...grpc.ServerOption) (mgrp
378381
runtime_sequencer.NewFactory("in-memory", func() sequencer.Store {
379382
return sequencer_inmemory.NewInMemorySequencer()
380383
}),
381-
))
384+
),
385+
// Custom components
386+
runtime.WithCustomComponentFactory("helloworld",
387+
custom.NewComponentFactory("in-memory", component.NewInMemoryHelloWorld),
388+
custom.NewComponentFactory("goodbye", component.NewSayGoodbyeHelloWorld),
389+
),
390+
)
382391
return server, err
383392
}
384393

components/custom/component.go

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//
2+
// Copyright 2021 Layotto Authors
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package custom
15+
16+
import "context"
17+
18+
type Component interface {
19+
Initialize(ctx context.Context, config Config) error
20+
}

components/custom/config.go

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//
2+
// Copyright 2021 Layotto Authors
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package custom
15+
16+
type Config struct {
17+
Version string `json:"version"`
18+
Metadata map[string]string `json:"metadata"`
19+
}

components/custom/registry.go

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//
2+
// Copyright 2021 Layotto Authors
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package custom
15+
16+
import (
17+
"fmt"
18+
"mosn.io/layotto/components/pkg/info"
19+
)
20+
21+
type Registry interface {
22+
Register(componentType string, factorys ...*ComponentFactory)
23+
Create(componentType, name string) (Component, error)
24+
}
25+
26+
type ComponentFactory struct {
27+
Name string
28+
FactoryMethod func() Component
29+
}
30+
31+
func NewComponentFactory(name string, f func() Component) *ComponentFactory {
32+
return &ComponentFactory{
33+
Name: name,
34+
FactoryMethod: f,
35+
}
36+
}
37+
38+
type componentRegistry struct {
39+
stores map[string]map[string]func() Component
40+
info *info.RuntimeInfo
41+
}
42+
43+
func NewRegistry(info *info.RuntimeInfo) Registry {
44+
return &componentRegistry{
45+
stores: make(map[string]map[string]func() Component),
46+
info: info,
47+
}
48+
}
49+
50+
func (r *componentRegistry) Register(componentType string, fs ...*ComponentFactory) {
51+
if len(fs) == 0 {
52+
return
53+
}
54+
r.info.AddService(componentType)
55+
// lazy init
56+
if _, ok := r.stores[componentType]; !ok {
57+
r.stores[componentType] = make(map[string]func() Component)
58+
}
59+
// register FactoryMethod
60+
for _, f := range fs {
61+
r.stores[componentType][f.Name] = f.FactoryMethod
62+
r.info.RegisterComponent(componentType, f.Name)
63+
}
64+
}
65+
66+
func (r *componentRegistry) Create(componentType, name string) (Component, error) {
67+
store, ok := r.stores[componentType]
68+
if !ok {
69+
return nil, fmt.Errorf("custom component type %s is not regsitered", componentType)
70+
}
71+
if f, ok := store[name]; ok {
72+
r.info.LoadComponent(componentType, name)
73+
return f(), nil
74+
}
75+
return nil, fmt.Errorf("custom component %s is not regsitered", name)
76+
}

components/custom/registry_test.go

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2021 Layotto Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package custom
18+
19+
import (
20+
"mosn.io/layotto/components/pkg/info"
21+
"strings"
22+
"testing"
23+
)
24+
25+
func TestNewRegistry(t *testing.T) {
26+
r := NewRegistry(info.NewRuntimeInfo())
27+
compType := "my_component"
28+
compName := "etcd"
29+
r.Register(compType,
30+
NewComponentFactory(compName, func() Component {
31+
return nil
32+
}),
33+
)
34+
_, err := r.Create(compType, compName)
35+
if err != nil {
36+
t.Fatalf("create mock store failed: %v", err)
37+
}
38+
if _, err := r.Create(compType, "not exists"); !strings.Contains(err.Error(), "not regsitered") {
39+
t.Fatalf("create mock store failed: %v", err)
40+
}
41+
}

0 commit comments

Comments
 (0)