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>
Этот коммит содержится в:
@@ -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)
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user