Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 14 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,25 @@ test:
go test -v ./pkg/...

integration-test:
ginkgo -v -r -p --skip="docker container" tests/ginkgo
ginkgo -v -r -p --skip="docker container|retraced.events" tests/ginkgo

integration-test-docker:
docker pull ubuntu:latest
ginkgo -v -r -p --focus="docker container" tests/ginkgo

# this task assumes a working retraced installation, and requires the following params to be set:
#
# RETRACED_API_ENDPOINT
# RETRACED_PROJECT_ID
# RETRACED_API_KEY
#
# Can also optionally set
#
# RETRACED_INSECURE_SKIP_VERIFY=1
#
integration-test-retraced:
ginkgo -v -r -p --focus="retraced.events" tests/ginkgo

build:
mkdir -p bin
go build -o ./bin/support-bundle .
Expand Down
2 changes: 1 addition & 1 deletion cmd/support-bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package cmd
import (
"os"

homedir "github.com/mitchellh/go-homedir"
"github.com/mitchellh/go-homedir"
"github.com/replicatedcom/support-bundle/pkg/cli"
"github.com/replicatedcom/support-bundle/pkg/cli/commands"
"github.com/spf13/cobra"
Expand Down
7 changes: 5 additions & 2 deletions pkg/cli/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/replicatedcom/support-bundle/pkg/spec"
"github.com/replicatedcom/support-bundle/pkg/types"
jww "github.com/spf13/jwalterweatherman"
"github.com/replicatedcom/support-bundle/pkg/plugins/retraced"
)

func (cli *Cli) Generate(cfgFiles []string, cfgDocs []string, bundlePath string, skipDefault bool, timeoutSeconds int) error {
Expand Down Expand Up @@ -52,10 +53,12 @@ func (cli *Cli) Generate(cfgFiles []string, cfgDocs []string, bundlePath string,
if err != nil {
return errors.Wrap(err, "Failed to initialize docker plugin")
}

planner := bundle.Planner{
Plugins: map[string]types.Plugin{
"core": core.New(),
"docker": d,
"core": core.New(),
"docker": d,
"retraced": retraced.New(),
},
}

Expand Down
31 changes: 31 additions & 0 deletions pkg/plugins/retraced/planners/events.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package planners

import (
"github.com/pkg/errors"
"github.com/replicatedcom/support-bundle/pkg/plans"
"github.com/replicatedcom/support-bundle/pkg/plugins/retraced/producers"
"github.com/replicatedcom/support-bundle/pkg/types"
)

func Events(spec types.Spec) []types.Task {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about doing a nil check here on spec.RetracedEventsCommand and then passing as value to producers.Events?
https://github.com/replicatedcom/support-bundle/blob/master/pkg/plugins/docker/planners/run-command.go#L11

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like it 👍

var err error
if spec.RetracedEventsCommand == nil {
err = errors.New("spec for retraced.events required")
} else if spec.RetracedEventsCommand.APIEndpoint == "" { // require an endpoint, I don't think defaulting to SaaS Retraced makes sense
err = errors.New("retraced.events spec missing api_endpoint")
} else if spec.RetracedEventsCommand.APIToken == "" {
err = errors.New("retraced.events spec missing api_token")
} else if spec.RetracedEventsCommand.ProjectID == "" {
err = errors.New("retraced.events spec missing project_id")
}
if err != nil {
task := plans.PreparedError(err, spec)
return []types.Task{task}
}
return []types.Task{
&plans.StreamSource{
Producer: producers.Events(spec),
RawPath: spec.Raw,
},
}
}
12 changes: 12 additions & 0 deletions pkg/plugins/retraced/plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package retraced

import (
"github.com/replicatedcom/support-bundle/pkg/plugins/retraced/planners"
"github.com/replicatedcom/support-bundle/pkg/types"
)

func New() types.Plugin {
return map[string]types.Planner{
"events": planners.Events,
}
}
88 changes: 88 additions & 0 deletions pkg/plugins/retraced/producers/events.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package producers

import (
"context"
"crypto/tls"
"io"
"net/http"

"github.com/pkg/errors"
"github.com/replicatedcom/support-bundle/pkg/types"
"github.com/retracedhq/retraced-go"
"github.com/spf13/jwalterweatherman"
)

var (
defaultMask = &retraced.EventNodeMask{
Action: true,
CRUD: true,
CanonicalTime: true,
}
defaultQuery = &retraced.StructuredQuery{
CRUD: "c,u,d",
}
)

func Events(spec types.Spec) types.StreamProducer {
return func(ctx context.Context) (io.Reader, error) {

client, err := getClient(spec)
if err != nil {
return nil, errors.Wrap(err, "create retraced client")
}

query := getQuery(spec)
mask := getMask(spec)
reader, writer := io.Pipe()

go func() {
var err error
defer func() {
writer.CloseWithError(err)
}()
err = client.ExportCSV(ctx, writer, query, mask)
if err != nil {
jwalterweatherman.ERROR.Printf("Failed to collect retraced events %v", err)
}
}()

return reader, nil
}
}
func getClient(spec types.Spec) (*retraced.Client, error) {
client, err := retraced.NewClient(spec.RetracedEventsCommand.ProjectID, spec.RetracedEventsCommand.APIToken)
if err != nil {
return nil, err
}

if spec.RetracedEventsCommand.Insecure {
client.HttpClient = &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}
}
return client, nil
}

func getQuery(spec types.Spec) *retraced.StructuredQuery {
var query *retraced.StructuredQuery
if spec.RetracedEventsCommand.Query != nil {
query = spec.RetracedEventsCommand.Query
} else {
query = defaultQuery
}
return query
}

func getMask(spec types.Spec) *retraced.EventNodeMask {
var mask *retraced.EventNodeMask
if spec.RetracedEventsCommand.Mask != nil {
mask = spec.RetracedEventsCommand.Mask
} else {
mask = defaultMask
}
return mask
}
11 changes: 11 additions & 0 deletions pkg/types/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package types

import (
dockertypes "github.com/docker/docker/api/types"
"github.com/retracedhq/retraced-go"
)

type Doc struct {
Expand All @@ -26,6 +27,7 @@ type Spec struct {
DockerContainerLogs *DockerContainerLogsOptions `json:"docker.container-logs,omitempty"`
DockerContainerInspect *DockerContainerInspectOptions `json:"docker.container-inspect,omitempty"`
HTTPRequestCommand *HTTPRequestCommandOptions `json:"core.http-request,omitempty"`
RetracedEventsCommand *RetracedEventsOptions `json:"retraced.events,omitempty"`
}

type Config struct {
Expand Down Expand Up @@ -60,6 +62,15 @@ type HTTPRequestCommandOptions struct {
Insecure bool `json:"insecure,omitempty"`
}

type RetracedEventsOptions struct {
APIEndpoint string `json:"api_endpoint"`
ProjectID string `json:"project_id,omitempty"`
APIToken string `json:"api_token,omitempty"`
Insecure bool `json:"insecure,omitempty"`
Mask *retraced.EventNodeMask `json:"mask,omitempty"`
Query *retraced.StructuredQuery `json:"query,omitempty"`
}

type Scrub struct {
Regex string `json:"regex"`
Replace string `json:"replace"`
Expand Down
File renamed without changes.
88 changes: 88 additions & 0 deletions tests/ginkgo/retraced_audit_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package ginkgo

import (
"fmt"
"os"

"strings"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/replicatedcom/support-bundle/pkg/types"
)

var _ = Describe("The retraced.events spec", func() {
opts := types.RetracedEventsOptions{
APIEndpoint: os.Getenv("RETRACED_API_ENDPOINT"),
APIToken: os.Getenv("RETRACED_API_TOKEN"),
ProjectID: os.Getenv("RETRACED_PROJECT_ID"),
Insecure: os.Getenv("RETRACED_INSECURE_SKIP_VERIFY") != "",
}

BeforeEach(EnterNewTempDir)
BeforeEach(func() {
Expect(opts.APIEndpoint).NotTo(BeEmpty(), "RETRACED_API_ENDPOINT must be set")
Expect(opts.APIToken).NotTo(BeEmpty(), "RETRACED_API_TOKEN must be set")
Expect(opts.ProjectID).NotTo(BeEmpty(), "RETRACED_PROJECT_ID must be set")
})
AfterEach(CleanupDir)

It("Collects events from retraced", func() {
WriteBundleConfig(`
specs:
- builtin: retraced.events
raw: /audit/events.txt
retraced.events:
api_endpoint: ` + opts.APIEndpoint + `
api_token: ` + opts.APIToken + `
project_id: ` + opts.ProjectID + `
insecure: ` + fmt.Sprintf("%v", opts.Insecure))

GenerateBundle()

Expect(err).NotTo(HaveOccurred())
errors := GetFileFromBundle("error.json")
Expect(errors).To(Equal("null"))

contents := GetFileFromBundle("audit/events.txt")
Expect(contents).ToNot(BeEmpty())

header := strings.Split(contents, "\n")[0]
Expect(header).To(Equal("action,crud,canonical_time"))

})

It("Allows for a custom mask+query", func() {
WriteBundleConfig(`
specs:
- builtin: retraced.events
raw: /audit/events.txt
retraced.events:
api_endpoint: ` + opts.APIEndpoint + `
api_token: ` + opts.APIToken + `
project_id: ` + opts.ProjectID + `
insecure: ` + fmt.Sprintf("%v", opts.Insecure) + `
mask:
CRUD: true
query:
CRUD: r`)

GenerateBundle()

Expect(err).NotTo(HaveOccurred())
errors := GetFileFromBundle("error.json")
Expect(errors).To(Equal("null"))

contents := GetFileFromBundle("audit/events.txt")
Expect(contents).ToNot(BeEmpty())

lines := strings.Split(contents, "\n")
Expect(lines[0]).To(Equal("crud"))
for _, line := range lines[1:] {
if line != "" {
Expect(line).To(Equal("r"))
}
}

})
})
7 changes: 7 additions & 0 deletions vendor/github.com/retracedhq/retraced-go/README.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions vendor/github.com/retracedhq/retraced-go/actor.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading