Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Git resolver #99

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

# Dependency directories (remove the comment below to include it)
# vendor/
falco
dist/*
playground

Expand Down
17 changes: 15 additions & 2 deletions cmd/falco/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ type Config struct {
Remote bool
Stats bool
Json bool
GitMode bool
RepoPath string
}

func write(c *color.Color, format string, args ...interface{}) {
Expand Down Expand Up @@ -86,6 +88,8 @@ Flags:
-vv : Varbose all lint result
-json : Output statistics as JSON
-stats : Analyze VCL statistics
-repo : Path to the git repo that contains the modules
-git : Use git to read versioned files that in the form of path/to/file/:version

Simple Linting example:
falco -I . -vv /path/to/vcl/main.vcl
Expand Down Expand Up @@ -119,6 +123,8 @@ func main() {
fs.BoolVar(&c.Remote, "remote", false, "Use Remote")
fs.BoolVar(&c.Stats, "stats", false, "Analyze VCL statistics")
fs.BoolVar(&c.Json, "json", false, "Output statistics as JSON")
fs.StringVar(&c.RepoPath, "repo", "", "Path to the git repo that contains the modules")
fs.BoolVar(&c.GitMode, "git", false, "Use git to read versioned files that in the form of path/to/file/:version")
fs.Usage = printUsage

var err error
Expand All @@ -136,17 +142,24 @@ func main() {
os.Exit(1)
}

getResolver := func(main string) ([]Resolver, error) {
if c.GitMode {
return NewGitFileResolvers(main, c, OsCommand)
} else {
return NewFileResolvers(main, c)
}
}
// falco could lint multiple services so resolver should be a slice
var resolvers []Resolver
switch fs.Arg(0) {
case subcommandTerraform:
resolvers, err = NewStdinResolvers()
case subcommandLint:
// "lint" command provides single file of service, then resolvers size is always 1
resolvers, err = NewFileResolvers(fs.Arg(1), c)
resolvers, err = getResolver(fs.Arg(1))
default:
// "lint" command provides single file of service, then resolvers size is always 1
resolvers, err = NewFileResolvers(fs.Arg(0), c)
resolvers, err = getResolver(fs.Arg(0))
}

if err != nil {
Expand Down
167 changes: 0 additions & 167 deletions cmd/falco/resolver.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,6 @@
package main

import (
"bytes"
"fmt"
"os"
"strings"
"time"

"io/ioutil"
"path/filepath"

"github.com/pkg/errors"
)

Expand All @@ -28,161 +19,3 @@ type Resolver interface {
Resolve(module string) (*VCL, error)
Name() string
}

// FileResolver is filesystem resolver, basically used for built vcl files
type FileResolver struct {
main string
includePaths []string
}

func NewFileResolvers(main string, c *Config) ([]Resolver, error) {
if main == "" {
return nil, ErrEmptyMain
}

if _, err := os.Stat(main); err != nil {
if err == os.ErrNotExist {
return nil, errors.New(fmt.Sprintf("Input file %s is not found", main))
}
return nil, errors.New(fmt.Sprintf("Unexpected stat error: %s", err.Error()))
}

abs, err := filepath.Abs(main)
if err != nil {
return nil, errors.New(fmt.Sprintf("Failed to get absolute path: %s", err.Error()))
}

var includePaths []string
// Add include paths as absolute
for i := range c.IncludePaths {
p, err := filepath.Abs(c.IncludePaths[i])
if err == nil {
includePaths = append(includePaths, p)
}
}
includePaths = append(includePaths, filepath.Dir(abs))

return []Resolver{
&FileResolver{
main: abs,
includePaths: includePaths,
},
}, nil
}

func (f *FileResolver) Name() string {
return ""
}

func (f *FileResolver) getVCL(file string) (*VCL, error) {
if _, err := os.Stat(file); err != nil {
return nil, err
}
fp, err := os.Open(file)
if err != nil {
return nil, err
}
defer fp.Close()

buf := &bytes.Buffer{}
if _, err := buf.ReadFrom(fp); err != nil {
return nil, err
}

return &VCL{
Name: file,
Data: buf.String(),
}, nil
}

func (f *FileResolver) MainVCL() (*VCL, error) {
return f.getVCL(f.main)
}

func (f *FileResolver) Resolve(module string) (*VCL, error) {
module_path_with_extension := module
if !strings.HasSuffix(module_path_with_extension, ".vcl") {
module_path_with_extension += ".vcl"
}

// Find for each include paths
for _, p := range f.includePaths {
if vcl, err := f.getVCL(filepath.Join(p, module_path_with_extension)); err == nil {
return vcl, nil
}
}
return nil, errors.New(fmt.Sprintf("Failed to resolve include file: %s.vcl", module))
}

// StdinResolver is in memory resolver, read and factory vcl data from terraform planned JSON input
type StdinResolver struct {
Modules []*VCL
Main *VCL
ServiceName string
}

func NewStdinResolvers() ([]Resolver, error) {
// Consider reading from stdin timeout to not to hang up in CI flow
input := make(chan []byte)
errChan := make(chan error)

go func() {
buf, err := ioutil.ReadAll(os.Stdin)
if err != nil {
errChan <- err
return
}
input <- buf
}()

var resolvers []Resolver
select {
case buf := <-input:
services, err := unmarshalTerraformPlannedInput(buf)
if err != nil {
return nil, err
}
for _, v := range services {
s := &StdinResolver{
ServiceName: v.Name,
}
for _, vcl := range v.Vcls {
if vcl.Main {
s.Main = &VCL{
Name: vcl.Name,
Data: vcl.Content,
}
} else {
s.Modules = append(s.Modules, &VCL{
Name: vcl.Name,
Data: vcl.Content,
})
}
}
resolvers = append(resolvers, s)
}
return resolvers, nil
case err := <-errChan:
return nil, errors.New(fmt.Sprintf("Failed to read from stdin: %s", err.Error()))
case <-time.After(10 * time.Second):
return nil, errors.New(("Failed to read from stdin: timed out"))
}
}

func (s *StdinResolver) Name() string {
return s.ServiceName
}

func (s *StdinResolver) MainVCL() (*VCL, error) {
return s.Main, nil
}

func (s *StdinResolver) Resolve(module string) (*VCL, error) {
for i := range s.Modules {
if s.Modules[i].Name == module {
return s.Modules[i], nil
}
}

return nil, errors.New(fmt.Sprintf("Failed to resolve include file: %s.vcl", module))
}
Loading