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

Use go-defaults to set default value for file mode, use validate library to validate all blocks #25

Merged
merged 5 commits into from
Jan 9, 2024
Merged
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
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ require (
github.com/lonegunmanb/go-yaml-edit v0.0.0-20231115083743-85302adf634b
github.com/lonegunmanb/hclfuncs v0.4.1
github.com/lonegunmanb/yaml-jsonpointer v0.1.2-0.20231115082754-71ac0a5bbbd2
github.com/mcuadros/go-defaults v1.2.0
github.com/mcuadros/go-defaults v1.3.0
github.com/peterh/liner v1.2.2
github.com/prashantv/gostub v1.1.0
github.com/spf13/afero v1.11.0
Expand Down Expand Up @@ -96,3 +96,5 @@ require (
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

replace github.com/mcuadros/go-defaults v1.3.0 => github.com/lonegunmanb/go-defaults v1.3.0
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
bou.ke/monkey v1.0.2 h1:kWcnsrCNUatbxncxR/ThdYqbytgOIArtYWqcQLQzKLI=
bou.ke/monkey v1.0.2/go.mod h1:OqickVX3tNx6t33n1xvtTtu85YN5s6cKwVug+oHMaIA=
codeberg.org/6543/go-yaml2json v1.0.0 h1:heGqo9VEi7gY2yNqjj7X4ADs5nzlFIbGsJtgYDLrnig=
codeberg.org/6543/go-yaml2json v1.0.0/go.mod h1:mz61q14LWF4ZABrgMEDMmk3t9dPi6zgR1uBh2VKV2RQ=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
Expand Down Expand Up @@ -196,6 +198,8 @@ github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/lonegunmanb/atomatt-yaml v0.0.0-20231115063413-65f675868d34 h1:k/TmToj7wP7k6Ul6bh4I9anc+y39+wVYRqjOVdAOnbQ=
github.com/lonegunmanb/atomatt-yaml v0.0.0-20231115063413-65f675868d34/go.mod h1:VIId/O/ib34Xhk/gUq3Wi/NBGSo8ArxVLurqhBInv1c=
github.com/lonegunmanb/go-defaults v1.3.0 h1:Ta5uZW54yWu2smdEZ3FrcgZcWIWvm/ppVFy0b1ibLI4=
github.com/lonegunmanb/go-defaults v1.3.0/go.mod h1:0xAP3y7xIeYvZRoC51utMuERLFWbTNdghNH6/FH/lFY=
github.com/lonegunmanb/go-yaml-edit v0.0.0-20231115083743-85302adf634b h1:iq8qR7E7mpIUs9n3TBxdFceYTfpsEdXcOaUXfxEW6e0=
github.com/lonegunmanb/go-yaml-edit v0.0.0-20231115083743-85302adf634b/go.mod h1:uvga3hKd6b8WDuxlf7sgaxP0jUN5fdYBfCGhWAj75yo=
github.com/lonegunmanb/hclfuncs v0.4.1 h1:z2ieMu+vCwIbT3K1MVVf6ac0coXi5XYjlIT/8CuL9rE=
Expand Down Expand Up @@ -225,8 +229,6 @@ github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mcuadros/go-defaults v1.2.0 h1:FODb8WSf0uGaY8elWJAkoLL0Ri6AlZ1bFlenk56oZtc=
github.com/mcuadros/go-defaults v1.2.0/go.mod h1:WEZtHEVIGYVDqkKSWBdWKUVdRyKlMfulPaGDWIVeCWY=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
Expand Down
2 changes: 1 addition & 1 deletion pkg/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ var metaAttributeNames = hashset.New("for_each", "rule_ids")
var metaNestedBlockNames = hashset.New("precondition")

func decode(b block) error {
defaults.SetDefaults(b)
hb := b.HclBlock()
evalContext := b.EvalContext()
if decodeBase, ok := b.(DecodeBase); ok {
Expand All @@ -54,6 +53,7 @@ func decode(b block) error {
if diag.HasErrors() {
return diag
}
defaults.SetDefaults(b)
return nil
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ func planBlock(b block) error {
return fmt.Errorf("%s.%s.%s is not valid: %s", b.BlockType(), b.Type(), b.Name(), err.Error())
}
}
if validateErr := validate.Struct(b); validateErr != nil {
return fmt.Errorf("%s.%s.%s is not valid: %s", b.BlockType(), b.Type(), b.Name(), validateErr.Error())
}
failedChecks, preConditionCheckError := b.PreConditionCheck(b.EvalContext())
if preConditionCheckError != nil {
return preConditionCheckError
Expand Down
1 change: 0 additions & 1 deletion pkg/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,6 @@ func (s *configSuite) TestLocalBetweenDataAndRule() {
}

func (s *configSuite) TestForEach_ForEachBlockShouldBeExpanded() {
//s.T().Skip("for now")
hclConfig := `
locals {
items = ["item1", "item2", "item3"]
Expand Down
28 changes: 16 additions & 12 deletions pkg/fix_local_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ var _ Fix = &LocalFileFix{}
type LocalFileFix struct {
*BaseBlock
*BaseFix
Paths []string `json:"paths" hcl:"paths"`
Content string `json:"content" hcl:"content"`
Mode *uint32 `json:"mode" hcl:"mode,optional"`
Paths []string `json:"paths" hcl:"paths"`
Content string `json:"content" hcl:"content"`
Mode *fs.FileMode `json:"mode" hcl:"mode,optional" default:"0644" validate:"file_mode"`
}

func (lf *LocalFileFix) Values() map[string]cty.Value {
Expand All @@ -33,22 +33,26 @@ func (lf *LocalFileFix) Type() string {
}

func (lf *LocalFileFix) Apply() error {
var err error
var filemode = fs.FileMode(0644)
if lf.Mode != nil {
mode, err := strconv.ParseUint(strconv.Itoa(int(*lf.Mode)), 8, 32)
if err != nil {
return fmt.Errorf("invalid file mode: %w", err)
}
filemode = fs.FileMode(mode)
fm, err := toDecimal(*lf.Mode)
if err != nil {
return err
}

for _, path := range lf.Paths {
writeErr := afero.WriteFile(FsFactory(), path, []byte(lf.Content), filemode)
writeErr := afero.WriteFile(FsFactory(), path, []byte(lf.Content), fm)
if writeErr != nil {
err = multierror.Append(err, writeErr)
}
}

return err
}

func toDecimal(octalMode fs.FileMode) (fs.FileMode, error) {
mode, err := strconv.ParseUint(strconv.Itoa(int(octalMode)), 8, 32)
if err != nil {
return 0, fmt.Errorf("invalid file mode: %w", err)
}
fm := fs.FileMode(mode)
return fm, nil
}
141 changes: 82 additions & 59 deletions pkg/fix_local_file_test.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package pkg

import (
"fmt"
iofs "io/fs"
"testing"

"github.com/stretchr/testify/suite"

"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
)

type localFileFixSuite struct {
Expand All @@ -23,16 +23,26 @@ func (s *localFileFixSuite) SetupTest() {
s.testBase = newTestBase()
}

func (s *localFileFixSuite) SetupSubTest() {
s.SetupTest()
}

func (s *localFileFixSuite) TearDownTest() {
s.teardown()
}

func (s *localFileFixSuite) TearDownSubTest() {
s.TearDownTest()
}

func (s *localFileFixSuite) TestLocalFile_ApplyFix_CreateNewFile() {
fs := s.fs
t := s.T()
mode := iofs.FileMode(0644)
fix := &LocalFileFix{
Paths: []string{"/file1.txt"},
Content: "Hello, world!",
Mode: &mode,
}

err := fix.Apply()
Expand All @@ -48,10 +58,12 @@ func (s *localFileFixSuite) TestLocalFile_ApplyFix_OverwriteExistingFile() {
fs := s.fs
t := s.T()
path := "/file1.txt"
mode := iofs.FileMode(0644)
fix := &LocalFileFix{
BaseBlock: &BaseBlock{},
Paths: []string{path},
Content: "Hello, world!",
Mode: &mode,
}

// Create the file first
Expand All @@ -73,10 +85,12 @@ func (s *localFileFixSuite) TestLocalFile_ApplyFix_FileInSubFolder() {
fs := s.fs
t := s.T()
path := "/example/sub1/file1.txt"
mode := iofs.FileMode(0644)
fix := &LocalFileFix{
BaseBlock: &BaseBlock{},
Paths: []string{path},
Content: "Hello, world!",
Mode: &mode,
}

// Create the file first
Expand All @@ -94,65 +108,74 @@ func (s *localFileFixSuite) TestLocalFile_ApplyFix_FileInSubFolder() {
assert.Equal(t, fix.Content, string(content))
}

func (s *localFileFixSuite) TestLocalFile_ApplyFix_FileHasDefaultMode0644() {
fs := s.fs
t := s.T()
path := "/file1.txt"
fix := &LocalFileFix{
BaseBlock: &BaseBlock{},
Paths: []string{path},
Content: "Hello, world!",
func (s *localFileFixSuite) TestLocalFile_ApplyFix_FileMode() {
var pString = func(s string) *string {
return &s
}

// Create the file first
err := fix.Apply()
assert.NoError(t, err)

// Check default mode 0644
finfo, err := fs.Stat(path)
assert.NoError(t, err)
assert.Equal(t, finfo.Mode(), iofs.FileMode(0644))
}

func (s *localFileFixSuite) TestLocalFile_ApplyFix_FileHasCustomMode() {
fs := s.fs
t := s.T()
path := "/file1.txt"
mode := uint32(755)
fix := &LocalFileFix{
BaseBlock: &BaseBlock{},
Paths: []string{path},
Content: "Hello, world!",
Mode: &mode,
cases := []struct {
desc string
mode *string
wanted iofs.FileMode
expectedErrorMessage *string
}{
{
desc: "no assignment",
mode: nil,
wanted: iofs.FileMode(0644),
},
{
desc: "customized mode",
mode: pString("0777"),
wanted: iofs.ModePerm,
},
{
desc: "explicit null",
mode: pString("null"),
wanted: iofs.FileMode(0644),
},
{
desc: "invalid mode1",
mode: pString("0778"),
expectedErrorMessage: pString("file_mode"),
},
}

// Create the file first
err := fix.Apply()
assert.NoError(t, err)

// Check custom mode
finfo, err := fs.Stat(path)
assert.NoError(t, err)
assert.Equal(t, finfo.Mode(), iofs.FileMode(0755))
}

func (s *localFileFixSuite) TestLocalFile_ApplyFix_FileHasNilMode() {
fs := s.fs
t := s.T()
path := "/file1.txt"
fix := &LocalFileFix{
BaseBlock: &BaseBlock{},
Paths: []string{path},
Content: "Hello, world!",
Mode: nil,
for i := 0; i < len(cases); i++ {
sc := cases[i]
s.Run(sc.desc, func() {
var assignment = ""
if sc.mode != nil {
assignment = fmt.Sprintf("mode = %s", *sc.mode)
}
config := fmt.Sprintf(`
rule "must_be_true" "sample" {
condition = false
}

fix "local_file" "sample" {
rule_ids = [rule.must_be_true.sample.id]
paths = ["/file1.txt"]
content = "Hello world!"
%s
}
`, assignment)
s.dummyFsWithFiles([]string{"test.grept.hcl"}, []string{config})
path := "/file1.txt"
// Create the file first
c, err := NewConfig("", "", nil)
s.NoError(err)
p, err := c.Plan()
if sc.expectedErrorMessage != nil {
s.Contains(err.Error(), *sc.expectedErrorMessage)
return
}
s.NoError(err)
err = p.Apply()
s.NoError(err)

finfo, err := s.fs.Stat(path)
s.NoError(err)
s.Equal(sc.wanted, finfo.Mode())
})
}

// Create the file first
err := fix.Apply()
assert.NoError(t, err)

// Check custom mode
finfo, err := fs.Stat(path)
assert.NoError(t, err)
assert.Equal(t, finfo.Mode(), iofs.FileMode(0644))
}
19 changes: 18 additions & 1 deletion pkg/validatable.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package pkg

import (
"io/fs"
"strconv"
"strings"

"github.com/ahmetb/go-linq/v3"
"github.com/go-playground/validator/v10"
"strings"
)

var validate = validator.New(validator.WithRequiredStructEnabled())
Expand All @@ -13,12 +16,26 @@ func registerValidator() {
_ = validate.RegisterValidation("at_least_one_of", validateAtLeastOneOf)
_ = validate.RegisterValidation("required_with", validateRequiredWith)
_ = validate.RegisterValidation("all_string_in_slice", validateAllStringInSlice)
_ = validate.RegisterValidation("file_mode", validateFileMode)
}

type Validatable interface {
Validate() error
}

func validateFileMode(fl validator.FieldLevel) bool {
field := fl.Field().Interface()
num, ok := field.(fs.FileMode)
if !ok {
return false
}
mode, err := strconv.ParseUint(strconv.Itoa(int(num)), 8, 32)
if err != nil {
return false
}
return uint32(mode) <= uint32(fs.ModePerm)
}

func validateAllStringInSlice(fl validator.FieldLevel) bool {
candidatesQuery := linq.From(strings.Split(fl.Param(), " "))
parentStruct := fl.Parent()
Expand Down
Loading