Skip to content

Commit

Permalink
ci: add more coverage (#332)
Browse files Browse the repository at this point in the history
Signed-off-by: spacewander <[email protected]>
  • Loading branch information
spacewander authored Feb 27, 2024
1 parent aabf4a7 commit 0c97187
Show file tree
Hide file tree
Showing 13 changed files with 287 additions and 30 deletions.
8 changes: 4 additions & 4 deletions .github/codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ coverage:
project:
default:
target: 80%
threshold: 5%
threshold: 5% # 75%~80% is the yellow zone - ok to merge but should increase it
if_ci_failed: error
patch:
default:
target: 80%
threshold: 20% # higher threshold for single change
target: 70% # lower target for single change
if_ci_failed: error
ignore:
# test files
- "plugins/tests"
- "controller/tests"
- "e2e/"
- "**/mock.go"
# examples
- "examples/"
# tools
Expand All @@ -23,4 +23,4 @@ ignore:
# generated code
- "**/*.pb.go"
- "**/*.pb.validate.go"
- 'zz_generated.deepcopy.go'
- '**/zz_generated.deepcopy.go'
46 changes: 38 additions & 8 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ concurrency:

env:
IN_CI: true
COVERAGE_OPTION: "-covermode=atomic -coverprofile=coverage.out"

jobs:
unit-test:
Expand All @@ -34,14 +33,12 @@ jobs:

- name: Test
run: make unit-test
- name: Upload to codecov
uses: codecov/[email protected]
- name: Upload artifact
if: always() # always upload coverage, so the coverage percents won't affect by the failed tests
uses: actions/upload-artifact@v4
with:
fail_ci_if_error: false # let CI build pass if Codecov runs into an error during upload
file: ./coverage.out
flags: unittests
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true
name: unit-test-cover
path: ./cover.out

plugins-integration-test:
timeout-minutes: 10
Expand Down Expand Up @@ -70,6 +67,16 @@ jobs:
# upload artifact can be found in https://github.com/mosn/htnn/actions/runs/$id
name: ci-logs
path: ./test-envoy
- name: Generate coverage
if: always()
run: |
go tool covdata textfmt -i=/tmp/htnn_coverage -o plugins/tests/integration/cover.out -v 2
- name: Upload artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: plugins-integration-test-cover
path: plugins/tests/integration/cover.out

controller-test:
timeout-minutes: 10
Expand Down Expand Up @@ -97,3 +104,26 @@ jobs:
- name: Test
run: make test
- name: Upload artifact
if: always()
uses: actions/upload-artifact@v4
with:
name: controller-test-cover
path: ./controller/cover.out

coverage:
timeout-minutes: 10
runs-on: ubuntu-latest
needs: [unit-test, plugins-integration-test, controller-test]
if: always()
steps:
- uses: actions/checkout@v4
- name: Download artifact
uses: actions/download-artifact@v4
- name: Upload to codecov
uses: codecov/[email protected]
with:
fail_ci_if_error: true
file: ./cover.out,plugins/tests/integration/cover.out,./controller/cover.out
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ PROTOC = protoc
PROTO_FILES = $(call rwildcard,./,*.proto)
GO_TARGETS = $(patsubst %.proto,%.pb.go,$(PROTO_FILES))

TEST_OPTION ?= -gcflags="all=-N -l" -race $(COVERAGE_OPTION)
TEST_OPTION ?= -gcflags="all=-N -l" -race -covermode=atomic -coverprofile=cover.out -coverpkg=./...

MOUNT_GOMOD_CACHE = -v $(shell go env GOPATH):/go
ifeq ($(IN_CI), true)
Expand Down Expand Up @@ -92,6 +92,7 @@ build-test-so-local:
CGO_ENABLED=1 go build -tags so \
-ldflags "-B 0x$(shell head -c20 /dev/urandom|od -An -tx1|tr -d ' \n') -X main.Version=${VERSION}(${GIT_VERSION})" \
--buildmode=c-shared \
-cover -covermode=atomic -coverpkg=./... \
-v -o plugins/tests/integration/${TARGET_SO} \
${PROJECT_NAME}/plugins/tests/integration/libgolang

Expand All @@ -109,6 +110,7 @@ plugins-integration-test:
if ! docker images ${PROXY_IMAGE} | grep envoyproxy/envoy > /dev/null; then \
docker pull ${PROXY_IMAGE}; \
fi
test -d /tmp/htnn_coverage && rm -rf /tmp/htnn_coverage || true
$(foreach PKG, $(shell go list ./plugins/tests/integration/...), \
go test -v ${PKG} || exit 1; \
)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# HTNN

[![test](https://github.com/mosn/htnn/actions/workflows/test.yml/badge.svg)](https://github.com/mosn/htnn/actions/workflows/test.yml)
[![codecov](https://codecov.io/gh/mosn/htnn/branch/main/graph/badge.svg)](https://codecov.io/gh/mosn/htnn)

HTNN is...

Expand Down
2 changes: 1 addition & 1 deletion controller/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and
.PHONY: test
test: manifests generate envtest ## Run tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test -gcflags="all=-N -l" -race \
./... -coverprofile cover.out
./... -coverprofile cover.out -covermode=atomic -coverpkg=./...

.PHONY: benchmark
benchmark: manifests generate envtest ## Run benchmarks
Expand Down
36 changes: 36 additions & 0 deletions pkg/plugins/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,39 @@ func (m *MockPlugin) Merge(parent interface{}, child interface{}) interface{} {
type MockPluginConfig struct {
Config
}

type MockConsumerPlugin struct {
MockPlugin
}

func (m *MockConsumerPlugin) Order() PluginOrder {
return PluginOrder{
Position: OrderPositionAuthn,
}
}

func (m *MockConsumerPlugin) ConsumerConfig() api.PluginConsumerConfig {
return nil
}

type MockNativePlugin struct {
PluginMethodDefaultImpl
}

func (m *MockNativePlugin) Config() api.PluginConfig {
return &MockPluginConfig{}
}

func (m *MockNativePlugin) Order() PluginOrder {
return PluginOrder{
Position: OrderPositionOuter,
}
}

func (m *MockNativePlugin) RouteConfigTypeURL() string {
return ""
}

func (m *MockNativePlugin) HTTPFilterConfigPlaceholder() map[string]interface{} {
return nil
}
22 changes: 16 additions & 6 deletions pkg/plugins/plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,32 +55,42 @@ func LoadHttpFilterFactoryAndParser(name string) *FilterFactoryAndParser {
return httpFilterFactoryAndParser[name]
}

const (
errNilPlugin = "plugin should not be nil"
errUnknownPluginType = "a plugin should be either Go plugin or Native plugin"
errInvalidGoPluginOrder = "invalid plugin order position: Go plugin should not use OrderPositionOuter or OrderPositionInner"
errInvalidNativePluginOrder = "invalid plugin order position: Native plugin should use OrderPositionOuter or OrderPositionInner"
errInvalidConsumerPluginOrder = "invalid plugin order position: Consumer plugin should use OrderPositionAuthn"
)

func RegisterHttpPlugin(name string, plugin Plugin) {
if plugin == nil {
panic("plugin should not be nil")
panic(errNilPlugin)
}

logger.Info("register plugin", "name", name)

if goPlugin, ok := plugin.(GoPlugin); ok {
order := plugin.Order()
if order.Position == OrderPositionOuter || order.Position == OrderPositionInner {
panic("invalid plugin order position: Go plugin should not use OrderPositionOuter or OrderPositionInner")
panic(errInvalidGoPluginOrder)
}
RegisterHttpFilterFactoryAndParser(name,
goPlugin.Factory(),
NewPluginConfigParser(goPlugin))
}
if _, ok := plugin.(NativePlugin); ok {
} else if _, ok := plugin.(NativePlugin); ok {
order := plugin.Order()
if order.Position != OrderPositionOuter && order.Position != OrderPositionInner {
panic("invalid plugin order position: Native plugin should use OrderPositionOuter or OrderPositionInner")
panic(errInvalidNativePluginOrder)
}
} else {
panic(errUnknownPluginType)
}

if _, ok := plugin.(ConsumerPlugin); ok {
order := plugin.Order()
if order.Position != OrderPositionAuthn {
panic("invalid plugin order position: Consumer plugin should use OrderPositionAuthn")
panic(errInvalidConsumerPluginOrder)
}
}

Expand Down
93 changes: 87 additions & 6 deletions pkg/plugins/plugins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,33 @@ func TestMerge(t *testing.T) {
assert.Equal(t, "parent", res)
}

type pluginOrderWrapper struct {
Plugin
type goPluginOrderWrapper struct {
GoPlugin

order PluginOrder
}

func (p *pluginOrderWrapper) Order() PluginOrder {
func (p *goPluginOrderWrapper) Order() PluginOrder {
return p.order
}

type consumerPluginWrapper struct {
ConsumerPlugin

order PluginOrder
}

func (p *consumerPluginWrapper) Order() PluginOrder {
return p.order
}

type nativePluginWrapper struct {
NativePlugin

order PluginOrder
}

func (p *nativePluginWrapper) Order() PluginOrder {
return p.order
}

Expand All @@ -146,9 +166,9 @@ func TestComparePluginOrder(t *testing.T) {
},
}
for name, po := range pluginOrders {
RegisterHttpPlugin(name, &pluginOrderWrapper{
Plugin: plugin,
order: po,
RegisterHttpPlugin(name, &goPluginOrderWrapper{
GoPlugin: plugin,
order: po,
})
}

Expand All @@ -170,3 +190,64 @@ func TestComparePluginOrder(t *testing.T) {
"authz_last",
}, plugins)
}

func TestRejectBadPluginDef(t *testing.T) {
type pluginWrapper struct {
Plugin
}

cases := []struct {
name string
input Plugin
err string
}{
{
name: "unknown type",
input: &pluginWrapper{
Plugin: &MockPlugin{},
},
err: errUnknownPluginType,
},
{
name: "nil plugin",
err: errNilPlugin,
},
{
name: "invalid Go plugin order",
input: &goPluginOrderWrapper{
GoPlugin: &MockPlugin{},
order: PluginOrder{
Position: OrderPositionInner,
},
},
err: errInvalidGoPluginOrder,
},
{
name: "invalid Native plugin order",
input: &nativePluginWrapper{
NativePlugin: &MockNativePlugin{},
order: PluginOrder{
Position: OrderPositionAuthz,
},
},
err: errInvalidNativePluginOrder,
},
{
name: "invalid Consumer plugin order",
input: &consumerPluginWrapper{
ConsumerPlugin: &MockConsumerPlugin{},
order: PluginOrder{
Position: OrderPositionAuthz,
},
},
err: errInvalidConsumerPluginOrder,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
assert.PanicsWithValue(t, c.err, func() {
RegisterHttpPlugin(c.name, c.input)
})
})
}
}
2 changes: 1 addition & 1 deletion pkg/plugins/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ type GoPlugin interface {
}

type ConsumerPlugin interface {
Plugin
GoPlugin

ConsumerConfig() api.PluginConsumerConfig
}
24 changes: 24 additions & 0 deletions plugins/tests/integration/control_plane/control_plane.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,30 @@ func (cp *ControlPlane) UseGoPluginConfig(config *filtermanager.FilterManagerCon
},
},
},
{
Match: &route.RouteMatch{
PathSpecifier: &route.RouteMatch_Path{
Path: "/flush_coverage",
},
},
Action: &route.Route_DirectResponse{
DirectResponse: &route.DirectResponseAction{
Status: 200,
},
},
TypedPerFilterConfig: map[string]*any1.Any{
"envoy.filters.http.golang": proto.MessageToAny(&golang.ConfigsPerRoute{
PluginsConfig: map[string]*golang.RouterPlugin{
"fm": {
Override: &golang.RouterPlugin_Config{
Config: proto.MessageToAny(
FilterManagerConfigToTypedStruct(NewSinglePluinConfig("coverage", nil))),
},
},
},
}),
},
},
{
Match: &route.RouteMatch{
PathSpecifier: &route.RouteMatch_Prefix{
Expand Down
Loading

0 comments on commit 0c97187

Please sign in to comment.