Moved MMF for end-to-end in-cluster testing to internal (#1014)

* Moved MMF for end-to-end in-cluster testing to internal

* Fix
This commit is contained in:
yfei1
2019-12-11 16:55:43 -08:00
committed by GitHub
parent f56263b074
commit e048b97c71
8 changed files with 233 additions and 15 deletions

View File

@ -244,7 +244,7 @@ build-mmf-go-rosterbased-image: docker build-base-build-image
docker build -f examples/functions/golang/rosterbased/Dockerfile -t $(REGISTRY)/openmatch-mmf-go-rosterbased:$(TAG) -t $(REGISTRY)/openmatch-mmf-go-rosterbased:$(ALTERNATE_TAG) .
build-mmf-go-pool-image: docker build-base-build-image
docker build -f examples/functions/golang/pool/Dockerfile -t $(REGISTRY)/openmatch-mmf-go-pool:$(TAG) -t $(REGISTRY)/openmatch-mmf-go-pool:$(ALTERNATE_TAG) .
docker build -f test/customize/matchfunction/Dockerfile -t $(REGISTRY)/openmatch-mmf-go-pool:$(TAG) -t $(REGISTRY)/openmatch-mmf-go-pool:$(ALTERNATE_TAG) .
build-evaluator-go-simple-image: docker build-base-build-image
docker build -f test/evaluator/Dockerfile -t $(REGISTRY)/openmatch-evaluator-go-simple:$(TAG) -t $(REGISTRY)/openmatch-evaluator-go-simple:$(ALTERNATE_TAG) .

View File

@ -21,16 +21,17 @@ import (
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
pool "open-match.dev/open-match/examples/functions/golang/pool/mmf"
"open-match.dev/open-match/internal/app/minimatch"
"open-match.dev/open-match/internal/rpc"
rpcTesting "open-match.dev/open-match/internal/rpc/testing"
statestoreTesting "open-match.dev/open-match/internal/statestore/testing"
"open-match.dev/open-match/internal/telemetry"
"open-match.dev/open-match/internal/testing/customize/evaluator"
internalMmf "open-match.dev/open-match/internal/testing/mmf"
"open-match.dev/open-match/internal/util"
mmfHarness "open-match.dev/open-match/pkg/harness/function/golang"
pb "open-match.dev/open-match/pkg/pb"
"open-match.dev/open-match/test/customize/matchfunction/mmf"
"open-match.dev/open-match/test/evaluator/evaluate"
)
@ -162,8 +163,8 @@ func createMatchFunctionForTest(t *testing.T, c *rpcTesting.TestContext) *rpcTes
cfg.Set("api.mmlogic.grpcport", c.GetGRPCPort())
cfg.Set("api.mmlogic.httpport", c.GetHTTPPort())
assert.Nil(t, mmfHarness.BindService(p, cfg, &mmfHarness.FunctionSettings{
Func: pool.MakeMatches,
assert.Nil(t, internalMmf.BindService(p, cfg, &internalMmf.FunctionSettings{
Func: mmf.MakeMatches,
}))
})
return tc

View File

@ -0,0 +1,64 @@
// Copyright 2019 Google LLC
//
// 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 mmf provides the Match Making Function service for Open Match golang harness.
package mmf
import (
"github.com/spf13/viper"
"google.golang.org/grpc"
"open-match.dev/open-match/internal/app"
"open-match.dev/open-match/internal/config"
"open-match.dev/open-match/internal/rpc"
"open-match.dev/open-match/pkg/pb"
)
// FunctionSettings is a collection of parameters used to customize matchfunction views.
type FunctionSettings struct {
Func MatchFunction
}
// RunMatchFunction is a hook for the main() method in the main executable.
func RunMatchFunction(settings *FunctionSettings) {
app.RunApplication("functions", getCfg, func(p *rpc.ServerParams, cfg config.View) error {
return BindService(p, cfg, settings)
})
}
// BindService creates the function service to the server Params.
func BindService(p *rpc.ServerParams, cfg config.View, fs *FunctionSettings) error {
service, err := newMatchFunctionService(cfg, fs)
if err != nil {
return err
}
p.AddHandleFunc(func(s *grpc.Server) {
pb.RegisterMatchFunctionServer(s, service)
}, pb.RegisterMatchFunctionHandlerFromEndpoint)
return nil
}
func getCfg() (config.View, error) {
cfg := viper.New()
cfg.Set("api.functions.hostname", "om-function")
cfg.Set("api.functions.grpcport", 50502)
cfg.Set("api.functions.httpport", 51502)
cfg.Set("api.mmlogic.hostname", "om-mmlogic")
cfg.Set("api.mmlogic.grpcport", 50503)
return cfg, nil
}

View File

@ -0,0 +1,153 @@
// Copyright 2019 Google LLC
//
// 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 mmf provides the Match Making Function service for Open Match golang harness.
package mmf
import (
"context"
"io"
"github.com/golang/protobuf/ptypes/any"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
"open-match.dev/open-match/internal/config"
"open-match.dev/open-match/internal/rpc"
"open-match.dev/open-match/pkg/pb"
)
var (
logger = logrus.WithFields(logrus.Fields{
"app": "openmatch",
"component": "matchfunction.harness.golang",
})
)
// MatchFunction is the function signature for the Match Making Function (MMF) to be implemented by the user.
// The harness will pass the Rosters and PlayerPool for the match profile to this
// function and it will return the Rosters to be populated in the proposal.
// Input:
// - MatchFunctionParams:
// A structure that defines the resources that are available to the match function.
// Developers can choose to add context to the structure such that match function has the ability
// to cancel a stream response/request or to limit match function by sharing a static and protected view.
type MatchFunction func(*MatchFunctionParams) ([]*pb.Match, error)
// matchFunctionService implements pb.MatchFunctionServer, the server generated
// by compiling the protobuf, by fulfilling the pb.MatchFunctionServer interface.
type matchFunctionService struct {
cfg config.View
function MatchFunction
mmlogicClient pb.MmLogicClient
}
// MatchFunctionParams is a protected view for the match function.
type MatchFunctionParams struct {
// Logger is used to generate error/debug logs
Logger *logrus.Entry
// 'Name' from the MatchProfile.
ProfileName string
// 'Extensions' from the MatchProfile.
Extensions map[string]*any.Any
// An array of Rosters. By convention, your input Roster contains players already in
// the match, and the names of pools to search when trying to fill an empty slot.
Rosters []*pb.Roster
// A map that contains mappings from pool name to a list of tickets that satisfied the filters in the pool
PoolNameToTickets map[string][]*pb.Ticket
}
// Run is this harness's implementation of the gRPC call defined in api/matchfunction.proto.
func (s *matchFunctionService) Run(req *pb.RunRequest, stream pb.MatchFunction_RunServer) error {
poolNameToTickets, err := s.getMatchManifest(stream.Context(), req)
if err != nil {
return err
}
// The matchfunction takes in some half-filled/empty rosters, a property bag, and a map[poolNames]tickets to generate match proposals
mfParams := &MatchFunctionParams{
Logger: logrus.WithFields(logrus.Fields{
"app": "openmatch",
"component": "matchfunction.implementation",
}),
ProfileName: req.GetProfile().GetName(),
Extensions: req.GetProfile().GetExtensions(),
Rosters: req.GetProfile().GetRosters(),
PoolNameToTickets: poolNameToTickets,
}
// Run the customize match function!
proposals, err := s.function(mfParams)
if err != nil {
return err
}
logger.WithFields(logrus.Fields{
"proposals": proposals,
}).Trace("proposals returned by match function")
for _, proposal := range proposals {
if err := stream.Send(&pb.RunResponse{Proposal: proposal}); err != nil {
return err
}
}
return nil
}
func newMatchFunctionService(cfg config.View, fs *FunctionSettings) (*matchFunctionService, error) {
conn, err := rpc.GRPCClientFromConfig(cfg, "api.mmlogic")
if err != nil {
logger.Errorf("Failed to get MMLogic connection, %v.", err)
return nil, err
}
mmfService := &matchFunctionService{cfg: cfg, function: fs.Func, mmlogicClient: pb.NewMmLogicClient(conn)}
return mmfService, nil
}
// getMatchManifest fetches all the data needed from the mmlogic API.
func (s *matchFunctionService) getMatchManifest(ctx context.Context, req *pb.RunRequest) (map[string][]*pb.Ticket, error) {
poolNameToTickets := make(map[string][]*pb.Ticket)
filterPools := req.GetProfile().GetPools()
for _, pool := range filterPools {
qtClient, err := s.mmlogicClient.QueryTickets(ctx, &pb.QueryTicketsRequest{Pool: pool}, grpc.WaitForReady(true))
if err != nil {
logger.WithError(err).Error("Failed to get queryTicketClient from mmlogic.")
return nil, err
}
// Aggregate tickets by poolName
poolTickets := make([]*pb.Ticket, 0)
for {
qtResponse, err := qtClient.Recv()
if err == io.EOF {
logger.Trace("Received all results from the queryTicketClient.")
// Break when all results are received
break
}
if err != nil {
logger.WithError(err).Error("Failed to receive a response from the queryTicketClient.")
return nil, err
}
poolTickets = append(poolTickets, qtResponse.Tickets...)
}
poolNameToTickets[pool.GetName()] = poolTickets
}
return poolNameToTickets, nil
}

View File

@ -14,11 +14,11 @@
FROM open-match-base-build as builder
WORKDIR /go/src/open-match.dev/open-match/examples/functions/golang/pool
WORKDIR /go/src/open-match.dev/open-match/test/customize/matchfunction
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o matchfunction .
FROM gcr.io/distroless/static:nonroot
WORKDIR /app/
COPY --from=builder --chown=nonroot /go/src/open-match.dev/open-match/examples/functions/golang/pool/matchfunction /app/
COPY --from=builder --chown=nonroot /go/src/open-match.dev/open-match/test/customize/matchfunction/matchfunction /app/
ENTRYPOINT ["/app/matchfunction"]

View File

@ -20,8 +20,8 @@
package main
import (
pool "open-match.dev/open-match/examples/functions/golang/pool/mmf"
mmfHarness "open-match.dev/open-match/pkg/harness/function/golang"
internalMmf "open-match.dev/open-match/internal/testing/mmf"
"open-match.dev/open-match/test/customize/matchfunction/mmf"
)
func main() {
@ -29,7 +29,7 @@ func main() {
// match function. The harness itself queries open match for player pools for
// the specified request and passes the pools to the match function to generate
// proposals.
mmfHarness.RunMatchFunction(&mmfHarness.FunctionSettings{
Func: pool.MakeMatches,
internalMmf.RunMatchFunction(&internalMmf.FunctionSettings{
Func: mmf.MakeMatches,
})
}

View File

@ -24,7 +24,7 @@ import (
"github.com/golang/protobuf/ptypes/any"
"github.com/pkg/errors"
"github.com/rs/xid"
mmfHarness "open-match.dev/open-match/pkg/harness/function/golang"
internalMmf "open-match.dev/open-match/internal/testing/mmf"
"open-match.dev/open-match/pkg/pb"
)
@ -37,7 +37,7 @@ var (
// The goal of this function is to generate predictable matches that can be validated without flakyness.
// This match function loops through all the pools and generates one match per pool aggregating all players
// in that pool in the generated match.
func MakeMatches(params *mmfHarness.MatchFunctionParams) ([]*pb.Match, error) {
func MakeMatches(params *internalMmf.MatchFunctionParams) ([]*pb.Match, error) {
var result []*pb.Match
for pool, tickets := range params.PoolNameToTickets {
if len(tickets) != 0 {

View File

@ -23,7 +23,7 @@ import (
"github.com/golang/protobuf/ptypes/any"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
mmfHarness "open-match.dev/open-match/pkg/harness/function/golang"
internalMmf "open-match.dev/open-match/internal/testing/mmf"
)
func TestMakeMatches(t *testing.T) {
@ -72,7 +72,7 @@ func TestMakeMatches(t *testing.T) {
"pool2": tickets[2:],
}
p := &mmfHarness.MatchFunctionParams{
p := &internalMmf.MatchFunctionParams{
Logger: &logrus.Entry{},
ProfileName: "test-profile",
Rosters: []*pb.Roster{},