129 lines
3.2 KiB
Go
129 lines
3.2 KiB
Go
package output
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/snyk/driftctl/pkg/analyser"
|
|
"github.com/snyk/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.ResourceType(), res.ResourceId()),
|
|
Type: res.ResourceType(),
|
|
Name: res.ResourceId(),
|
|
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.ResourceType(), res.ResourceId()),
|
|
Type: res.ResourceType(),
|
|
Name: res.ResourceId(),
|
|
Change: change{
|
|
Actions: []string{action},
|
|
After: *res.Attributes(),
|
|
},
|
|
}
|
|
if action == "no-op" {
|
|
r.Change.Before = *res.Attributes()
|
|
}
|
|
ret = append(ret, r)
|
|
|
|
}
|
|
return ret
|
|
}
|