Skip to content

Commit

Permalink
Initial implementation with playbook matching
Browse files Browse the repository at this point in the history
  • Loading branch information
meomap committed Aug 30, 2018
1 parent 4ecfe33 commit 7de2574
Show file tree
Hide file tree
Showing 12 changed files with 804 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@

# Output of the go coverage tool, specifically when used with LiteIDE
*.out
vendor
zeno
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,19 @@
# zeno
CLI tool to examine ansible playbooks affected by git changes

## Installation
```
$ go get -u github.com/meomap/zeno
```
## Usage
```
$ zeno -files="$(git diff $COMMIT_HASH_BEFORE $COMMIT_HASH_AFTER --name-only)" -playbooks=qa/site.yml,staging/site.yml
qa/site.yml,staging/site.yml
```
## Features

- Ansible playbook supported.

## Contributing

Bug reports & pull requests are welcome.
19 changes: 19 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module github.com/meomap/zeno

require (
github.com/alecthomas/gometalinter v2.0.6+incompatible // indirect
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/davidrjenni/reftools v0.0.0-20180509164333-3813a62570d2 // indirect
github.com/fatih/gomodifytags v0.0.0-20180826164257-7987f52a7108 // indirect
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9 // indirect
github.com/nicksnyder/go-i18n v1.10.0 // indirect
github.com/nsf/gocode v0.0.0-20180502111240-9d1e0378d35b // indirect
github.com/pelletier/go-toml v1.2.0 // indirect
github.com/pkg/errors v0.8.0
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.2.2
golang.org/x/tools v0.0.0-20180826000951-f6ba57429505 // indirect
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c // indirect
gopkg.in/yaml.v2 v2.2.1
)
37 changes: 37 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
github.com/alecthomas/gometalinter v2.0.6+incompatible h1:Z7mLBD7u7kjfsxa/edMutwqGygQI//inz0s+FCRmnvw=
github.com/alecthomas/gometalinter v2.0.6+incompatible/go.mod h1:qfIpQGGz3d+NmgyPBqv+LSh50emm1pt72EtcX2vKYQk=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davidrjenni/reftools v0.0.0-20180509164333-3813a62570d2 h1:s9FFqeh2hSpBk09NiFLaHHQddrrLBlTrT2p26sPUfd4=
github.com/davidrjenni/reftools v0.0.0-20180509164333-3813a62570d2/go.mod h1:8o/GRMvsb9VyFbSEZGXfa0dkSXml4G23W0D/h9FksWM=
github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8=
github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc=
github.com/fatih/gomodifytags v0.0.0-20180826164257-7987f52a7108 h1:CIkmEYX6OO/UB9pBSdFvVlhkpT0pXjqXYo9lMmGVaLI=
github.com/fatih/gomodifytags v0.0.0-20180826164257-7987f52a7108/go.mod h1:p2/x7bnOQsbq/deXsDIlj2yLiKFGPkD2nuoYqwn8R4Y=
github.com/fatih/structtag v1.0.0 h1:pTHj65+u3RKWYPSGaU290FpI/dXxTaHdVwVwbcPKmEc=
github.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA=
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9 h1:JM174NTeGNJ2m/oLH3UOWOvWQQKd+BoL3hcSCUWFLt0=
github.com/google/shlex v0.0.0-20150127133951-6f45313302b9/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE=
github.com/nicksnyder/go-i18n v1.10.0 h1:5AzlPKvXBH4qBzmZ09Ua9Gipyruv6uApMcrNZdo96+Q=
github.com/nicksnyder/go-i18n v1.10.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q=
github.com/nsf/gocode v0.0.0-20180502111240-9d1e0378d35b h1:agMWdTHUzw9qlSSiKEz7SwXP2ZmLesKkmWYaS0K2GLk=
github.com/nsf/gocode v0.0.0-20180502111240-9d1e0378d35b/go.mod h1:6Q8/OMaaKAgTX7/jt2bOXVDrm1eJhoNd+iwzghR7jvs=
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 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/tools v0.0.0-20180824175216-6c1c5e93cdc1/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180826000951-f6ba57429505 h1:A7IWEQZw8yuHhjAcYjsnhC1/6KYEknVQYRVmhut3Gzk=
golang.org/x/tools v0.0.0-20180826000951-f6ba57429505/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c h1:vTxShRUnK60yd8DZU+f95p1zSLj814+5CuEh7NjF2/Y=
gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c/go.mod h1:3HH7i1SgMqlzxCcBmUHW657sD4Kvv9sC3HpL3YukzwA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
131 changes: 131 additions & 0 deletions loader/loader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package loader

import (
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"

"github.com/pkg/errors"
)

// DataSource defines common interface for data loading
type DataSource interface {
ReadFile(string) ([]byte, error)
ReadDir(string) ([]string, error)
IsExist(string) (bool, error)
}

// MemoryLoader implements IO operations for testing
type MemoryLoader struct {
files map[string][]byte
dirs map[string][]string
}

// ReadFile returns byte content given preload file name
func (ml MemoryLoader) ReadFile(name string) ([]byte, error) {
content, ok := ml.files[name]
if !ok {
return nil, errors.Errorf("file %s not exist", name)
}
return content, nil
}

// SetFile update stored file's content
func (ml *MemoryLoader) SetFile(name string, content []byte) {
if ml.files == nil {
ml.files = map[string][]byte{}
}
pathComps := strings.Split(name, string(filepath.Separator))
lenComps := len(pathComps)
var (
subdir []byte
ok bool
parent string
)
for i := 0; i < lenComps-1; i++ {
if i == 0 {
parent = pathComps[i]
} else {
parent = path.Join(parent, pathComps[i])
}
child := pathComps[i+1]
if subdir, ok = ml.files[parent]; !ok {
subdir = []byte(child)
} else {
updated := strings.Split(string(subdir), ",")
updated = append(updated, child)
subdir = []byte(strings.Join(updated, ","))
}
ml.files[parent] = subdir
}
ml.files[name] = content
}

// ReadDir returns list of files' name under specified directory
func (ml MemoryLoader) ReadDir(name string) ([]string, error) {
content, ok := ml.files[name]
if !ok {
return nil, &os.PathError{Err: os.ErrNotExist}
}
children := strings.Split(string(content), ",")
return children, nil
}

// IsExist returns true if given file name exists
func (ml MemoryLoader) IsExist(name string) (ok bool, err error) {
if _, ok = ml.files[name]; !ok {
for dir := range ml.dirs {
if strings.HasPrefix(dir, name) {
ok = true
break
}
}
}
return
}

// Clear reset in-mem data
func (ml *MemoryLoader) Clear() {
ml.files = nil
ml.dirs = nil
}

// FileLoader implements IO operation on local disk file
type FileLoader struct {
}

// ReadFile returns byte content given file name
func (fl FileLoader) ReadFile(name string) ([]byte, error) {
content, err := ioutil.ReadFile(name)
if err != nil {
return nil, errors.Wrapf(err, "ioutil.ReadFile name=%s", name)
}
return content, nil
}

// ReadDir returns list of files' name under specified directory
func (fl FileLoader) ReadDir(name string) (out []string, err error) {
var stats []os.FileInfo
if stats, err = ioutil.ReadDir(name); err != nil {
err = errors.Wrapf(err, "ioutil.ReadDir name=%s", name)
return
}
for _, v := range stats {
out = append(out, v.Name())
}
return
}

// IsExist returns true if given file name exists
func (fl FileLoader) IsExist(name string) (bool, error) {
_, err := os.Stat(name)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, errors.Wrapf(err, "os.Stat name=%s", name)
}
return true, nil
}
66 changes: 66 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// zeno is a command line tool to analyse ansible playbook dependencies
package main

import (
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"path"
"strings"

"github.com/meomap/zeno/loader"
"github.com/meomap/zeno/search"
)

func main() {
var (
filesIn = flag.String("files", "", "names of changed files from command 'git diff $BEFORE $AFTER --name-only'")
debug = flag.Bool("debug", false, "enable for verbose logging")
pbsIn = flag.String("playbooks", "", "comma separated list of playbooks to examined")
)
flag.Parse()

// required args
if *pbsIn == "" || *filesIn == "" {
flag.PrintDefaults()
os.Exit(1)
}
if *debug == false {
log.SetOutput(ioutil.Discard)
}
diffFiles := strings.Split(*filesIn, "\n")
repoDir, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
lenDiffs := len(diffFiles)
// construct absolute path for input files
for i := 0; i < lenDiffs; i++ {
diffFiles[i] = path.Join(repoDir, diffFiles[i])
}
pbFiles := strings.Split(*pbsIn, ",")
lenPbs := len(pbFiles)
log.Printf("Match against [%d] files", lenDiffs)
log.Printf("Examine [%d] playbooks: %s\n", lenPbs, *pbsIn)

ds := new(loader.FileLoader)
var (
matched bool
out []string
)
for i := 0; i < lenPbs; i++ {
name := pbFiles[i]
if matched, err = search.MatchPlaybook(name, diffFiles, repoDir, ds); err != nil {
log.Fatal(err)
} else if matched {
out = append(out, name)
}
}
fmt.Println(strings.Join(out, ","))
}

func init() {
log.SetFlags(log.LstdFlags | log.Lmicroseconds | log.Lshortfile)
}
Loading

0 comments on commit 7de2574

Please sign in to comment.