Skip to content

Commit

Permalink
Merge branch 'v3' into PMM-13129-encryption
Browse files Browse the repository at this point in the history
  • Loading branch information
JiriCtvrtka authored Sep 9, 2024
2 parents 5929939 + 11cba1b commit 182b3d7
Show file tree
Hide file tree
Showing 173 changed files with 6,122 additions and 3,791 deletions.
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
* @percona/pmm-review-be
/docs/ @percona/pmm-docs
/ui/ @percona/pmm-review-fe
/agent/agents/postgres/ @JiriCtvrtka @percona/pmm-review-be
/api/ @BupycHuk @percona/pmm-review-be
/managed/services/checks/ @idoqo @percona/pmm-review-be
Expand Down
31 changes: 18 additions & 13 deletions .github/workflows/doc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,31 @@ on:
- "docs/api/**"

workflow_dispatch:
inputs:
version:
description: "API Version on readme.io"
required: true
default: "v1.0" # v0.0 stands for the dev version

jobs:
sync:
name: Sync
runs-on: ubuntu-22.04

steps:
- name: Check out code
uses: actions/checkout@v4

- name: API
uses: readmeio/rdme@v8
with:
rdme: openapi ./api/swagger/swagger.json --id=626badcabbc59c02acc1a53f --key=${{ secrets.README_TOKEN }}
- name: Detect PMM version and API ID
run: |
# For reference:
# PMM 2: VERSION=v2, ID=626badcabbc59c02acc1a53f
# PMM 3: VERSION=v3, ID=622892a957a7410330bc6184
export VERSION=$(cat api/swagger/swagger.json | jq -r '.info.version')
export ID=$(cat api/swagger/swagger.json | jq -r '."x-readme-id"')
echo "VERSION=$VERSION" >> $GITHUB_ENV
echo "ID=$ID" >> $GITHUB_ENV
- name: Provision rdme
run: npm install -g rdme

- name: Sync API spec
run: rdme openapi ./api/swagger/swagger.json --id=${{ env.ID }} --key=${{ secrets.README_TOKEN }}

- name: Markdown docs
uses: readmeio/rdme@v8
with:
rdme: docs docs/api --version=${{ github.event.inputs.version || 'v1.0' }} --key=${{ secrets.README_TOKEN }}
- name: Sync Markdown docs
run: rdme docs docs/api --version=${{ env.VERSION }} --key=${{ secrets.README_TOKEN }}
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ RUN export GOPATH=$(go env GOPATH) && \
COPY . $GOPATH/src/github.com/percona/pmm/
WORKDIR $GOPATH/src/github.com/percona/pmm/api-tests/

CMD make init run-race
CMD ["make", "init", "run-race"]

4 changes: 1 addition & 3 deletions admin/commands/management/management.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@
// Package management provides management commands.
package management

import (
"github.com/percona/pmm/api/inventory/v1/types"
)
import "github.com/percona/pmm/api/inventory/v1/types"

var (
allNodeTypes = map[string]string{
Expand Down
170 changes: 158 additions & 12 deletions agent/runner/actions/mongodb_explain_action.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import (
"context"
"fmt"
"path/filepath"
"strings"
"time"

"github.com/percona/percona-toolkit/src/go/mongolib/proto"
"github.com/pkg/errors"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
Expand All @@ -39,6 +39,15 @@ type mongodbExplainAction struct {
dsn string
}

type explain struct {
Ns string `json:"ns"`
Op string `json:"op"`
Query bson.D `json:"query,omitempty"`
Command bson.D `json:"command,omitempty"`
OriginatingCommand bson.D `json:"originatingCommand,omitempty"`
UpdateObj bson.D `json:"updateobj,omitempty"`
}

var errCannotExplain = fmt.Errorf("cannot explain this type of query")

// NewMongoDBExplainAction creates a MongoDB EXPLAIN query Action.
Expand Down Expand Up @@ -89,28 +98,165 @@ func (a *mongodbExplainAction) Run(ctx context.Context) ([]byte, error) {
}
defer client.Disconnect(ctx) //nolint:errcheck

var eq proto.ExampleQuery
return explainForQuery(ctx, client, a.params.Query)
}

func (a *mongodbExplainAction) sealed() {}

func (e explain) prepareCommand() (bson.D, error) {
command := e.Command

switch e.Op {
case "query":
if len(command) == 0 {
command = e.Query
}

if len(command) == 0 || command[0].Key != "find" {
return bson.D{
{Key: "find", Value: e.getCollection()},
{Key: "filter", Value: command},
}, nil
}

if len(command) != 0 && command[0].Key == "query" {
return bson.D{
{Key: "find", Value: e.getCollection()},
{Key: "filter", Value: command[0].Value},
}, nil
}

return dropDBField(command), nil
case "update":
if len(command) == 0 {
command = bson.D{
{Key: "q", Value: e.Query},
{Key: "u", Value: e.UpdateObj},
}
}

return bson.D{
{Key: "update", Value: e.getCollection()},
{Key: "updates", Value: []any{command}},
}, nil
case "remove":
if len(command) == 0 {
command = bson.D{{Key: "q", Value: e.Query}}
}

return bson.D{
{Key: "delete", Value: e.getCollection()},
{Key: "deletes", Value: []any{command}},
}, nil
case "getmore":
if len(e.OriginatingCommand) == 0 {
return bson.D{{Key: "getmore", Value: ""}}, nil
}

command = e.OriginatingCommand

return dropDBField(command), nil
case "command":
command = dropDBField(command)

if len(command) == 0 {
return command, nil
}

switch command[0].Key {
// Not supported commands.
case "dbStats":
return nil, errors.Errorf("command %s is not supported for explain", command[0].Key)
case "group":
default:
return command, nil
}

return fixReduceField(command), nil
// Not supported operations.
case "insert", "drop":
return nil, errors.Errorf("operation %s is not supported for explain", e.Op)
}

return command, nil
}

func (e explain) getDB() string {
s := strings.SplitN(e.Ns, ".", 2)
if len(s) == 2 {
return s[0]
}

return ""
}

func (e explain) getCollection() string {
s := strings.SplitN(e.Ns, ".", 2)
if len(s) == 2 {
return s[1]
}

return ""
}

// dropDBField remove DB field to be able run explain on all supported types.
// Otherwise it could end up with BSON field 'xxx.$db' is a duplicate field.
func dropDBField(command bson.D) bson.D {
for i := range command {
if command[i].Key != "$db" {
continue
}

if len(command)-1 == i {
return command[:i]
}

return append(command[:i], command[i+1:]...)
}

return command
}

// fixReduceField fixing nil/empty values after unmarshalling funcs.
func fixReduceField(command bson.D) bson.D {
var group bson.D
var ok bool
if group, ok = command[0].Value.(bson.D); !ok {
return command
}

err = bson.UnmarshalExtJSON([]byte(a.params.Query), true, &eq)
for i := range group {
if group[i].Key == "$reduce" {
group[i].Value = "{}"
command[0].Value = group
break
}
}

return command
}

func explainForQuery(ctx context.Context, client *mongo.Client, query string) ([]byte, error) {
var e explain
err := bson.UnmarshalExtJSON([]byte(query), false, &e)
if err != nil {
return nil, errors.Wrapf(err, "Query: %s", a.params.Query)
return nil, errors.Wrapf(err, "Query: %s", query)
}

database := "admin"
if eq.Db() != "" {
database = eq.Db()
preparedCommand, err := e.prepareCommand()
if err != nil {
return nil, errors.Wrap(errCannotExplain, err.Error())
}
res := client.Database(database).RunCommand(ctx, eq.ExplainCmd())
command := bson.D{{Key: "explain", Value: preparedCommand}}
res := client.Database(e.getDB()).RunCommand(ctx, command)
if res.Err() != nil {
return nil, errors.Wrap(errCannotExplain, res.Err().Error())
}

result, err := res.DecodeBytes()
result, err := res.Raw()
if err != nil {
return nil, err
}
// We need it because result

return []byte(result.String()), nil
}

func (a *mongodbExplainAction) sealed() {}
Loading

0 comments on commit 182b3d7

Please sign in to comment.