Skip to content

Commit

Permalink
manifest diff: add resource rename flag (istio#304)
Browse files Browse the repository at this point in the history
* manifest diff: add resource rename flag to be able to diff a name-changed resource.

* Add tests to cover wildcard cases. Update log messages and comments to reduce confusion.
  • Loading branch information
elfinhe authored and istio-testing committed Sep 30, 2019
1 parent a1c1bdb commit 643be5b
Show file tree
Hide file tree
Showing 4 changed files with 299 additions and 18 deletions.
24 changes: 18 additions & 6 deletions cmd/mesh/manifest-diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ type manifestDiffArgs struct {
selectResources string
// ignoreResources ignores all listed items during comparison. It uses the same list format as selectResources.
ignoreResources string
// renameResources identifies renamed resources before comparison.
// The format of each renaming pair is A->B, all renaming pairs are comma separated.
// e.g. Service:*:istio-pilot->Service:*:istio-control - rename istio-pilot service into istio-control
renameResources string
}

func addManifestDiffFlags(cmd *cobra.Command, diffArgs *manifestDiffArgs) {
Expand All @@ -54,6 +58,10 @@ func addManifestDiffFlags(cmd *cobra.Command, diffArgs *manifestDiffArgs) {
" Service:*:istio-pilot - compare Services called \"istio-pilot\" in all namespaces")
cmd.PersistentFlags().StringVar(&diffArgs.ignoreResources, "ignore", "",
"ignoreResources ignores all listed items during comparison. It uses the same list format as selectResources")
cmd.PersistentFlags().StringVar(&diffArgs.renameResources, "rename", "",
"renameResources identifies renamed resources before comparison.\n"+
"The format of each renaming pair is A->B, all renaming pairs are comma separated.\n"+
"e.g. Service:*:istio-pilot->Service:*:istio-control - rename istio-pilot service into istio-control")
}

func manifestDiffCmd(rootArgs *rootArgs, diffArgs *manifestDiffArgs) *cobra.Command {
Expand All @@ -70,16 +78,19 @@ func manifestDiffCmd(rootArgs *rootArgs, diffArgs *manifestDiffArgs) *cobra.Comm
Run: func(cmd *cobra.Command, args []string) {
l := newLogger(rootArgs.logToStdErr, cmd.OutOrStdout(), cmd.OutOrStderr())
if diffArgs.compareDir {
compareManifestsFromDirs(rootArgs, args[0], args[1], diffArgs.selectResources, diffArgs.ignoreResources)
compareManifestsFromDirs(rootArgs, args[0], args[1], diffArgs.renameResources,
diffArgs.selectResources, diffArgs.ignoreResources)
} else {
compareManifestsFromFiles(rootArgs, args, diffArgs.selectResources, diffArgs.ignoreResources, l)
compareManifestsFromFiles(rootArgs, args, diffArgs.renameResources,
diffArgs.selectResources, diffArgs.ignoreResources, l)
}
}}
return cmd
}

//compareManifestsFromFiles compares two manifest files
func compareManifestsFromFiles(rootArgs *rootArgs, args []string, selectResources, ignoreResources string, l *logger) {
func compareManifestsFromFiles(rootArgs *rootArgs, args []string,
renameResources, selectResources, ignoreResources string, l *logger) {
initLogsOrExit(rootArgs)

a, err := ioutil.ReadFile(args[0])
Expand All @@ -91,7 +102,7 @@ func compareManifestsFromFiles(rootArgs *rootArgs, args []string, selectResource
l.logAndFatal(fmt.Sprintf("Could not read %q: %v\n", args[1], err.Error()))
}

diff, err := object.ManifestDiffWithSelectAndIgnore(string(a), string(b), selectResources,
diff, err := object.ManifestDiffWithRenameSelectIgnore(string(a), string(b), renameResources, selectResources,
ignoreResources, rootArgs.verbose)
if err != nil {
log.Error(err.Error())
Expand All @@ -110,7 +121,8 @@ func yamlFileFilter(path string) bool {
}

//compareManifestsFromDirs compares manifests from two directories
func compareManifestsFromDirs(rootArgs *rootArgs, dirName1, dirName2, selectResources, ignoreResources string) {
func compareManifestsFromDirs(rootArgs *rootArgs, dirName1, dirName2,
renameResources, selectResources, ignoreResources string) {
initLogsOrExit(rootArgs)

mf1, err := util.ReadFilesWithFilter(dirName1, yamlFileFilter)
Expand All @@ -124,7 +136,7 @@ func compareManifestsFromDirs(rootArgs *rootArgs, dirName1, dirName2, selectReso
os.Exit(1)
}

diff, err := object.ManifestDiffWithSelectAndIgnore(mf1, mf2, selectResources,
diff, err := object.ManifestDiffWithRenameSelectIgnore(mf1, mf2, renameResources, selectResources,
ignoreResources, rootArgs.verbose)
if err != nil {
log.Error(err.Error())
Expand Down
3 changes: 2 additions & 1 deletion cmd/mesh/manifest-generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@ func runTestGroup(t *testing.T, tests testGroup) {
}

for _, v := range []bool{true, false} {
diff, err := object.ManifestDiffWithSelectAndIgnore(got, want, diffSelect, tt.diffIgnore, v)
diff, err := object.ManifestDiffWithRenameSelectIgnore(got, want,
"", diffSelect, tt.diffIgnore, v)
if err != nil {
t.Fatal(err)
}
Expand Down
94 changes: 85 additions & 9 deletions pkg/object/objects.go
Original file line number Diff line number Diff line change
Expand Up @@ -385,28 +385,89 @@ func ManifestDiff(a, b string, verbose bool) (string, error) {

// ManifestDiffWithSelect checks the manifest differences with selected and ignored resources.
// The selected filter will apply before the ignored filter.
func ManifestDiffWithSelectAndIgnore(a, b, selectResources, ignoreResources string, verbose bool) (string, error) {
func ManifestDiffWithRenameSelectIgnore(a, b, renameResources, selectResources, ignoreResources string, verbose bool) (string, error) {
rnm := getKeyValueMap(renameResources)
sm := getObjPathMap(selectResources)
im := getObjPathMap(ignoreResources)
aosm, err := filterResourceWithSelectAndIgnore(a, sm, im)

ao, err := ParseK8sObjectsFromYAMLManifest(a)
if err != nil {
return "", err
}
bosm, err := filterResourceWithSelectAndIgnore(b, sm, im)
aom := ao.ToMap()

bo, err := ParseK8sObjectsFromYAMLManifest(b)
if err != nil {
return "", err
}
bom := bo.ToMap()

if len(rnm) != 0 {
aom, err = renameResource(aom, rnm)
if err != nil {
return "", err
}
}

aosm, err := filterResourceWithSelectAndIgnore(aom, sm, im)
if err != nil {
return "", err
}
bosm, err := filterResourceWithSelectAndIgnore(bom, sm, im)
if err != nil {
return "", err
}

return manifestDiff(aosm, bosm, im, verbose)
}

// filterResourceWithSelectAndIgnore filter the input resources with selected and ignored filter.
func filterResourceWithSelectAndIgnore(a string, sm, im map[string]string) (map[string]*K8sObject, error) {
ao, err := ParseK8sObjectsFromYAMLManifest(a)
if err != nil {
return nil, err
// renameResource filter the input resources with selected and ignored filter.
func renameResource(iom map[string]*K8sObject, rnm map[string]string) (map[string]*K8sObject, error) {
oom := make(map[string]*K8sObject)
for name, obj := range iom {
isRenamed := false
for fromPat, toPat := range rnm {
fromRe, err := buildResourceRegexp(strings.TrimSpace(fromPat))
if err != nil {
return nil, fmt.Errorf("error building the regexp from "+
"rename-from string: %v, error: %v", fromPat, err)
}
if fromRe.MatchString(name) {
fromList := strings.Split(name, ":")
if len(fromList) != 3 {
return nil, fmt.Errorf("failed to split the old name,"+
" length != 3: %v", name)
}
toList := strings.Split(toPat, ":")
if len(toList) != 3 {
return nil, fmt.Errorf("failed to split the rename-to string,"+
" length != 3: %v", toPat)
}

// Use the old name if toList has "*" or ""
// Otherwise, use the name in toList
newList := make([]string, 3)
for i := range toList {
if toList[i] == "" || toList[i] == "*" {
newList[i] = fromList[i]
} else {
newList[i] = toList[i]
}
}
tk := strings.Join(newList, ":")
oom[tk] = obj
isRenamed = true
}
}
if !isRenamed {
oom[name] = obj
}
}
aom := ao.ToMap()
return oom, nil
}

// filterResourceWithSelectAndIgnore filter the input resources with selected and ignored filter.
func filterResourceWithSelectAndIgnore(aom map[string]*K8sObject, sm, im map[string]string) (map[string]*K8sObject, error) {
aosm := make(map[string]*K8sObject)
for ak, av := range aom {
for selected := range sm {
Expand Down Expand Up @@ -514,6 +575,21 @@ func getObjPathMap(rs string) map[string]string {
return rm
}

func getKeyValueMap(rs string) map[string]string {
rm := make(map[string]string)
if len(rs) == 0 {
return rm
}
for _, r := range strings.Split(rs, ",") {
split := strings.Split(r, "->")
if len(split) != 2 {
continue
}
rm[split[0]] = split[1]
}
return rm
}

func objectIgnorePaths(objectName string, im map[string]string) (ignorePaths []string) {
if im == nil {
im = make(map[string]string)
Expand Down
Loading

0 comments on commit 643be5b

Please sign in to comment.