MM-21626, MM-21627 - Plugin API/Hooks Prometheus instrumentati… (#13825)

* MM-21626,MM-21627 - Plugin API/Hooks Prometheus instrumentation

* Updated einterface mocks

* Fixed supervisor tests

* ignoring golint errors for plugin metrics wrappers

* Making golangci happy

* Using variadic form when generating wrapper code

* Removed artificial delay

* Removed comments from tests

* Renaming plugin wrappers to api/hooks_timer_layer

* updating vendor dir and mod files

* Recording plugin api/hook responses in prometheus

* Updated einterfaces-mocks

* Updating go sum

* Updating go sum

* Fixing conflicts

* More conflicts fixing

Co-authored-by: mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
Ali Farooq
2020-02-14 15:47:43 -05:00
коммит произвёл GitHub
родитель 7da85922af
Коммит e4fb5791b0
49 изменённых файлов: 17005 добавлений и 68 удалений

Просмотреть файл

@@ -12,13 +12,13 @@ import (
"go/token"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"text/template"
"github.com/pkg/errors"
"golang.org/x/tools/imports"
)
type IHookEntry struct {
@@ -55,14 +55,18 @@ func FieldListToFuncList(fieldList *ast.FieldList, fileset *token.FileSet) strin
return "(" + strings.Join(result, ", ") + ")"
}
func FieldListToNames(fieldList *ast.FieldList, fileset *token.FileSet) string {
func FieldListToNames(fieldList *ast.FieldList, fileset *token.FileSet, variadicForm bool) string {
result := []string{}
if fieldList == nil || len(fieldList.List) == 0 {
return ""
}
for _, field := range fieldList.List {
for _, name := range field.Names {
result = append(result, name.Name)
paramName := name.Name
if _, ok := field.Type.(*ast.Ellipsis); ok && variadicForm {
paramName = fmt.Sprintf("%s...", paramName)
}
result = append(result, paramName)
}
}
@@ -314,6 +318,90 @@ func (s *apiRPCServer) {{.Name}}(args *{{.Name | obscure}}Args, returns *{{.Name
{{end}}
`
var apiTimerLayerTemplate = `// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// Code generated by "make pluginapi"
// DO NOT EDIT
package plugin
import (
"io"
"net/http"
timePkg "time"
"github.com/mattermost/mattermost-server/v5/einterfaces"
"github.com/mattermost/mattermost-server/v5/model"
)
type apiTimerLayer struct {
pluginID string
apiImpl API
metrics einterfaces.MetricsInterface
}
func (api *apiTimerLayer) recordTime(startTime timePkg.Time, name string, success bool) {
if api.metrics != nil {
elapsedTime := float64(timePkg.Since(startTime)) / float64(timePkg.Second)
api.metrics.ObservePluginApiDuration(api.pluginID, name, success, elapsedTime)
}
}
{{range .APIMethods}}
func (api *apiTimerLayer) {{.Name}}{{funcStyle .Params}} {{funcStyle .Return}} {
startTime := timePkg.Now()
{{ if .Return }} {{destruct "_returns" .Return}} := {{ end }} api.apiImpl.{{.Name}}({{valuesOnly .Params}})
api.recordTime(startTime, "{{.Name}}", true)
{{ if .Return }} return {{destruct "_returns" .Return}} {{ end -}}
}
{{end}}
`
var hooksTimerLayerTemplate = `// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// Code generated by "make pluginapi"
// DO NOT EDIT
package plugin
import (
"io"
"net/http"
timePkg "time"
"github.com/mattermost/mattermost-server/v5/einterfaces"
"github.com/mattermost/mattermost-server/v5/model"
)
type hooksTimerLayer struct {
pluginID string
hooksImpl Hooks
metrics einterfaces.MetricsInterface
}
func (hooks *hooksTimerLayer) recordTime(startTime timePkg.Time, name string, success bool) {
if hooks.metrics != nil {
elapsedTime := float64(timePkg.Since(startTime)) / float64(timePkg.Second)
hooks.metrics.ObservePluginHookDuration(hooks.pluginID, name, success, elapsedTime)
}
}
{{range .HooksMethods}}
func (hooks *hooksTimerLayer) {{.Name}}{{funcStyle .Params}} {{funcStyle .Return}} {
startTime := timePkg.Now()
{{ if .Return }} {{destruct "_returns" .Return}} := {{ end }} hooks.hooksImpl.{{.Name}}({{valuesOnly .Params}})
hooks.recordTime(startTime, "{{.Name}}", true)
{{ if .Return }} return {{destruct "_returns" .Return}} {{end -}}
}
{{end}}
`
type MethodParams struct {
Name string
Params *ast.FieldList
@@ -325,11 +413,11 @@ type HooksTemplateParams struct {
APIMethods []MethodParams
}
func generateGlue(info *PluginInterfaceInfo) {
func generateHooksGlue(info *PluginInterfaceInfo) {
templateFunctions := map[string]interface{}{
"funcStyle": func(fields *ast.FieldList) string { return FieldListToFuncList(fields, info.FileSet) },
"structStyle": func(fields *ast.FieldList) string { return FieldListToStructList(fields, info.FileSet) },
"valuesOnly": func(fields *ast.FieldList) string { return FieldListToNames(fields, info.FileSet) },
"valuesOnly": func(fields *ast.FieldList) string { return FieldListToNames(fields, info.FileSet, false) },
"encodeErrors": func(structPrefix string, fields *ast.FieldList) string {
return FieldListToEncodedErrors(structPrefix, fields, info.FileSet)
},
@@ -364,20 +452,68 @@ func generateGlue(info *PluginInterfaceInfo) {
templateResult := &bytes.Buffer{}
hooksTemplate.Execute(templateResult, &templateParams)
importsBuffer := &bytes.Buffer{}
cmd := exec.Command("goimports")
cmd.Stdin = templateResult
cmd.Stdout = importsBuffer
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
formatted, err := imports.Process("", templateResult.Bytes(), nil)
if err != nil {
panic(err)
}
if err := ioutil.WriteFile(filepath.Join(getPluginPackageDir(), "client_rpc_generated.go"), importsBuffer.Bytes(), 0664); err != nil {
if err := ioutil.WriteFile(filepath.Join(getPluginPackageDir(), "client_rpc_generated.go"), formatted, 0664); err != nil {
panic(err)
}
}
func generatePluginTimerLayer(info *PluginInterfaceInfo) {
templateFunctions := map[string]interface{}{
"funcStyle": func(fields *ast.FieldList) string { return FieldListToFuncList(fields, info.FileSet) },
"structStyle": func(fields *ast.FieldList) string { return FieldListToStructList(fields, info.FileSet) },
"valuesOnly": func(fields *ast.FieldList) string { return FieldListToNames(fields, info.FileSet, true) },
"destruct": func(structPrefix string, fields *ast.FieldList) string {
return FieldListDestruct(structPrefix, fields, info.FileSet)
},
}
// Prepare template params
templateParams := HooksTemplateParams{}
for _, hook := range info.Hooks {
templateParams.HooksMethods = append(templateParams.HooksMethods, MethodParams{
Name: hook.FuncName,
Params: hook.Args,
Return: hook.Results,
})
}
for _, api := range info.API {
templateParams.APIMethods = append(templateParams.APIMethods, MethodParams{
Name: api.FuncName,
Params: api.Args,
Return: api.Results,
})
}
pluginTemplates := map[string]string{
"api_timer_layer_generated.go": apiTimerLayerTemplate,
"hooks_timer_layer_generated.go": hooksTimerLayerTemplate,
}
for fileName, presetTemplate := range pluginTemplates {
parsedTemplate, err := template.New("hooks").Funcs(templateFunctions).Parse(presetTemplate)
if err != nil {
panic(err)
}
templateResult := &bytes.Buffer{}
parsedTemplate.Execute(templateResult, &templateParams)
formatted, err := imports.Process("", templateResult.Bytes(), nil)
if err != nil {
panic(err)
}
if err := ioutil.WriteFile(filepath.Join(getPluginPackageDir(), fileName), formatted, 0664); err != nil {
panic(err)
}
}
}
func getPluginPackageDir() string {
dirs, err := goList("github.com/mattermost/mattermost-server/v5/plugin")
if err != nil {
@@ -435,13 +571,18 @@ func removeExcluded(info *PluginInterfaceInfo) *PluginInterfaceInfo {
func main() {
pluginPackageDir := getPluginPackageDir()
log.Println("Generating plugin glue")
info, err := getPluginInfo(pluginPackageDir)
log.Println("Generating plugin hooks glue")
forRPC, err := getPluginInfo(pluginPackageDir)
if err != nil {
fmt.Println("Unable to get plugin info: " + err.Error())
}
generateHooksGlue(removeExcluded(forRPC))
info = removeExcluded(info)
generateGlue(info)
// Generate plugin timer layers
log.Println("Generating plugin timer glue")
forPlugins, err := getPluginInfo(pluginPackageDir)
if err != nil {
fmt.Println("Unable to get plugin info: " + err.Error())
}
generatePluginTimerLayer(forPlugins)
}