Skip to content

Commit

Permalink
feat: Add startup hooks (#405)
Browse files Browse the repository at this point in the history
  • Loading branch information
seeflood authored Apr 1, 2022
1 parent d74ff0e commit 31dda6a
Show file tree
Hide file tree
Showing 32 changed files with 899 additions and 130 deletions.
1 change: 1 addition & 0 deletions cmd/layotto/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ func NewRuntimeGrpcServer(data json.RawMessage, opts ...grpc.ServerOption) (mgrp
}
// 2. new instance
rt := runtime.NewMosnRuntime(cfg)
rt.AppendInitRuntimeStage(runtime.DefaultInitRuntimeStage)
// 3. run
server, err := rt.Run(
runtime.WithGrpcOptions(opts...),
Expand Down
23 changes: 23 additions & 0 deletions cmd/layotto_multiple_api/helloworld/component/helloworld.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// Copyright 2021 Layotto Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package component

import (
"mosn.io/layotto/components/custom"
)

type HelloWorld interface {
custom.Component
SayHello(name string) (string, error)
}
38 changes: 38 additions & 0 deletions cmd/layotto_multiple_api/helloworld/component/in_memory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// Copyright 2021 Layotto Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package component

import (
"context"
"mosn.io/layotto/components/custom"
)

type inMemoryHelloWorld struct {
ctx context.Context
config *custom.Config
}

func (i *inMemoryHelloWorld) Initialize(ctx context.Context, config custom.Config) error {
i.ctx = ctx
i.config = &config
return nil
}

func (i *inMemoryHelloWorld) SayHello(name string) (string, error) {
return "Hello " + name, nil
}

func NewInMemoryHelloWorld() custom.Component {
return &inMemoryHelloWorld{}
}
38 changes: 38 additions & 0 deletions cmd/layotto_multiple_api/helloworld/component/say_goodbye.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// Copyright 2021 Layotto Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package component

import (
"context"
"mosn.io/layotto/components/custom"
)

type sayGoodbyeHelloWorld struct {
ctx context.Context
config *custom.Config
}

func (s *sayGoodbyeHelloWorld) Initialize(ctx context.Context, config custom.Config) error {
s.ctx = ctx
s.config = &config
return nil
}

func (s *sayGoodbyeHelloWorld) SayHello(name string) (string, error) {
return "Goodbye " + name, nil
}

func NewSayGoodbyeHelloWorld() custom.Component {
return &sayGoodbyeHelloWorld{}
}
65 changes: 55 additions & 10 deletions cmd/layotto_multiple_api/helloworld/grpc_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,32 +18,77 @@ package helloworld

import (
"context"
"fmt"
rawGRPC "google.golang.org/grpc"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
"mosn.io/layotto/cmd/layotto_multiple_api/helloworld/component"
"mosn.io/layotto/components/lock"
"mosn.io/layotto/pkg/grpc"
grpc_api "mosn.io/layotto/pkg/grpc"
mgrpc "mosn.io/mosn/pkg/filter/network/grpc"
"mosn.io/pkg/log"
)

const componentType = "helloworld"

// This demo will always use this component name.
const componentName = "in-memory"

func NewHelloWorldAPI(ac *grpc_api.ApplicationContext) grpc.GrpcAPI {
return &server{}
// 1. convert custom components
name2component := make(map[string]component.HelloWorld)
if len(ac.CustomComponent) != 0 {
// we only care about those components of type "helloworld"
name2comp, ok := ac.CustomComponent[componentType]
if ok && len(name2comp) > 0 {
for name, v := range name2comp {
// convert them using type assertion
comp, ok := v.(component.HelloWorld)
if !ok {
errMsg := fmt.Sprintf("custom component %s does not implement HelloWorld interface", name)
log.DefaultLogger.Errorf(errMsg)
}
name2component[name] = comp
}
}
}
// 2. construct your API implementation
return &server{
appId: ac.AppId,
// Your API plugin can store and use all the components.
// For example,this demo set all the LockStore components here.
name2LockStore: ac.LockStores,
// Custom components of type "helloworld"
name2component: name2component,
}
}

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

func (s *server) Init(conn *rawGRPC.ClientConn) error {
return nil
// SayHello implements helloworld.GreeterServer.SayHello
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
if _, ok := s.name2component[componentName]; !ok {
return &pb.HelloReply{Message: "We don't want to talk with you!"}, nil
}
message, err := s.name2component[componentName].SayHello(in.GetName())
if err != nil {
return nil, err
}
return &pb.HelloReply{Message: message}, nil
}

func (s *server) Register(grpcServer *rawGRPC.Server, registeredServer mgrpc.RegisteredServer) (mgrpc.RegisteredServer, error) {
pb.RegisterGreeterServer(grpcServer, s)
return registeredServer, nil
func (s *server) Init(conn *rawGRPC.ClientConn) error {
return nil
}

// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
func (s *server) Register(rawGrpcServer *rawGRPC.Server) error {
pb.RegisterGreeterServer(rawGrpcServer, s)
return nil
}
13 changes: 11 additions & 2 deletions cmd/layotto_multiple_api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"encoding/json"
"mosn.io/api"
helloworld_api "mosn.io/layotto/cmd/layotto_multiple_api/helloworld"
"mosn.io/layotto/cmd/layotto_multiple_api/helloworld/component"
"mosn.io/layotto/components/custom"
component_actuators "mosn.io/layotto/components/pkg/actuators"
l8_grpc "mosn.io/layotto/pkg/grpc"
"mosn.io/layotto/pkg/grpc/dapr"
Expand Down Expand Up @@ -188,6 +190,7 @@ func NewRuntimeGrpcServer(data json.RawMessage, opts ...grpc.ServerOption) (mgrp
}
// 2. new instance
rt := runtime.NewMosnRuntime(cfg)
rt.AppendInitRuntimeStage(runtime.DefaultInitRuntimeStage)
// 3. run
server, err := rt.Run(
runtime.WithGrpcOptions(opts...),
Expand All @@ -206,7 +209,7 @@ func NewRuntimeGrpcServer(data json.RawMessage, opts ...grpc.ServerOption) (mgrp
// a demo to show how to register your own gRPC API
helloworld_api.NewHelloWorldAPI,
// support Dapr API
// Currently it only support Dapr's InvokeService and InvokeBinding API.
// Currently it only support Dapr's InvokeService,secret API,state API and InvokeBinding API.
// Note: this feature is still in Alpha state and we don't recommend that you use it in your production environment.
dapr.NewDaprAPI_Alpha,
),
Expand Down Expand Up @@ -378,7 +381,13 @@ func NewRuntimeGrpcServer(data json.RawMessage, opts ...grpc.ServerOption) (mgrp
runtime_sequencer.NewFactory("in-memory", func() sequencer.Store {
return sequencer_inmemory.NewInMemorySequencer()
}),
))
),
// Custom components
runtime.WithCustomComponentFactory("helloworld",
custom.NewComponentFactory("in-memory", component.NewInMemoryHelloWorld),
custom.NewComponentFactory("goodbye", component.NewSayGoodbyeHelloWorld),
),
)
return server, err
}

Expand Down
20 changes: 20 additions & 0 deletions components/custom/component.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// Copyright 2021 Layotto Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package custom

import "context"

type Component interface {
Initialize(ctx context.Context, config Config) error
}
19 changes: 19 additions & 0 deletions components/custom/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// Copyright 2021 Layotto Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package custom

type Config struct {
Version string `json:"version"`
Metadata map[string]string `json:"metadata"`
}
76 changes: 76 additions & 0 deletions components/custom/registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//
// Copyright 2021 Layotto Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package custom

import (
"fmt"
"mosn.io/layotto/components/pkg/info"
)

type Registry interface {
Register(componentType string, factorys ...*ComponentFactory)
Create(componentType, name string) (Component, error)
}

type ComponentFactory struct {
Name string
FactoryMethod func() Component
}

func NewComponentFactory(name string, f func() Component) *ComponentFactory {
return &ComponentFactory{
Name: name,
FactoryMethod: f,
}
}

type componentRegistry struct {
stores map[string]map[string]func() Component
info *info.RuntimeInfo
}

func NewRegistry(info *info.RuntimeInfo) Registry {
return &componentRegistry{
stores: make(map[string]map[string]func() Component),
info: info,
}
}

func (r *componentRegistry) Register(componentType string, fs ...*ComponentFactory) {
if len(fs) == 0 {
return
}
r.info.AddService(componentType)
// lazy init
if _, ok := r.stores[componentType]; !ok {
r.stores[componentType] = make(map[string]func() Component)
}
// register FactoryMethod
for _, f := range fs {
r.stores[componentType][f.Name] = f.FactoryMethod
r.info.RegisterComponent(componentType, f.Name)
}
}

func (r *componentRegistry) Create(componentType, name string) (Component, error) {
store, ok := r.stores[componentType]
if !ok {
return nil, fmt.Errorf("custom component type %s is not regsitered", componentType)
}
if f, ok := store[name]; ok {
r.info.LoadComponent(componentType, name)
return f(), nil
}
return nil, fmt.Errorf("custom component %s is not regsitered", name)
}
41 changes: 41 additions & 0 deletions components/custom/registry_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2021 Layotto Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package custom

import (
"mosn.io/layotto/components/pkg/info"
"strings"
"testing"
)

func TestNewRegistry(t *testing.T) {
r := NewRegistry(info.NewRuntimeInfo())
compType := "my_component"
compName := "etcd"
r.Register(compType,
NewComponentFactory(compName, func() Component {
return nil
}),
)
_, err := r.Create(compType, compName)
if err != nil {
t.Fatalf("create mock store failed: %v", err)
}
if _, err := r.Create(compType, "not exists"); !strings.Contains(err.Error(), "not regsitered") {
t.Fatalf("create mock store failed: %v", err)
}
}
Loading

0 comments on commit 31dda6a

Please sign in to comment.