Skip to content

Commit

Permalink
refator: unify diff util functions, delete similar code
Browse files Browse the repository at this point in the history
  • Loading branch information
howieyuen committed Jul 12, 2022
1 parent 182d162 commit 40306cf
Show file tree
Hide file tree
Showing 10 changed files with 238 additions and 271 deletions.
67 changes: 7 additions & 60 deletions pkg/engine/operation/diff.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,14 @@
package operation

import (
"bytes"
"fmt"
"strings"

"kusionstack.io/kusion/pkg/engine/operation/utils"

opsmodels "kusionstack.io/kusion/pkg/engine/operation/models"

"github.com/gonvenience/wrap"
"github.com/pkg/errors"
yamlv3 "gopkg.in/yaml.v3"

"kusionstack.io/kusion/pkg/engine/models"
opsmodels "kusionstack.io/kusion/pkg/engine/operation/models"
"kusionstack.io/kusion/pkg/engine/states"
"kusionstack.io/kusion/pkg/kusionctl/cmd/diff"
"kusionstack.io/kusion/pkg/log"
"kusionstack.io/kusion/pkg/util"
"kusionstack.io/kusion/pkg/util/diff"
jsonutil "kusionstack.io/kusion/pkg/util/json"
"kusionstack.io/kusion/third_party/dyff"
)
Expand Down Expand Up @@ -65,61 +56,17 @@ func (d *Diff) Diff(request *DiffRequest) (string, error) {

func DiffWithRequestResourceAndState(plan *models.Spec, latest *states.State) (string, error) {
planString := jsonutil.MustMarshal2String(plan.Resources)
var report *dyff.Report
var err error
if latest == nil {
return DiffReport("", planString, diff.OutputHuman)
report, err = diff.ToReport("", planString)
} else {
latestResources := latest.Resources
priorString := jsonutil.MustMarshal2String(latestResources)
return DiffReport(priorString, planString, diff.OutputHuman)
}
}

func DiffReport(prior, plan, mode string) (string, error) {
from, err := utils.LoadFile(prior, "Last State")
if err != nil {
return "", err
}
to, err := utils.LoadFile(plan, "Request State")
if err != nil {
return "", err
report, err = diff.ToReport(priorString, priorString)
}

report, err := dyff.CompareInputFiles(from, to, dyff.IgnoreOrderChanges(true))
if err != nil {
return "", wrap.Errorf(err, "failed to compare input files")
}
return buildReport(mode, report)
}

func buildReport(mode string, report dyff.Report) (string, error) {
switch strings.ToLower(mode) {
case diff.OutputHuman:
return writeReport(report)
case diff.OutputRaw:
// output stdout/file
reportMap := map[string]interface{}{
"diffs": report.Diffs,
}
reportYAML, err := yamlv3.Marshal(reportMap)
if err != nil {
return "", wrap.Errorf(err, "failed to marshal report diffs")
}
return string(reportYAML), nil
default:
return "", fmt.Errorf("invalid output style `%s`", mode)
}
}

// WriteReport writes a human-readable report to the provided writer
func writeReport(report dyff.Report) (string, error) {
reportWriter := &dyff.HumanReport{
Report: report,
MinorChangeThreshold: 0.1,
}

buffer := new(bytes.Buffer)
if err := reportWriter.WriteReport(buffer); err != nil {
return "", err
}
return buffer.String(), nil
return diff.ToHumanString(diff.NewHumanReport(report))
}
2 changes: 1 addition & 1 deletion pkg/engine/operation/models/change.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func (cs *ChangeStep) Diff() (string, error) {
return "", err
}

reportString, err := diff.ToReportString(*diffReport)
reportString, err := diff.ToHumanString(diff.NewHumanReport(diffReport))
if err != nil {
log.Warn("diff to string error: %v", err)
return "", err
Expand Down
27 changes: 0 additions & 27 deletions pkg/engine/operation/utils/file.go

This file was deleted.

9 changes: 2 additions & 7 deletions pkg/kusionctl/cmd/diff/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,12 @@ import (
"k8s.io/kubectl/pkg/util/templates"
"sigs.k8s.io/yaml"

diffutil "kusionstack.io/kusion/pkg/util/diff"
"kusionstack.io/kusion/pkg/util/i18n"
"kusionstack.io/kusion/third_party/diff"
"kusionstack.io/kusion/third_party/dyff"
)

// Supported output option values
const (
OutputHuman = "human"
OutputRaw = "raw"
)

var (
diffShort = "Compare differences between input files <from> and <to>"

Expand Down Expand Up @@ -78,7 +73,7 @@ func NewCmdDiff() *cobra.Command {
cmd.Flags().StringVar(&o.diffMode, "diff-mode", "normal",
i18n.T(fmt.Sprintf("Diff mode. One of %s and %s. The default is normal", DiffModeNormal, DiffModeIgnoreAdded)))
cmd.Flags().StringVarP(&o.outStyle, "output", "o", "human",
i18n.T(fmt.Sprintf("Specify the output style. One of %s and %s. The default is human", OutputHuman, OutputRaw)))
i18n.T(fmt.Sprintf("Specify the output style. One of %s and %s. The default is human", diffutil.OutputHuman, diffutil.OutputRaw)))
cmd.Flags().BoolVarP(&o.ignoreOrderChanges, "ignore-order-changes", "i", false,
i18n.T("Ignore order changes in lists. The default is false"))
cmd.Flags().BoolVarP(&o.omitHeader, "omit-header", "b", false,
Expand Down
5 changes: 3 additions & 2 deletions pkg/kusionctl/cmd/diff/diff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/assert"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

diffutil "kusionstack.io/kusion/pkg/util/diff"
"kusionstack.io/kusion/third_party/diff"
)

Expand Down Expand Up @@ -84,7 +85,7 @@ func TestNewCmdDiff(t *testing.T) {
t.Run("diff by files with flags", func(t *testing.T) {
cmd := NewCmdDiff()
assert.Nil(t, cmd.Flags().Set("diff-mode", DiffModeIgnoreAdded))
assert.Nil(t, cmd.Flags().Set("output", OutputRaw))
assert.Nil(t, cmd.Flags().Set("output", diffutil.OutputRaw))
assert.Nil(t, cmd.Flags().Set("sort-by-kubernetes-resource", "true"))
assert.Nil(t, cmd.Flags().Set("swap", "true"))
cmd.SetArgs([]string{"testdata/pod1.yaml", "testdata/pod2.yaml"})
Expand All @@ -106,7 +107,7 @@ func TestNewCmdDiff(t *testing.T) {

cmd := NewCmdDiff()
assert.Nil(t, cmd.Flags().Set("diff-mode", DiffModeIgnoreAdded))
assert.Nil(t, cmd.Flags().Set("output", OutputRaw))
assert.Nil(t, cmd.Flags().Set("output", diffutil.OutputRaw))
assert.Nil(t, cmd.Flags().Set("sort-by-kubernetes-resource", "true"))
assert.Nil(t, cmd.Flags().Set("swap", "true"))
cmd.SetArgs([]string{"testdata/pod-full.yaml"})
Expand Down
43 changes: 11 additions & 32 deletions pkg/kusionctl/cmd/diff/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package diff

import (
"fmt"
"os"
"sort"
"strings"

"github.com/gonvenience/wrap"
"github.com/gonvenience/ytbx"
yamlv3 "gopkg.in/yaml.v3"

diffutil "kusionstack.io/kusion/pkg/util/diff"
"kusionstack.io/kusion/third_party/dyff"
)

Expand Down Expand Up @@ -79,8 +80,8 @@ func (o *DiffOptions) Validate() error {
}

switch strings.ToLower(o.outStyle) {
case OutputHuman:
case OutputRaw:
case diffutil.OutputHuman:
case diffutil.OutputRaw:
break
default:
return fmt.Errorf("invalid output style `%s`", o.outStyle)
Expand Down Expand Up @@ -166,41 +167,19 @@ func (o *DiffOptions) Run() error {
}

// handle output option
switch strings.ToLower(o.outStyle) {
case OutputHuman:
return o.writeReport(report)
case OutputRaw:
// output stdout/file
reportMap := map[string]interface{}{
"diffs": report.Diffs,
}

reportYAML, err := yamlv3.Marshal(reportMap)
if err != nil {
return wrap.Errorf(err, "failed to marshal report diffs")
}

fmt.Println(string(reportYAML))

return nil
}

return nil
}

func (o *DiffOptions) writeReport(report dyff.Report) error {
reportWriter := &dyff.HumanReport{
Report: report,
DoNotInspectCerts: o.doNotInspectCerts,
humanReport := &dyff.HumanReport{
NoTableStyle: o.noTableStyle,
DoNotInspectCerts: o.doNotInspectCerts,
OmitHeader: o.omitHeader,
UseGoPatchPaths: o.useGoPatchPaths,
MinorChangeThreshold: 0.1,
Report: report,
}

if err := reportWriter.WriteReport(os.Stdout); err != nil {
return wrap.Errorf(err, "failed to print report")
reportString, err := diffutil.ToReportString(humanReport, o.outStyle)
if err != nil {
return err
}
fmt.Println(reportString)

return nil
}
103 changes: 103 additions & 0 deletions pkg/util/diff/diff.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package diff

import (
"bytes"
"fmt"

"github.com/gonvenience/wrap"
"github.com/gonvenience/ytbx"
yamlv3 "gopkg.in/yaml.v3"

"kusionstack.io/kusion/pkg/util/yaml"
"kusionstack.io/kusion/third_party/dyff"
)

// Supported output option values
const (
OutputHuman = "human"
OutputRaw = "raw"
)

// NewHumanReport return a default *dyff.HumanReport with head omitted
func NewHumanReport(report *dyff.Report) *dyff.HumanReport {
return &dyff.HumanReport{
NoTableStyle: false,
DoNotInspectCerts: false,
OmitHeader: true,
UseGoPatchPaths: false,
MinorChangeThreshold: 0.1,
Report: *report,
}
}

// ToReportString return a report string base on mode, valid mode: "human" and "raw"
func ToReportString(humanReport *dyff.HumanReport, mode string) (string, error) {
switch mode {
case OutputHuman:
return ToHumanString(humanReport)
case OutputRaw:
return ToRawString(humanReport)
default:
return "", fmt.Errorf("invalid output style `%s`", mode)
}
}

func ToHumanString(humanReport *dyff.HumanReport) (string, error) {
buf := bytes.NewBuffer([]byte{})
err := humanReport.WriteReport(buf)
if err != nil {
return "", err
}
return buf.String(), nil
}

func ToRawString(humanReport *dyff.HumanReport) (string, error) {
reportMap := map[string]interface{}{
"diffs": humanReport.Diffs,
}
reportYAML, err := yamlv3.Marshal(reportMap)
if err != nil {
return "", wrap.Errorf(err, "failed to marshal report diffs")
}
return string(reportYAML), nil
}

// ToReport compares objects, oldData and newData,
// and returns a report with the list of differences.
func ToReport(oldData, newData interface{}) (*dyff.Report, error) {
from, err := LoadFile(yaml.MergeToOneYAML(oldData), "Old item")
if err != nil {
return nil, err
}

to, err := LoadFile(yaml.MergeToOneYAML(newData), "New item")
if err != nil {
return nil, err
}

report, err := dyff.CompareInputFiles(from, to, dyff.IgnoreOrderChanges(true))
if err != nil {
return nil, err
}
return &report, nil
}

// LoadFile reads the provided input data slice as a YAML, JSON, or TOML
// file with potential multiple documents.
func LoadFile(input, location string) (ytbx.InputFile, error) {
var (
documents []*yamlv3.Node
data []byte
err error
)

data = []byte(input)
if documents, err = ytbx.LoadDocuments(data); err != nil {
return ytbx.InputFile{}, wrap.Errorf(err, "unable to parse data %v", data)
}

return ytbx.InputFile{
Location: location,
Documents: documents,
}, nil
}
Loading

0 comments on commit 40306cf

Please sign in to comment.