commit
226e1e716a
|
@ -111,7 +111,7 @@ func TestDriftctlCmd_Scan(t *testing.T) {
|
||||||
env: map[string]string{
|
env: map[string]string{
|
||||||
"DCTL_OUTPUT": "test",
|
"DCTL_OUTPUT": "test",
|
||||||
},
|
},
|
||||||
err: fmt.Errorf("Unable to parse output flag 'test': \nAccepted formats are: console://,html://PATH/TO/FILE.html,json://PATH/TO/FILE.json"),
|
err: fmt.Errorf("Unable to parse output flag 'test': \nAccepted formats are: console://,html://PATH/TO/FILE.html,json://PATH/TO/FILE.json,plan://PATH/TO/FILE.json"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
env: map[string]string{
|
env: map[string]string{
|
||||||
|
|
|
@ -387,6 +387,20 @@ func parseOutputFlag(out string) (*output.OutputConfig, error) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
options["path"] = opts[0]
|
options["path"] = opts[0]
|
||||||
|
case output.PlanOutputType:
|
||||||
|
if len(opts) != 1 || opts[0] == "" {
|
||||||
|
return nil, errors.Wrapf(
|
||||||
|
cmderrors.NewUsageError(
|
||||||
|
fmt.Sprintf(
|
||||||
|
"\nMust be of kind: %s",
|
||||||
|
output.Example(output.PlanOutputType),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
"Invalid plan output '%s'",
|
||||||
|
out,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
options["path"] = opts[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
return &output.OutputConfig{
|
return &output.OutputConfig{
|
||||||
|
|
|
@ -15,12 +15,14 @@ var supportedOutputTypes = []string{
|
||||||
ConsoleOutputType,
|
ConsoleOutputType,
|
||||||
JSONOutputType,
|
JSONOutputType,
|
||||||
HTMLOutputType,
|
HTMLOutputType,
|
||||||
|
PlanOutputType,
|
||||||
}
|
}
|
||||||
|
|
||||||
var supportedOutputExample = map[string]string{
|
var supportedOutputExample = map[string]string{
|
||||||
ConsoleOutputType: ConsoleOutputExample,
|
ConsoleOutputType: ConsoleOutputExample,
|
||||||
JSONOutputType: JSONOutputExample,
|
JSONOutputType: JSONOutputExample,
|
||||||
HTMLOutputType: HTMLOutputExample,
|
HTMLOutputType: HTMLOutputExample,
|
||||||
|
PlanOutputType: PlanOutputExample,
|
||||||
}
|
}
|
||||||
|
|
||||||
func SupportedOutputs() []string {
|
func SupportedOutputs() []string {
|
||||||
|
@ -57,6 +59,8 @@ func GetOutput(config OutputConfig, quiet bool) Output {
|
||||||
return NewJSON(config.Options["path"])
|
return NewJSON(config.Options["path"])
|
||||||
case HTMLOutputType:
|
case HTMLOutputType:
|
||||||
return NewHTML(config.Options["path"])
|
return NewHTML(config.Options["path"])
|
||||||
|
case PlanOutputType:
|
||||||
|
return NewPlan(config.Options["path"])
|
||||||
case ConsoleOutputType:
|
case ConsoleOutputType:
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
|
@ -75,6 +79,11 @@ func GetPrinter(config OutputConfig, quiet bool) output.Printer {
|
||||||
return &output.VoidPrinter{}
|
return &output.VoidPrinter{}
|
||||||
}
|
}
|
||||||
fallthrough
|
fallthrough
|
||||||
|
case PlanOutputType:
|
||||||
|
if isStdOut(config.Options["path"]) {
|
||||||
|
return &output.VoidPrinter{}
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
case ConsoleOutputType:
|
case ConsoleOutputType:
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -324,6 +324,43 @@ func fakeAnalysisWithGithubEnumerationError() *analyser.Analysis {
|
||||||
return &a
|
return &a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fakeAnalysisForJSONPlan() *analyser.Analysis {
|
||||||
|
a := analyser.Analysis{}
|
||||||
|
a.AddUnmanaged(
|
||||||
|
&resource.AbstractResource{
|
||||||
|
Id: "unmanaged-id-1",
|
||||||
|
Type: "aws_unmanaged_resource",
|
||||||
|
Attrs: &resource.Attributes{
|
||||||
|
"name": "First unmanaged resource",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&resource.AbstractResource{
|
||||||
|
Id: "unmanaged-id-2",
|
||||||
|
Type: "aws_unmanaged_resource",
|
||||||
|
Attrs: &resource.Attributes{
|
||||||
|
"name": "Second unmanaged resource",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
a.AddManaged(
|
||||||
|
&resource.AbstractResource{
|
||||||
|
Id: "managed-id-1",
|
||||||
|
Type: "aws_managed_resource",
|
||||||
|
Attrs: &resource.Attributes{
|
||||||
|
"name": "First managed resource",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&resource.AbstractResource{
|
||||||
|
Id: "managed-id-2",
|
||||||
|
Type: "aws_managed_resource",
|
||||||
|
Attrs: &resource.Attributes{
|
||||||
|
"name": "Second managed resource",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetPrinter(t *testing.T) {
|
func TestGetPrinter(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -370,6 +407,24 @@ func TestGetPrinter(t *testing.T) {
|
||||||
key: ConsoleOutputType,
|
key: ConsoleOutputType,
|
||||||
want: &output.VoidPrinter{},
|
want: &output.VoidPrinter{},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "jsonplan file output",
|
||||||
|
path: "/path/to/file",
|
||||||
|
key: PlanOutputType,
|
||||||
|
want: output.NewConsolePrinter(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "jsonplan stdout output",
|
||||||
|
path: "stdout",
|
||||||
|
key: PlanOutputType,
|
||||||
|
want: &output.VoidPrinter{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "jsonplan /dev/stdout output",
|
||||||
|
path: "/dev/stdout",
|
||||||
|
key: PlanOutputType,
|
||||||
|
want: &output.VoidPrinter{},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
package output
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/cloudskiff/driftctl/pkg/analyser"
|
||||||
|
"github.com/cloudskiff/driftctl/pkg/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
const FormatVersion = "0.1"
|
||||||
|
const PlanOutputType = "plan"
|
||||||
|
const PlanOutputExample = "plan://PATH/TO/FILE.json"
|
||||||
|
|
||||||
|
type plan struct {
|
||||||
|
FormatVersion string `json:"format_version,omitempty"`
|
||||||
|
PlannedValues plannedValues `json:"planned_values,omitempty"`
|
||||||
|
ResourceChanges []rscChange `json:"resource_changes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type plannedValues struct {
|
||||||
|
RootModule module `json:"root_module,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type rscChange struct {
|
||||||
|
Address string `json:"address,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Change change `json:"change,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type change struct {
|
||||||
|
Actions []string `json:"actions,omitempty"`
|
||||||
|
Before map[string]interface{} `json:"before,omitempty"`
|
||||||
|
After map[string]interface{} `json:"after,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type module struct {
|
||||||
|
Resources []rsc `json:"resources,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type rsc struct {
|
||||||
|
Address string `json:"address,omitempty"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
AttributeValues map[string]interface{} `json:"values,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Plan struct {
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPlan(path string) *Plan {
|
||||||
|
return &Plan{path}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Plan) Write(analysis *analyser.Analysis) error {
|
||||||
|
file := os.Stdout
|
||||||
|
if !isStdOut(c.path) {
|
||||||
|
f, err := os.OpenFile(c.path, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
file = f
|
||||||
|
}
|
||||||
|
output := plan{FormatVersion: FormatVersion}
|
||||||
|
output.PlannedValues.RootModule = addPlannedValues(analysis)
|
||||||
|
output.ResourceChanges = addResourceChanges(analysis)
|
||||||
|
jsonPlan, err := json.MarshalIndent(output, "", "\t")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := file.Write(jsonPlan); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addPlannedValues(analysis *analyser.Analysis) module {
|
||||||
|
managedRsc := listRsc(analysis.Managed())
|
||||||
|
unmanagedRsc := listRsc(analysis.Unmanaged())
|
||||||
|
return module{
|
||||||
|
Resources: append(managedRsc, unmanagedRsc...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func listRsc(resources []resource.Resource) []rsc {
|
||||||
|
var ret []rsc
|
||||||
|
for _, res := range resources {
|
||||||
|
r := rsc{
|
||||||
|
Address: fmt.Sprintf("%s.%s", res.TerraformType(), res.TerraformId()),
|
||||||
|
Type: res.TerraformType(),
|
||||||
|
Name: res.TerraformId(),
|
||||||
|
AttributeValues: *res.Attributes(),
|
||||||
|
}
|
||||||
|
ret = append(ret, r)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func addResourceChanges(analysis *analyser.Analysis) []rscChange {
|
||||||
|
managedRsc := listRscChange(analysis.Managed(), "no-op")
|
||||||
|
unmanagedRsc := listRscChange(analysis.Unmanaged(), "create")
|
||||||
|
return append(managedRsc, unmanagedRsc...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listRscChange(resources []resource.Resource, action string) []rscChange {
|
||||||
|
var ret []rscChange
|
||||||
|
for _, res := range resources {
|
||||||
|
r := rscChange{
|
||||||
|
Address: fmt.Sprintf("%s.%s", res.TerraformType(), res.TerraformId()),
|
||||||
|
Type: res.TerraformType(),
|
||||||
|
Name: res.TerraformId(),
|
||||||
|
Change: change{
|
||||||
|
Actions: []string{action},
|
||||||
|
After: *res.Attributes(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if action == "no-op" {
|
||||||
|
r.Change.Before = *res.Attributes()
|
||||||
|
}
|
||||||
|
ret = append(ret, r)
|
||||||
|
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
package output
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/cloudskiff/driftctl/pkg/analyser"
|
||||||
|
"github.com/cloudskiff/driftctl/test/goldenfile"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPlan_Write(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
goldenfile string
|
||||||
|
analysis *analyser.Analysis
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "test jsonplan output",
|
||||||
|
goldenfile: "output_plan.json",
|
||||||
|
analysis: fakeAnalysisForJSONPlan(),
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test jsonplan output when no infra",
|
||||||
|
goldenfile: "output_plan_empty.json",
|
||||||
|
analysis: &analyser.Analysis{},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
tempDir := t.TempDir()
|
||||||
|
tempFile, err := ioutil.TempFile(tempDir, "result")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
c := NewPlan(tempFile.Name())
|
||||||
|
if err := c.Write(tt.analysis); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("Write() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
result, err := ioutil.ReadFile(tempFile.Name())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
expectedFilePath := path.Join("./testdata/", tt.goldenfile)
|
||||||
|
if *goldenfile.Update == tt.goldenfile {
|
||||||
|
if err := ioutil.WriteFile(expectedFilePath, result, 0600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expected, err := ioutil.ReadFile(expectedFilePath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, string(expected), string(result))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPlan_Write_stdout(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
path string
|
||||||
|
goldenfile string
|
||||||
|
analysis *analyser.Analysis
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "test jsonplan output on stdout",
|
||||||
|
goldenfile: "output_plan.json",
|
||||||
|
path: "stdout",
|
||||||
|
analysis: fakeAnalysisForJSONPlan(),
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "test jsonplan output on /dev/stdout",
|
||||||
|
goldenfile: "output_plan.json",
|
||||||
|
path: "/dev/stdout",
|
||||||
|
analysis: fakeAnalysisForJSONPlan(),
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
stdout := os.Stdout // keep backup of the real stdout
|
||||||
|
r, w, _ := os.Pipe()
|
||||||
|
os.Stdout = w
|
||||||
|
|
||||||
|
c := NewPlan(tt.path)
|
||||||
|
if err := c.Write(tt.analysis); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("Write() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
outC := make(chan []byte)
|
||||||
|
// copy the output in a separate goroutine so printing can't block indefinitely
|
||||||
|
go func() {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
_, _ = io.Copy(&buf, r)
|
||||||
|
outC <- buf.Bytes()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// back to normal state
|
||||||
|
w.Close()
|
||||||
|
os.Stdout = stdout // restoring the real stdout
|
||||||
|
result := <-outC
|
||||||
|
|
||||||
|
expectedFilePath := path.Join("./testdata/", tt.goldenfile)
|
||||||
|
if *goldenfile.Update == tt.goldenfile {
|
||||||
|
if err := ioutil.WriteFile(expectedFilePath, result, 0600); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expected, err := ioutil.ReadFile(expectedFilePath)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, string(expected), string(result))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
{
|
||||||
|
"format_version": "0.1",
|
||||||
|
"planned_values": {
|
||||||
|
"root_module": {
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"address": "aws_managed_resource.managed-id-1",
|
||||||
|
"type": "aws_managed_resource",
|
||||||
|
"name": "managed-id-1",
|
||||||
|
"values": {
|
||||||
|
"name": "First managed resource"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "aws_managed_resource.managed-id-2",
|
||||||
|
"type": "aws_managed_resource",
|
||||||
|
"name": "managed-id-2",
|
||||||
|
"values": {
|
||||||
|
"name": "Second managed resource"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "aws_unmanaged_resource.unmanaged-id-1",
|
||||||
|
"type": "aws_unmanaged_resource",
|
||||||
|
"name": "unmanaged-id-1",
|
||||||
|
"values": {
|
||||||
|
"name": "First unmanaged resource"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "aws_unmanaged_resource.unmanaged-id-2",
|
||||||
|
"type": "aws_unmanaged_resource",
|
||||||
|
"name": "unmanaged-id-2",
|
||||||
|
"values": {
|
||||||
|
"name": "Second unmanaged resource"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resource_changes": [
|
||||||
|
{
|
||||||
|
"address": "aws_managed_resource.managed-id-1",
|
||||||
|
"type": "aws_managed_resource",
|
||||||
|
"name": "managed-id-1",
|
||||||
|
"change": {
|
||||||
|
"actions": [
|
||||||
|
"no-op"
|
||||||
|
],
|
||||||
|
"before": {
|
||||||
|
"name": "First managed resource"
|
||||||
|
},
|
||||||
|
"after": {
|
||||||
|
"name": "First managed resource"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "aws_managed_resource.managed-id-2",
|
||||||
|
"type": "aws_managed_resource",
|
||||||
|
"name": "managed-id-2",
|
||||||
|
"change": {
|
||||||
|
"actions": [
|
||||||
|
"no-op"
|
||||||
|
],
|
||||||
|
"before": {
|
||||||
|
"name": "Second managed resource"
|
||||||
|
},
|
||||||
|
"after": {
|
||||||
|
"name": "Second managed resource"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "aws_unmanaged_resource.unmanaged-id-1",
|
||||||
|
"type": "aws_unmanaged_resource",
|
||||||
|
"name": "unmanaged-id-1",
|
||||||
|
"change": {
|
||||||
|
"actions": [
|
||||||
|
"create"
|
||||||
|
],
|
||||||
|
"after": {
|
||||||
|
"name": "First unmanaged resource"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"address": "aws_unmanaged_resource.unmanaged-id-2",
|
||||||
|
"type": "aws_unmanaged_resource",
|
||||||
|
"name": "unmanaged-id-2",
|
||||||
|
"change": {
|
||||||
|
"actions": [
|
||||||
|
"create"
|
||||||
|
],
|
||||||
|
"after": {
|
||||||
|
"name": "Second unmanaged resource"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"format_version": "0.1",
|
||||||
|
"planned_values": {
|
||||||
|
"root_module": {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -177,7 +177,7 @@ func Test_parseOutputFlag(t *testing.T) {
|
||||||
out: "",
|
out: "",
|
||||||
},
|
},
|
||||||
want: nil,
|
want: nil,
|
||||||
err: fmt.Errorf("Unable to parse output flag '': \nAccepted formats are: console://,html://PATH/TO/FILE.html,json://PATH/TO/FILE.json"),
|
err: fmt.Errorf("Unable to parse output flag '': \nAccepted formats are: console://,html://PATH/TO/FILE.html,json://PATH/TO/FILE.json,plan://PATH/TO/FILE.json"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "test invalid",
|
name: "test invalid",
|
||||||
|
@ -185,7 +185,7 @@ func Test_parseOutputFlag(t *testing.T) {
|
||||||
out: "sdgjsdgjsdg",
|
out: "sdgjsdgjsdg",
|
||||||
},
|
},
|
||||||
want: nil,
|
want: nil,
|
||||||
err: fmt.Errorf("Unable to parse output flag 'sdgjsdgjsdg': \nAccepted formats are: console://,html://PATH/TO/FILE.html,json://PATH/TO/FILE.json"),
|
err: fmt.Errorf("Unable to parse output flag 'sdgjsdgjsdg': \nAccepted formats are: console://,html://PATH/TO/FILE.html,json://PATH/TO/FILE.json,plan://PATH/TO/FILE.json"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "test invalid",
|
name: "test invalid",
|
||||||
|
@ -193,7 +193,7 @@ func Test_parseOutputFlag(t *testing.T) {
|
||||||
out: "://",
|
out: "://",
|
||||||
},
|
},
|
||||||
want: nil,
|
want: nil,
|
||||||
err: fmt.Errorf("Unable to parse output flag '://': \nAccepted formats are: console://,html://PATH/TO/FILE.html,json://PATH/TO/FILE.json"),
|
err: fmt.Errorf("Unable to parse output flag '://': \nAccepted formats are: console://,html://PATH/TO/FILE.html,json://PATH/TO/FILE.json,plan://PATH/TO/FILE.json"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "test unsupported",
|
name: "test unsupported",
|
||||||
|
@ -201,7 +201,7 @@ func Test_parseOutputFlag(t *testing.T) {
|
||||||
out: "foobar://",
|
out: "foobar://",
|
||||||
},
|
},
|
||||||
want: nil,
|
want: nil,
|
||||||
err: fmt.Errorf("Unsupported output 'foobar': \nValid formats are: console://,html://PATH/TO/FILE.html,json://PATH/TO/FILE.json"),
|
err: fmt.Errorf("Unsupported output 'foobar': \nValid formats are: console://,html://PATH/TO/FILE.html,json://PATH/TO/FILE.json,plan://PATH/TO/FILE.json"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "test empty json",
|
name: "test empty json",
|
||||||
|
@ -235,6 +235,27 @@ func Test_parseOutputFlag(t *testing.T) {
|
||||||
},
|
},
|
||||||
err: nil,
|
err: nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "test empty jsonplan",
|
||||||
|
args: args{
|
||||||
|
out: "plan://",
|
||||||
|
},
|
||||||
|
want: nil,
|
||||||
|
err: fmt.Errorf("Invalid plan output 'plan://': \nMust be of kind: plan://PATH/TO/FILE.json"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test valid jsonplan",
|
||||||
|
args: args{
|
||||||
|
out: "plan:///tmp/foobar.json",
|
||||||
|
},
|
||||||
|
want: &output.OutputConfig{
|
||||||
|
Key: "plan",
|
||||||
|
Options: map[string]string{
|
||||||
|
"path": "/tmp/foobar.json",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
err: nil,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|
Loading…
Reference in New Issue