From a0980859391cab71494b2c2e08ad7a9be48142dc Mon Sep 17 00:00:00 2001 From: hourglasshoro Date: Mon, 1 Feb 2021 19:42:25 +0900 Subject: [PATCH 1/8] update: Added the ability to display PatchesStrategicMerge --- cmd/root.go | 8 ++- pkg/file/kustomization_file.go | 2 +- pkg/file/resource_file.go | 3 ++ pkg/graph/graph.go | 93 ++++++++++++++++++++++++++-------- 4 files changed, 83 insertions(+), 23 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 67658a0..974fba3 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -54,7 +54,13 @@ You can open a dashboard in your browser and see a graph of dependencies represe if err != nil { return errors.Wrap(err, "cannot build graph") } - graph.ToTree() + + fmt.Println() + for _, tree := range graph.Resources { + tree.ToTree() + fmt.Println() + } + return nil }, } diff --git a/pkg/file/kustomization_file.go b/pkg/file/kustomization_file.go index 46b0e8a..fd1c6a6 100644 --- a/pkg/file/kustomization_file.go +++ b/pkg/file/kustomization_file.go @@ -13,7 +13,7 @@ type KustomizationFile struct { Kind string `yaml:"kind"` Resources []string `yaml:"resources"` //Patches []string `yaml:"patches"` - //PatchesStrategicMerge []string `yaml:"patchesStrategicMerge"` + PatchesStrategicMerge []string `yaml:"patchesStrategicMerge"` } // KustomizationFileNames represents a list of allowed filenames that diff --git a/pkg/file/resource_file.go b/pkg/file/resource_file.go index e0db67b..05c0c5f 100644 --- a/pkg/file/resource_file.go +++ b/pkg/file/resource_file.go @@ -10,6 +10,9 @@ import ( type ResourceFile struct { ApiVersion string `yaml:"apiVersion"` Kind string `yaml:"kind"` + Metadata struct { + Name string `yaml:"name"` + } `yaml:"metadata"` } // GetResourceFromFile attempts to read a yaml file from the given file name diff --git a/pkg/graph/graph.go b/pkg/graph/graph.go index 0d62018..5cfe4ab 100644 --- a/pkg/graph/graph.go +++ b/pkg/graph/graph.go @@ -13,10 +13,11 @@ import ( ) type Graph struct { - ApiVersion string `json:"apiVersion"` - Kind string `json:"kind"` - FileName string `json:"fileName"` - Resources []Graph `json:"resources"` + ApiVersion string `json:"apiVersion"` + Kind string `json:"kind"` + FileName string `json:"fileName"` + Resources []*Graph `json:"resources"` + Patches map[int]*Graph //Patches []Graph //PatchesStrategicMerge []Graph } @@ -25,13 +26,15 @@ func NewGraph( apiVersion string, kind string, fileName string, - resources []Graph, + resources []*Graph, + patches map[int]*Graph, ) *Graph { graph := new(Graph) graph.ApiVersion = apiVersion graph.Kind = kind graph.FileName = fileName graph.Resources = resources + graph.Patches = patches return graph } @@ -43,14 +46,20 @@ func (g *Graph) Marshal() ([]byte, error) { // ToTree displays a tree structure func (g *Graph) ToTree() { - var isLastLoopFlags []bool - treeRecursion(g, isLastLoopFlags) + treeRecursion(g, []bool{}, g.Patches, true) } // treeRecursion calls output for each hierarchy -func treeRecursion(g *Graph, isLastLoopFlags []bool) { +func treeRecursion(g *Graph, isLastLoopFlags []bool, patches map[int]*Graph, isRoot bool) { output(g.FileName, isLastLoopFlags) + for i, patch := range g.Patches { + _, ok := patches[i] + if ok && !isRoot { + output(patch.FileName+"(p)", append(isLastLoopFlags, []bool{true}...)) + } + } + resources := g.Resources maxCount := len(resources) @@ -60,7 +69,7 @@ func treeRecursion(g *Graph, isLastLoopFlags []bool) { isLastLoop = true } flags := append(isLastLoopFlags, []bool{isLastLoop}...) - treeRecursion(&resources[i], flags) + treeRecursion(resources[i], flags, patches, false) } } @@ -100,7 +109,8 @@ func Find(slice []string, val string) (bool, int) { // BuildGraph recursively explores the specified directory, builds a dependency tree, and returns it func BuildGraph(ctx file.Context, rootPath string) (*Graph, error) { - rootGraph := NewGraph("root", "root", "/", []Graph{}) + + rootGraph := NewGraph("root", "root", "/", []*Graph{}, nil) // parentNodes determine if search should be skipped in BuildGraphFromDir parentNodes := map[string]*Graph{} @@ -108,6 +118,9 @@ func BuildGraph(ctx file.Context, rootPath string) (*Graph, error) { // childNodes determine whether to put in parentNode childNodes := map[string]*Graph{} + resourceNodes := map[string]*Graph{} + patchId := 0 + err := afero.Walk(ctx.FileSystem, rootPath, func(path string, info os.FileInfo, err error) error { if err != nil { @@ -128,7 +141,7 @@ func BuildGraph(ctx file.Context, rootPath string) (*Graph, error) { return errors.Wrap(err, "cannot get kustomization file") } - graph, err := BuildGraphFromDir(ctx, rootPath, kustomizationFilePath, *kustomizationFile, parentNodes, childNodes) + graph, err := BuildGraphFromDir(ctx, rootPath, kustomizationFilePath, *kustomizationFile, &parentNodes, &childNodes, &resourceNodes, &patchId) if err != nil { return errors.Wrap(err, "cannot get graph") } @@ -147,15 +160,20 @@ func BuildGraph(ctx file.Context, rootPath string) (*Graph, error) { } for _, v := range parentNodes { - rootGraph.Resources = append(rootGraph.Resources, *v) + rootGraph.Resources = append(rootGraph.Resources, v) } return rootGraph, nil } // BuildGraphFromDir builds and returns a dependency tree from a kustomization file under the specified directory -func BuildGraphFromDir(ctx file.Context, rootPath string, directoryPath string, kustomizationFile file.KustomizationFile, parentNodes map[string]*Graph, childNodes map[string]*Graph) (*Graph, error) { - var resources []Graph +func BuildGraphFromDir(ctx file.Context, rootPath string, directoryPath string, kustomizationFile file.KustomizationFile, parentNodesPtr *map[string]*Graph, childNodesPtr *map[string]*Graph, resourceNodesPtr *map[string]*Graph, patchId *int) (*Graph, error) { + var resources []*Graph + + parentNodes := *parentNodesPtr + childNodes := *childNodesPtr + resourceNodes := *resourceNodesPtr + for _, resource := range kustomizationFile.Resources { resourcePath := path.Join(directoryPath, resource) @@ -166,30 +184,30 @@ func BuildGraphFromDir(ctx file.Context, rootPath string, directoryPath string, isDir, err := afero.IsDir(ctx.FileSystem, resourcePath) if !isExist || err != nil { - resources = append(resources, *NewGraph("Unknown Resource", "Unknown Resource", resource, []Graph{})) + resources = append(resources, NewGraph("Unknown Resource", "Unknown Resource", resource, []*Graph{}, nil)) } else if isDir { // For directories // If a file at this path is already registered as a parent when searching if graph, isParent := parentNodes[resourcePath]; isParent { delete(parentNodes, resourcePath) - resources = append(resources, *graph) + resources = append(resources, graph) // Register nodes that have already been explored childNodes[resourcePath] = graph } else if graph, isChild := childNodes[resourcePath]; isChild { // If a file at this path is already registered as a child when searching - resources = append(resources, *graph) + resources = append(resources, graph) } else { childKustomizationFile, err := ctx.GetKustomizationFromDirectory(resourcePath) if err != nil { return nil, errors.Wrap(err, "cannot get childKustomizationFile") } - graph, err := BuildGraphFromDir(ctx, rootPath, resourcePath, *childKustomizationFile, parentNodes, childNodes) + graph, err := BuildGraphFromDir(ctx, rootPath, resourcePath, *childKustomizationFile, parentNodesPtr, childNodesPtr, resourceNodesPtr, patchId) if err != nil { return nil, errors.Wrap(err, "cannot buildGraph for childKustomizationFile") } - resources = append(resources, *graph) + resources = append(resources, graph) // Register nodes that have already been explored childNodes[resourcePath] = graph @@ -204,13 +222,46 @@ func BuildGraphFromDir(ctx file.Context, rootPath string, directoryPath string, if err != nil { return nil, errors.Wrap(err, "cannot get childResourceFile") } - resources = append(resources, *NewGraph(childResourceFile.ApiVersion, childResourceFile.Kind, resource, []Graph{})) + graph := NewGraph(childResourceFile.ApiVersion, childResourceFile.Kind, resource, []*Graph{}, map[int]*Graph{}) + resources = append(resources, graph) + resourceNodes[childResourceFile.Metadata.Name] = graph + } + } + + paches := map[int]*Graph{} + for _, patch := range kustomizationFile.PatchesStrategicMerge { + patchPath := path.Join(directoryPath, patch) + _, err := afero.Exists(ctx.FileSystem, patchPath) + if err != nil { + return nil, errors.Wrap(err, "cannot determine if patchPath exist") + } + patchResourceFile, err := ctx.GetResourceFromFile(patchPath) + if err != nil { + return nil, errors.Wrap(err, "cannot get patchResourceFile") + } + resource, ok := resourceNodes[patchResourceFile.Metadata.Name] + + if ok { + // When the resource has already been registered + formRootPath, err := filepath.Rel(rootPath, patchPath) + if err != nil { + return nil, errors.Wrap(err, "cannot get patch path from root") + } + patchGraph := NewGraph(patchResourceFile.ApiVersion, patchResourceFile.Kind, formRootPath, []*Graph{}, map[int]*Graph{}) + resource.Patches[*patchId] = patchGraph + //log.Printf("resource:%v, patch:%v, patchId:%d", resource.FileName, formRootPath, *patchId) + paches[*patchId] = patchGraph + *patchId++ + + } else { + // When the resource is not already registered } } + relPath, err := filepath.Rel(rootPath, directoryPath) if err != nil { return nil, err } - graph := NewGraph(kustomizationFile.ApiVersion, kustomizationFile.Kind, relPath, resources) + graph := NewGraph(kustomizationFile.ApiVersion, kustomizationFile.Kind, relPath, resources, paches) return graph, nil } From 5fd6fe769b9a89ccaa6fb289a1dcf5132c685d5e Mon Sep 17 00:00:00 2001 From: hourglasshoro Date: Mon, 1 Feb 2021 19:54:07 +0900 Subject: [PATCH 2/8] update: Add color to the patch --- go.mod | 1 + go.sum | 19 ++++++++++--------- pkg/graph/graph.go | 17 ++++++++++++----- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index dc8c4c9..b2c7de3 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/hourglasshoro/graphmize go 1.15 require ( + github.com/fatih/color v1.10.0 github.com/mitchellh/go-homedir v1.1.0 github.com/pkg/errors v0.9.1 github.com/spf13/afero v1.5.1 diff --git a/go.sum b/go.sum index 872a143..93f591c 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= +github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -114,7 +116,11 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= +github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= @@ -135,7 +141,6 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -165,7 +170,6 @@ github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIK github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.5.1 h1:VHu76Lk0LSP1x254maIu2bplkWpfBWI+B+6fdoZprcg= github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= @@ -178,18 +182,15 @@ github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb6 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= @@ -259,11 +260,12 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -320,7 +322,6 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/pkg/graph/graph.go b/pkg/graph/graph.go index 5cfe4ab..61ef194 100644 --- a/pkg/graph/graph.go +++ b/pkg/graph/graph.go @@ -3,6 +3,7 @@ package graph import ( "encoding/json" "fmt" + "github.com/fatih/color" "github.com/hourglasshoro/graphmize/pkg/file" "github.com/pkg/errors" "github.com/spf13/afero" @@ -51,12 +52,12 @@ func (g *Graph) ToTree() { // treeRecursion calls output for each hierarchy func treeRecursion(g *Graph, isLastLoopFlags []bool, patches map[int]*Graph, isRoot bool) { - output(g.FileName, isLastLoopFlags) + output(g.FileName, isLastLoopFlags, false) for i, patch := range g.Patches { _, ok := patches[i] if ok && !isRoot { - output(patch.FileName+"(p)", append(isLastLoopFlags, []bool{true}...)) + output(patch.FileName+"(p)", append(isLastLoopFlags, []bool{true}...), true) } } @@ -74,7 +75,7 @@ func treeRecursion(g *Graph, isLastLoopFlags []bool, patches map[int]*Graph, isR } // output prints the result to the standard output -func output(data string, isLastLoopFlags []bool) { +func output(data string, isLastLoopFlags []bool, isPatch bool) { pathLine := "" maxCount := len(isLastLoopFlags) for i := 0; i < maxCount; i++ { @@ -93,8 +94,14 @@ func output(data string, isLastLoopFlags []bool) { } } } - pathLine += data - fmt.Println(pathLine) + if isPatch { + c := color.New(color.FgCyan) + fmt.Print(pathLine) + _, _ = c.Println(data) + } else { + pathLine += data + fmt.Println(pathLine) + } } // Find determines if an element exists in the slice From 3ea40bbef8aa1a2a2c272d15a27601b995aaccf4 Mon Sep 17 00:00:00 2001 From: hourglasshoro Date: Mon, 1 Feb 2021 20:27:57 +0900 Subject: [PATCH 3/8] update: Addition of comments and omission of implementation --- pkg/file/kustomization_file.go | 7 +++--- pkg/graph/graph.go | 45 ++++++++++++++++++++++------------ 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/pkg/file/kustomization_file.go b/pkg/file/kustomization_file.go index fd1c6a6..82a68df 100644 --- a/pkg/file/kustomization_file.go +++ b/pkg/file/kustomization_file.go @@ -9,10 +9,9 @@ import ( // KustomizationFile represents a kustomization yaml file type KustomizationFile struct { - ApiVersion string `yaml:"apiVersion"` - Kind string `yaml:"kind"` - Resources []string `yaml:"resources"` - //Patches []string `yaml:"patches"` + ApiVersion string `yaml:"apiVersion"` + Kind string `yaml:"kind"` + Resources []string `yaml:"resources"` PatchesStrategicMerge []string `yaml:"patchesStrategicMerge"` } diff --git a/pkg/graph/graph.go b/pkg/graph/graph.go index 61ef194..48854cd 100644 --- a/pkg/graph/graph.go +++ b/pkg/graph/graph.go @@ -13,16 +13,16 @@ import ( "strings" ) +// Graph represents a node that is a customization file or resource file type Graph struct { ApiVersion string `json:"apiVersion"` Kind string `json:"kind"` FileName string `json:"fileName"` Resources []*Graph `json:"resources"` Patches map[int]*Graph - //Patches []Graph - //PatchesStrategicMerge []Graph } +// NewGraph is Graph constructor func NewGraph( apiVersion string, kind string, @@ -119,13 +119,15 @@ func BuildGraph(ctx file.Context, rootPath string) (*Graph, error) { rootGraph := NewGraph("root", "root", "/", []*Graph{}, nil) - // parentNodes determine if search should be skipped in BuildGraphFromDir + // parentNodes determine if search should be skipped in BuildGraphFromDir; map[resourcePath]*Node parentNodes := map[string]*Graph{} - // childNodes determine whether to put in parentNode + // childNodes determine whether to put in parentNode; map[resourcePath]*Node childNodes := map[string]*Graph{} + // resourceNode is the data to determine the patch; map[metadata.name]*Node resourceNodes := map[string]*Graph{} + // patchId is an Id to identify the patch that appeared patchId := 0 err := afero.Walk(ctx.FileSystem, rootPath, @@ -231,11 +233,20 @@ func BuildGraphFromDir(ctx file.Context, rootPath string, directoryPath string, } graph := NewGraph(childResourceFile.ApiVersion, childResourceFile.Kind, resource, []*Graph{}, map[int]*Graph{}) resources = append(resources, graph) + // If the patch has already been found when searching for the kustomization file + resource, exist := resourceNodes[childResourceFile.Metadata.Name] + if exist { + // Patch IDs is stored in resource.Patch + graph.Patches = resource.Patches + } resourceNodes[childResourceFile.Metadata.Name] = graph } } - paches := map[int]*Graph{} + // Store the patchId; map[patchId]*Node + patches := map[int]*Graph{} + + // Explore the paths passed by PatchesStrategicMerge for _, patch := range kustomizationFile.PatchesStrategicMerge { patchPath := path.Join(directoryPath, patch) _, err := afero.Exists(ctx.FileSystem, patchPath) @@ -248,27 +259,29 @@ func BuildGraphFromDir(ctx file.Context, rootPath string, directoryPath string, } resource, ok := resourceNodes[patchResourceFile.Metadata.Name] + formRootPath, err := filepath.Rel(rootPath, patchPath) + if err != nil { + return nil, errors.Wrap(err, "cannot get patch path from root") + } + + patchGraph := NewGraph(patchResourceFile.ApiVersion, patchResourceFile.Kind, formRootPath, []*Graph{}, map[int]*Graph{}) + if ok { // When the resource has already been registered - formRootPath, err := filepath.Rel(rootPath, patchPath) - if err != nil { - return nil, errors.Wrap(err, "cannot get patch path from root") - } - patchGraph := NewGraph(patchResourceFile.ApiVersion, patchResourceFile.Kind, formRootPath, []*Graph{}, map[int]*Graph{}) resource.Patches[*patchId] = patchGraph - //log.Printf("resource:%v, patch:%v, patchId:%d", resource.FileName, formRootPath, *patchId) - paches[*patchId] = patchGraph - *patchId++ - } else { - // When the resource is not already registered + // When a resource is not registered + resourceNodes[patchResourceFile.Metadata.Name] = NewGraph("", "", "", []*Graph{}, patches) } + + patches[*patchId] = patchGraph + *patchId++ } relPath, err := filepath.Rel(rootPath, directoryPath) if err != nil { return nil, err } - graph := NewGraph(kustomizationFile.ApiVersion, kustomizationFile.Kind, relPath, resources, paches) + graph := NewGraph(kustomizationFile.ApiVersion, kustomizationFile.Kind, relPath, resources, patches) return graph, nil } From 9a6f4c508944ced602071375039e0a5a5c728b42 Mon Sep 17 00:00:00 2001 From: hourglasshoro Date: Mon, 1 Feb 2021 20:31:26 +0900 Subject: [PATCH 4/8] update: Modification of existing tests --- pkg/graph/graph_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/graph/graph_test.go b/pkg/graph/graph_test.go index 4f76a13..2b4a5fd 100644 --- a/pkg/graph/graph_test.go +++ b/pkg/graph/graph_test.go @@ -55,7 +55,8 @@ kind: Deployment dir := "app" kustomizationFile, _ := file.NewFromFileSystem(fakeFileSystem).GetKustomizationFromDirectory(dir) - graph, err := BuildGraphFromDir(*ctx, "", dir, *kustomizationFile, map[string]*Graph{}, map[string]*Graph{}) + patchId := 0 + graph, err := BuildGraphFromDir(*ctx, "", dir, *kustomizationFile, &map[string]*Graph{}, &map[string]*Graph{}, &map[string]*Graph{}, &patchId) assert.Nil(t, err) expected := "a.yaml" @@ -117,7 +118,8 @@ kind: Deployment dir := "app/sub" kustomizationFile, _ := file.NewFromFileSystem(fakeFileSystem).GetKustomizationFromDirectory(dir) - graph, err := BuildGraphFromDir(*ctx, "", dir, *kustomizationFile, map[string]*Graph{}, map[string]*Graph{}) + patchId := 0 + graph, err := BuildGraphFromDir(*ctx, "", dir, *kustomizationFile, &map[string]*Graph{}, &map[string]*Graph{}, &map[string]*Graph{}, &patchId) assert.Nil(t, err) From f138127392cec4381ae6637d6f2b2a76038465ec Mon Sep 17 00:00:00 2001 From: hourglasshoro Date: Mon, 1 Feb 2021 20:48:11 +0900 Subject: [PATCH 5/8] add: patch_test.go --- pkg/graph/patch_test.go | 84 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 pkg/graph/patch_test.go diff --git a/pkg/graph/patch_test.go b/pkg/graph/patch_test.go new file mode 100644 index 0000000..cd84d9e --- /dev/null +++ b/pkg/graph/patch_test.go @@ -0,0 +1,84 @@ +package graph + +import ( + "github.com/hourglasshoro/graphmize/pkg/file" + "github.com/spf13/afero" + "github.com/stretchr/testify/assert" + "testing" +) + +// TestPatchesStrategicMerge is a test when PatchesStrategicMerge is specified in kustomize +func TestPatchesStrategicMerge(t *testing.T) { + // Folder structure for this test + // + // /app + // | + // ├── base + // | ├── kustomization.yaml + // | └── a.yaml + // | + // └── sub + // ├── kustomization.yaml + // └── patch.yaml + + fake := afero.NewMemMapFs() + ctx := file.NewContext(fake) + fakeFileSystem := ctx.FileSystem + fakeFileSystem.Mkdir("app", 0755) + fakeFileSystem.Mkdir("app/base", 0755) + fakeFileSystem.Mkdir("app/sub", 0755) + + fileContents := ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- a.yaml +` + afero.WriteFile(fakeFileSystem, "app/base/kustomization.yaml", []byte(fileContents), 0644) + + fileContents = ` +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: +- ../base + +patchesStrategicMerge: + - patch.yaml +` + afero.WriteFile(fakeFileSystem, "app/sub/kustomization.yaml", []byte(fileContents), 0644) + + fileContents = ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-deployment +spec: + replicas: 1 +` + + afero.WriteFile(fakeFileSystem, "app/base/a.yaml", []byte(fileContents), 0644) + + fileContents = ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: test-deployment +spec: + replicas: 3 +` + + afero.WriteFile(fakeFileSystem, "app/sub/patch.yaml", []byte(fileContents), 0644) + + dir := "app/sub" + kustomizationFile, _ := file.NewFromFileSystem(fakeFileSystem).GetKustomizationFromDirectory(dir) + patchId := 0 + graph, err := BuildGraphFromDir(*ctx, "", dir, *kustomizationFile, &map[string]*Graph{}, &map[string]*Graph{}, &map[string]*Graph{}, &patchId) + + assert.Nil(t, err) + + expected := "app/sub/patch.yaml" + actual := graph.Resources[0].Resources[0].Patches[0].FileName + assert.Equal(t, expected, actual) +} From 56f68c0024c9644265a353badb6ea1e3440db245 Mon Sep 17 00:00:00 2001 From: hourglasshoro Date: Mon, 1 Feb 2021 20:54:11 +0900 Subject: [PATCH 6/8] update: Change Id to ID --- pkg/graph/graph.go | 18 +++++++++--------- pkg/graph/patch_test.go | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pkg/graph/graph.go b/pkg/graph/graph.go index 48854cd..9161c8a 100644 --- a/pkg/graph/graph.go +++ b/pkg/graph/graph.go @@ -127,8 +127,8 @@ func BuildGraph(ctx file.Context, rootPath string) (*Graph, error) { // resourceNode is the data to determine the patch; map[metadata.name]*Node resourceNodes := map[string]*Graph{} - // patchId is an Id to identify the patch that appeared - patchId := 0 + // patchID is an Id to identify the patch that appeared + patchID := 0 err := afero.Walk(ctx.FileSystem, rootPath, func(path string, info os.FileInfo, err error) error { @@ -150,7 +150,7 @@ func BuildGraph(ctx file.Context, rootPath string) (*Graph, error) { return errors.Wrap(err, "cannot get kustomization file") } - graph, err := BuildGraphFromDir(ctx, rootPath, kustomizationFilePath, *kustomizationFile, &parentNodes, &childNodes, &resourceNodes, &patchId) + graph, err := BuildGraphFromDir(ctx, rootPath, kustomizationFilePath, *kustomizationFile, &parentNodes, &childNodes, &resourceNodes, &patchID) if err != nil { return errors.Wrap(err, "cannot get graph") } @@ -176,7 +176,7 @@ func BuildGraph(ctx file.Context, rootPath string) (*Graph, error) { } // BuildGraphFromDir builds and returns a dependency tree from a kustomization file under the specified directory -func BuildGraphFromDir(ctx file.Context, rootPath string, directoryPath string, kustomizationFile file.KustomizationFile, parentNodesPtr *map[string]*Graph, childNodesPtr *map[string]*Graph, resourceNodesPtr *map[string]*Graph, patchId *int) (*Graph, error) { +func BuildGraphFromDir(ctx file.Context, rootPath string, directoryPath string, kustomizationFile file.KustomizationFile, parentNodesPtr *map[string]*Graph, childNodesPtr *map[string]*Graph, resourceNodesPtr *map[string]*Graph, patchID *int) (*Graph, error) { var resources []*Graph parentNodes := *parentNodesPtr @@ -212,7 +212,7 @@ func BuildGraphFromDir(ctx file.Context, rootPath string, directoryPath string, if err != nil { return nil, errors.Wrap(err, "cannot get childKustomizationFile") } - graph, err := BuildGraphFromDir(ctx, rootPath, resourcePath, *childKustomizationFile, parentNodesPtr, childNodesPtr, resourceNodesPtr, patchId) + graph, err := BuildGraphFromDir(ctx, rootPath, resourcePath, *childKustomizationFile, parentNodesPtr, childNodesPtr, resourceNodesPtr, patchID) if err != nil { return nil, errors.Wrap(err, "cannot buildGraph for childKustomizationFile") } @@ -243,7 +243,7 @@ func BuildGraphFromDir(ctx file.Context, rootPath string, directoryPath string, } } - // Store the patchId; map[patchId]*Node + // Store the patchID; map[patchID]*Node patches := map[int]*Graph{} // Explore the paths passed by PatchesStrategicMerge @@ -268,14 +268,14 @@ func BuildGraphFromDir(ctx file.Context, rootPath string, directoryPath string, if ok { // When the resource has already been registered - resource.Patches[*patchId] = patchGraph + resource.Patches[*patchID] = patchGraph } else { // When a resource is not registered resourceNodes[patchResourceFile.Metadata.Name] = NewGraph("", "", "", []*Graph{}, patches) } - patches[*patchId] = patchGraph - *patchId++ + patches[*patchID] = patchGraph + *patchID++ } relPath, err := filepath.Rel(rootPath, directoryPath) diff --git a/pkg/graph/patch_test.go b/pkg/graph/patch_test.go index cd84d9e..934217d 100644 --- a/pkg/graph/patch_test.go +++ b/pkg/graph/patch_test.go @@ -73,8 +73,8 @@ spec: dir := "app/sub" kustomizationFile, _ := file.NewFromFileSystem(fakeFileSystem).GetKustomizationFromDirectory(dir) - patchId := 0 - graph, err := BuildGraphFromDir(*ctx, "", dir, *kustomizationFile, &map[string]*Graph{}, &map[string]*Graph{}, &map[string]*Graph{}, &patchId) + patchID := 0 + graph, err := BuildGraphFromDir(*ctx, "", dir, *kustomizationFile, &map[string]*Graph{}, &map[string]*Graph{}, &map[string]*Graph{}, &patchID) assert.Nil(t, err) From 5c821d6e242eef51d6e82c2ed076ff832064dc01 Mon Sep 17 00:00:00 2001 From: hourglasshoro Date: Mon, 1 Feb 2021 21:02:29 +0900 Subject: [PATCH 7/8] update: Change Id to ID --- pkg/graph/graph_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/graph/graph_test.go b/pkg/graph/graph_test.go index 2b4a5fd..4091235 100644 --- a/pkg/graph/graph_test.go +++ b/pkg/graph/graph_test.go @@ -55,8 +55,8 @@ kind: Deployment dir := "app" kustomizationFile, _ := file.NewFromFileSystem(fakeFileSystem).GetKustomizationFromDirectory(dir) - patchId := 0 - graph, err := BuildGraphFromDir(*ctx, "", dir, *kustomizationFile, &map[string]*Graph{}, &map[string]*Graph{}, &map[string]*Graph{}, &patchId) + patchID := 0 + graph, err := BuildGraphFromDir(*ctx, "", dir, *kustomizationFile, &map[string]*Graph{}, &map[string]*Graph{}, &map[string]*Graph{}, &patchID) assert.Nil(t, err) expected := "a.yaml" @@ -118,8 +118,8 @@ kind: Deployment dir := "app/sub" kustomizationFile, _ := file.NewFromFileSystem(fakeFileSystem).GetKustomizationFromDirectory(dir) - patchId := 0 - graph, err := BuildGraphFromDir(*ctx, "", dir, *kustomizationFile, &map[string]*Graph{}, &map[string]*Graph{}, &map[string]*Graph{}, &patchId) + patchID := 0 + graph, err := BuildGraphFromDir(*ctx, "", dir, *kustomizationFile, &map[string]*Graph{}, &map[string]*Graph{}, &map[string]*Graph{}, &patchID) assert.Nil(t, err) From f6e4265a0a651cdc2849785984e9673e4a0bff9d Mon Sep 17 00:00:00 2001 From: hourglasshoro Date: Mon, 1 Feb 2021 21:03:45 +0900 Subject: [PATCH 8/8] update: Modify the README and examples. --- README.md | 25 +++++++++++-------- .../production/a_service/deployment.yaml | 8 ++++++ .../production/a_service/service.yaml | 10 ++++++++ .../overlays/production/kustomization.yaml | 6 ++++- .../staging/a_service/deployment.yaml | 8 ++++++ .../overlays/staging/kustomization.yaml | 5 +++- 6 files changed, 49 insertions(+), 13 deletions(-) create mode 100644 docs/example/overlays/production/a_service/deployment.yaml create mode 100644 docs/example/overlays/production/a_service/service.yaml create mode 100644 docs/example/overlays/staging/a_service/deployment.yaml diff --git a/README.md b/README.md index 5c85cc6..1b3656a 100644 --- a/README.md +++ b/README.md @@ -29,16 +29,19 @@ The actual directory structure, manifest, etc. can be found on [this page](https If you run Graphmize with this directory specified, the output will look like this. ``` -/ -├── overlays/production -│ └── base -│ └── base/a_service -│ ├── deployment.yaml -│ └── service.yaml -└── overlays/staging - └── base - └── base/a_service - ├── deployment.yaml - └── service.yaml +example/overlays/production +└── example/base + └── example/base/a_service + ├── deployment.yaml + │ └── example/overlays/production/a_service/deployment.yaml(p) + └── service.yaml + └── example/overlays/production/a_service/service.yaml(p) + +example/overlays/staging +└── example/base + └── example/base/a_service + ├── deployment.yaml + │ └── example/overlays/staging/a_service/deployment.yaml(p) + └── service.yaml ``` \ No newline at end of file diff --git a/docs/example/overlays/production/a_service/deployment.yaml b/docs/example/overlays/production/a_service/deployment.yaml new file mode 100644 index 0000000..a02a0b5 --- /dev/null +++ b/docs/example/overlays/production/a_service/deployment.yaml @@ -0,0 +1,8 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: example + labels: + app: example +spec: + replicas: 5 diff --git a/docs/example/overlays/production/a_service/service.yaml b/docs/example/overlays/production/a_service/service.yaml new file mode 100644 index 0000000..b7e3f75 --- /dev/null +++ b/docs/example/overlays/production/a_service/service.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: example-service +spec: + selector: + app: example + ports: + - port: 8080 + type: LoadBalancer \ No newline at end of file diff --git a/docs/example/overlays/production/kustomization.yaml b/docs/example/overlays/production/kustomization.yaml index 681848f..1e8bd66 100644 --- a/docs/example/overlays/production/kustomization.yaml +++ b/docs/example/overlays/production/kustomization.yaml @@ -2,4 +2,8 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - - ../../base \ No newline at end of file + - ../../base + +patchesStrategicMerge: + - a_service/deployment.yaml + - a_service/service.yaml \ No newline at end of file diff --git a/docs/example/overlays/staging/a_service/deployment.yaml b/docs/example/overlays/staging/a_service/deployment.yaml new file mode 100644 index 0000000..389d034 --- /dev/null +++ b/docs/example/overlays/staging/a_service/deployment.yaml @@ -0,0 +1,8 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: example + labels: + app: example +spec: + replicas: 3 diff --git a/docs/example/overlays/staging/kustomization.yaml b/docs/example/overlays/staging/kustomization.yaml index 681848f..8ec01dc 100644 --- a/docs/example/overlays/staging/kustomization.yaml +++ b/docs/example/overlays/staging/kustomization.yaml @@ -2,4 +2,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - - ../../base \ No newline at end of file + - ../../base + +patchesStrategicMerge: + - a_service/deployment.yaml \ No newline at end of file