diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000..bf44d00e8 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,13 @@ +# Golang CircleCI 2.0 configuration file +# +# Check https://circleci.com/docs/2.0/language-go/ for more details +version: 2 +jobs: + build: + docker: + - image: circleci/golang:1.8.1 + + working_directory: /go/src/github.com/replicatedcom/support-bundle + steps: + - checkout + - run: make test diff --git a/plugins/core/core.go b/plugins/core/core.go index 5b6bdd686..22188a00d 100644 --- a/plugins/core/core.go +++ b/plugins/core/core.go @@ -7,8 +7,10 @@ import ( func New() types.Plugin { return map[string]types.Planner{ - "loadavg": planners.PlanLoadAverage, - "hostname": planners.Hostname, - "uptime": planners.Uptime, + "loadavg": planners.PlanLoadAverage, + "hostname": planners.Hostname, + "uptime": planners.Uptime, + "read-file": planners.ReadFile, + "read-command": planners.ReadCommand, } } diff --git a/plugins/core/planners/read-command.go b/plugins/core/planners/read-command.go new file mode 100644 index 000000000..41a882a92 --- /dev/null +++ b/plugins/core/planners/read-command.go @@ -0,0 +1,27 @@ +package planners + +import ( + "errors" + + "github.com/replicatedcom/support-bundle/plans" + "github.com/replicatedcom/support-bundle/plugins/core/producers" + "github.com/replicatedcom/support-bundle/types" +) + +func ReadCommand(spec types.Spec) []types.Task { + if spec.Config.Command == "" { + err := errors.New("spec requires a command within config") + task := plans.PreparedError(err, spec) + + return []types.Task{task} + } + + task := &plans.ByteSource{ + Producer: producers.ReadCommand(spec.Config.Command, spec.Config.Args...), + RawPath: spec.Raw, + JSONPath: spec.JSON, + HumanPath: spec.Human, + } + + return []types.Task{task} +} diff --git a/plugins/core/planners/read-file.go b/plugins/core/planners/read-file.go new file mode 100644 index 000000000..7f5d5ac32 --- /dev/null +++ b/plugins/core/planners/read-file.go @@ -0,0 +1,27 @@ +package planners + +import ( + "errors" + + "github.com/replicatedcom/support-bundle/plans" + "github.com/replicatedcom/support-bundle/plugins/core/producers" + "github.com/replicatedcom/support-bundle/types" +) + +func ReadFile(spec types.Spec) []types.Task { + if spec.Config.FilePath == "" { + err := errors.New("spec requires a filename within config") + task := plans.PreparedError(err, spec) + + return []types.Task{task} + } + + task := &plans.ByteSource{ + Producer: producers.ReadFile(spec.Config.FilePath), + RawPath: spec.Raw, + JSONPath: spec.JSON, + HumanPath: spec.Human, + } + + return []types.Task{task} +} diff --git a/plugins/docker/docker.go b/plugins/docker/docker.go index 98cec575c..c3140c826 100644 --- a/plugins/docker/docker.go +++ b/plugins/docker/docker.go @@ -15,7 +15,10 @@ func New() (types.Plugin, error) { producers := producers.New(c) docker := planners.New(producers) return map[string]types.Planner{ - "daemon": docker.Daemon, - "logs": docker.Logs, + "daemon": docker.Daemon, + "logs": docker.Logs, + "inspect": docker.Inspect, + "read-file": docker.ReadFile, + "run-command": docker.RunCommand, }, nil } diff --git a/plugins/docker/planners/inspect.go b/plugins/docker/planners/inspect.go new file mode 100644 index 000000000..0865f18dd --- /dev/null +++ b/plugins/docker/planners/inspect.go @@ -0,0 +1,26 @@ +package planners + +import ( + "errors" + + "github.com/replicatedcom/support-bundle/plans" + "github.com/replicatedcom/support-bundle/types" +) + +func (d *Docker) Inspect(spec types.Spec) []types.Task { + if spec.Config.ContainerID == "" { + err := errors.New("spec requires a container id") + task := plans.PreparedError(err, spec) + + return []types.Task{task} + } + + task := &plans.StructuredSource{ + Producer: d.producers.Inspect(spec.Config.ContainerID), + RawPath: spec.Raw, + JSONPath: spec.JSON, + HumanPath: spec.Human, + } + + return []types.Task{task} +} diff --git a/plugins/docker/planners/logs.go b/plugins/docker/planners/logs.go index 86da48fd9..1a5a508e2 100644 --- a/plugins/docker/planners/logs.go +++ b/plugins/docker/planners/logs.go @@ -7,37 +7,16 @@ import ( "github.com/replicatedcom/support-bundle/types" ) -func parseContainerConfig(src interface{}) types.ContainerConfig { - config := types.ContainerConfig{} - - m, ok := src.(map[interface{}]interface{}) - if !ok { - return config - } - for k, v := range m { - if key, ok := k.(string); ok { - switch key { - case "container_id": - if val, ok := v.(string); ok { - config.ContainerID = val - } - } - } - } - return config -} - func (d *Docker) Logs(spec types.Spec) []types.Task { - config := parseContainerConfig(spec.Config) - if config.ContainerID == "" { - err := errors.New("spec requires container config") + if spec.Config.ContainerID == "" { + err := errors.New("spec requires a container id") task := plans.PreparedError(err, spec) return []types.Task{task} } task := &plans.StreamSource{ - Producer: d.producers.Logs(config.ContainerID), + Producer: d.producers.Logs(spec.Config.ContainerID), RawPath: spec.Raw, JSONPath: spec.JSON, HumanPath: spec.Human, diff --git a/plugins/docker/planners/read-file.go b/plugins/docker/planners/read-file.go new file mode 100644 index 000000000..56bd6ed0c --- /dev/null +++ b/plugins/docker/planners/read-file.go @@ -0,0 +1,26 @@ +package planners + +import ( + "errors" + + "github.com/replicatedcom/support-bundle/plans" + "github.com/replicatedcom/support-bundle/types" +) + +func (d *Docker) ReadFile(spec types.Spec) []types.Task { + if spec.Config.ContainerID == "" || spec.Config.FilePath == "" { + err := errors.New("spec requires a container ID and filename within config") + task := plans.PreparedError(err, spec) + + return []types.Task{task} + } + + task := &plans.StreamSource{ + Producer: d.producers.ReadFile(spec.Config.ContainerID, spec.Config.FilePath), + RawPath: spec.Raw, + JSONPath: spec.JSON, + HumanPath: spec.Human, + } + + return []types.Task{task} +} diff --git a/plugins/docker/planners/run-command.go b/plugins/docker/planners/run-command.go new file mode 100644 index 000000000..968804a92 --- /dev/null +++ b/plugins/docker/planners/run-command.go @@ -0,0 +1,31 @@ +package planners + +import ( + "errors" + + "github.com/replicatedcom/support-bundle/plans" + "github.com/replicatedcom/support-bundle/types" +) + +func (d *Docker) RunCommand(spec types.Spec) []types.Task { + fullCommand := append([]string{spec.Config.Command}, spec.Config.Args...) + + if spec.Config.ContainerID == "" || len(fullCommand) == 0 || spec.Config.Command == "" { + err := errors.New("spec requires a container ID and command within config") + task := plans.PreparedError(err, spec) + + return []types.Task{task} + } + + // task := &plans.StreamsSource{ + // Producer: d.producers.RunCommand(spec.Config.ContainerID, fullCommand), + // RawPath: spec.Raw, + // JSONPath: spec.JSON, + // HumanPath: spec.Human, + // } + + err := errors.New("This task type not yet implemented") + task := plans.PreparedError(err, spec) + + return []types.Task{task} +} diff --git a/spec/parse_test.go b/spec/parse_test.go index 3d2d80822..0be744470 100644 --- a/spec/parse_test.go +++ b/spec/parse_test.go @@ -34,11 +34,13 @@ specs: Human: "/human/metrics/loadavg", }) - require.Contains(t, specs, types.Spec{ + logsSpec := types.Spec{ Builtin: "docker.logs", Raw: "/raw/containers/testExample/logs.txt", - Config: map[interface{}]interface{}{"container_id": "testExample"}, - }) + } + logsSpec.Config.ContainerID = "testExample" + + require.Contains(t, specs, logsSpec) require.Contains(t, specs, types.Spec{ Builtin: "docker.daemon", diff --git a/types/spec.go b/types/spec.go index 800a947c9..104111973 100644 --- a/types/spec.go +++ b/types/spec.go @@ -10,9 +10,10 @@ type Spec struct { Human string // Plan-specific config - Config interface{} -} - -type ContainerConfig struct { - ContainerID string `json="container_id"` + Config struct { + FilePath string `yaml:"file_path"` + Args []string `yaml:"args"` + ContainerID string `yaml:"container_id"` + Command string `yaml:"command"` + } }