Unverified Commit 3da0dc2c authored by Yishay Mendelsohn's avatar Yishay Mendelsohn Committed by GitHub
Browse files

feat: add kustomize support (#466)

Co-authored-by: default avatarNoaa Barki <noaabarki@Noaas-MacBook-Pro.local>
parent f002d316
No related merge requests found
Showing with 812 additions and 58 deletions
+812 -58
package kustomize
import (
"os"
"strings"
"github.com/datreeio/datree/cmd/test"
"github.com/datreeio/datree/pkg/executor"
"github.com/datreeio/datree/pkg/utils"
"github.com/spf13/cobra"
)
type KustomizeCommandRunner interface {
BuildCommandDescription(dir string, name string, args []string) string
RunCommand(name string, args []string) (executor.CommandOutput, error)
ExecuteKustomizeBin(args []string) ([]byte, error)
CreateTempFile(tempFilePrefix string, content []byte) (string, error)
}
type KustomizeContext struct {
CommandRunner KustomizeCommandRunner
}
func New(testCtx *test.TestCommandContext, kustomizeCtx *KustomizeContext) *cobra.Command {
testCommandFlags := test.NewTestCommandFlags()
kustomizeTestCommand := &cobra.Command{
Use: "test <args>",
Short: "Execute datree test for kustomize build <args>",
Long: "Execute datree test for kustomize build <args>. Input should be a kustomize build directory or file.",
Example: utils.Example(`
# Test the kustomize build for the current directory
datree kustomize test .
# Test the kustomize build for some shared configuration directory
datree kustomize test /home/config/production
# Test the kustomize build from github
datree kustomize test https://github.com/kubernetes-sigs/kustomize.git/examples/helloWorld?ref=v1.0.6
`),
PreRunE: func(cmd *cobra.Command, args []string) error {
return test.LoadVersionMessages(testCtx, args, cmd)
},
RunE: func(cmd *cobra.Command, args []string) error {
test.SetSilentMode(cmd)
var err error = nil
defer func() {
if err != nil {
testCtx.Printer.PrintMessage(strings.Join([]string{"\n", err.Error(), "\n"}, ""), "error")
}
}()
err = testCommandFlags.Validate()
if err != nil {
return err
}
localConfigContent, err := testCtx.LocalConfig.GetLocalConfiguration()
if err != nil {
return err
}
testCommandOptions := test.GenerateTestCommandOptions(testCommandFlags, localConfigContent)
out, err := kustomizeCtx.CommandRunner.ExecuteKustomizeBin(args)
if err != nil {
return err
}
tempFilename, err := kustomizeCtx.CommandRunner.CreateTempFile("datree_kustomize", out)
if err != nil {
return err
}
defer os.Remove(tempFilename)
err = test.Test(testCtx, []string{tempFilename}, testCommandOptions)
if err != nil {
return err
}
return nil
},
}
testCommandFlags.AddFlags(kustomizeTestCommand)
kustomizeCommand := &cobra.Command{
Use: "kustomize",
}
kustomizeCommand.AddCommand(kustomizeTestCommand)
return kustomizeCommand
}
package kustomize
import (
"testing"
"github.com/datreeio/datree/bl/evaluation"
"github.com/datreeio/datree/bl/messager"
"github.com/datreeio/datree/cmd/test"
"github.com/datreeio/datree/pkg/ciContext"
"github.com/datreeio/datree/pkg/cliClient"
"github.com/datreeio/datree/pkg/executor"
"github.com/datreeio/datree/pkg/extractor"
"github.com/datreeio/datree/pkg/localConfig"
"github.com/datreeio/datree/pkg/printer"
"github.com/stretchr/testify/mock"
)
func TestKustomizeTestCommand(t *testing.T) {
tests := []*somethingTestCase{
test_kustomize_run_method_success(),
}
for _, tt := range tests {
t.Skip(tt.name)
// t.Run(tt.name, func(t *testing.T) {
// cmd := New(tt.testCtx, tt.kustomizeCtx)
// got := cmd.RunE(cmd, tt.args)
// assert.Equal(t, tt.expected, got)
// })
}
}
type somethingTestCase struct {
name string
args []string
testCtx *test.TestCommandContext
kustomizeCtx *KustomizeContext
expected error
}
func test_kustomize_run_method_success() *somethingTestCase {
return &somethingTestCase{
name: "should return nil when kustomize run method is successful",
args: []string{"./kustomization.yaml"},
testCtx: &test.TestCommandContext{
K8sValidator: &k8sValidatorMock{},
Evaluator: &mockEvaluator{},
LocalConfig: &LocalConfigMock{},
Messager: &mockMessager{},
Printer: &PrinterMock{},
Reader: &ReaderMock{},
},
kustomizeCtx: &KustomizeContext{
CommandRunner: &mockKustomizeExecuter{},
},
expected: nil,
}
}
// --- Mocks ---------------------------------------------------------------
type mockEvaluator struct {
mock.Mock
}
func (m *mockEvaluator) Evaluate(filesConfigurationsChan []*extractor.FileConfigurations, evaluationResponse *cliClient.CreateEvaluationResponse, isInteractiveMode bool) (evaluation.ResultType, error) {
args := m.Called(filesConfigurationsChan, evaluationResponse, isInteractiveMode)
return args.Get(0).(evaluation.ResultType), args.Error(1)
}
func (m *mockEvaluator) CreateEvaluation(cliId string, cliVersion string, k8sVersion string, policyName string, ciContext *ciContext.CIContext) (*cliClient.CreateEvaluationResponse, error) {
args := m.Called(cliId, cliVersion, k8sVersion, policyName)
return args.Get(0).(*cliClient.CreateEvaluationResponse), args.Error(1)
}
func (m *mockEvaluator) UpdateFailedYamlValidation(invalidYamlFiles []*extractor.InvalidFile, evaluationId int, stopEvaluation bool) error {
args := m.Called(invalidYamlFiles, evaluationId, stopEvaluation)
return args.Error(0)
}
func (m *mockEvaluator) UpdateFailedK8sValidation(invalidK8sFiles []*extractor.InvalidFile, evaluationId int, stopEvaluation bool) error {
args := m.Called(invalidK8sFiles, evaluationId, stopEvaluation)
return args.Error(0)
}
type mockMessager struct {
mock.Mock
}
func (m *mockMessager) LoadVersionMessages(cliVersion string) chan *messager.VersionMessage {
messages := make(chan *messager.VersionMessage, 1)
m.Called(cliVersion)
return messages
}
func (m *mockMessager) HandleVersionMessage(messageChannel <-chan *messager.VersionMessage) {
m.Called(messageChannel)
}
type mockKustomizeExecuter struct {
mock.Mock
}
func (m *mockKustomizeExecuter) ExecuteKustomizeBin(args []string) ([]byte, error) {
_args := m.Called(args)
return _args.Get(0).([]byte), _args.Error(1)
}
func (m *mockKustomizeExecuter) RunCommand(name string, args []string) (executor.CommandOutput, error) {
_args := m.Called(args)
return _args.Get(0).(executor.CommandOutput), _args.Error(1)
}
func (m *mockKustomizeExecuter) BuildCommandDescription(dir string, name string, args []string) string {
_args := m.Called(dir, name)
return _args.Get(0).(string)
}
func (m *mockKustomizeExecuter) CreateTempFile(tempFilePrefix string, content []byte) (string, error) {
_args := m.Called(tempFilePrefix, content)
return _args.Get(0).(string), _args.Error(1)
}
type k8sValidatorMock struct {
mock.Mock
}
func (kv *k8sValidatorMock) ValidateResources(filesConfigurationsChan chan *extractor.FileConfigurations, concurrency int) (chan *extractor.FileConfigurations, chan *extractor.InvalidFile) {
args := kv.Called(filesConfigurationsChan, concurrency)
return args.Get(0).(chan *extractor.FileConfigurations), args.Get(1).(chan *extractor.InvalidFile)
}
func (kv *k8sValidatorMock) GetK8sFiles(filesConfigurationsChan chan *extractor.FileConfigurations, concurrency int) (chan *extractor.FileConfigurations, chan *extractor.FileConfigurations) {
args := kv.Called(filesConfigurationsChan, concurrency)
return args.Get(0).(chan *extractor.FileConfigurations), args.Get(1).(chan *extractor.FileConfigurations)
}
func (kv *k8sValidatorMock) InitClient(k8sVersion string, ignoreMissingSchemas bool, schemaLocations []string) {
}
type PrinterMock struct {
mock.Mock
}
func (p *PrinterMock) PrintWarnings(warnings []printer.Warning) {
p.Called(warnings)
}
func (p *PrinterMock) PrintSummaryTable(summary printer.Summary) {
p.Called(summary)
}
func (p *PrinterMock) PrintEvaluationSummary(evaluationSummary printer.EvaluationSummary, k8sVersion string) {
p.Called(evaluationSummary)
}
func (p *PrinterMock) PrintMessage(messageText string, messageColor string) {
p.Called(messageText, messageColor)
}
func (p *PrinterMock) PrintPromptMessage(promptMessage string) {
p.Called(promptMessage)
}
func (p *PrinterMock) SetTheme(theme *printer.Theme) {
p.Called(theme)
}
type ReaderMock struct {
mock.Mock
}
func (rm *ReaderMock) FilterFiles(paths []string) ([]string, error) {
args := rm.Called(paths)
return args.Get(0).([]string), nil
}
type LocalConfigMock struct {
mock.Mock
}
func (lc *LocalConfigMock) GetLocalConfiguration() (*localConfig.ConfigContent, error) {
lc.Called()
return &localConfig.ConfigContent{CliId: "134kh"}, nil
}
...@@ -6,12 +6,14 @@ import ( ...@@ -6,12 +6,14 @@ import (
"github.com/datreeio/datree/bl/validation" "github.com/datreeio/datree/bl/validation"
"github.com/datreeio/datree/cmd/completion" "github.com/datreeio/datree/cmd/completion"
"github.com/datreeio/datree/cmd/config" "github.com/datreeio/datree/cmd/config"
"github.com/datreeio/datree/cmd/kustomize"
"github.com/datreeio/datree/cmd/publish" "github.com/datreeio/datree/cmd/publish"
schema_validator "github.com/datreeio/datree/cmd/schema-validator" schema_validator "github.com/datreeio/datree/cmd/schema-validator"
"github.com/datreeio/datree/cmd/test" "github.com/datreeio/datree/cmd/test"
"github.com/datreeio/datree/cmd/version" "github.com/datreeio/datree/cmd/version"
"github.com/datreeio/datree/pkg/cliClient" "github.com/datreeio/datree/pkg/cliClient"
"github.com/datreeio/datree/pkg/deploymentConfig" "github.com/datreeio/datree/pkg/deploymentConfig"
"github.com/datreeio/datree/pkg/executor"
"github.com/datreeio/datree/pkg/fileReader" "github.com/datreeio/datree/pkg/fileReader"
"github.com/datreeio/datree/pkg/localConfig" "github.com/datreeio/datree/pkg/localConfig"
"github.com/datreeio/datree/pkg/printer" "github.com/datreeio/datree/pkg/printer"
...@@ -40,6 +42,16 @@ func init() { ...@@ -40,6 +42,16 @@ func init() {
K8sValidator: app.context.K8sValidator, K8sValidator: app.context.K8sValidator,
})) }))
rootCmd.AddCommand(kustomize.New(&test.TestCommandContext{
CliVersion: CliVersion,
Evaluator: app.context.Evaluator,
LocalConfig: app.context.LocalConfig,
Messager: app.context.Messager,
Printer: app.context.Printer,
Reader: app.context.Reader,
K8sValidator: app.context.K8sValidator,
}, &kustomize.KustomizeContext{CommandRunner: app.context.CommandRunner}))
rootCmd.AddCommand(version.New(&version.VersionCommandContext{ rootCmd.AddCommand(version.New(&version.VersionCommandContext{
CliVersion: CliVersion, CliVersion: CliVersion,
Messager: app.context.Messager, Messager: app.context.Messager,
...@@ -82,6 +94,7 @@ type context struct { ...@@ -82,6 +94,7 @@ type context struct {
Reader *fileReader.FileReader Reader *fileReader.FileReader
K8sValidator *validation.K8sValidator K8sValidator *validation.K8sValidator
YamlSchemaValidator *yamlSchemaValidator.YamlSchemaValidator YamlSchemaValidator *yamlSchemaValidator.YamlSchemaValidator
CommandRunner *executor.CommandRunner
} }
type app struct { type app struct {
...@@ -103,6 +116,7 @@ func startup() *app { ...@@ -103,6 +116,7 @@ func startup() *app {
Reader: fileReader.CreateFileReader(nil), Reader: fileReader.CreateFileReader(nil),
K8sValidator: validation.New(), K8sValidator: validation.New(),
YamlSchemaValidator: yamlSchemaValidator.New(), YamlSchemaValidator: yamlSchemaValidator.New(),
CommandRunner: executor.CreateNewCommandRunner(),
}, },
} }
} }
...@@ -124,6 +124,23 @@ type TestCommandContext struct { ...@@ -124,6 +124,23 @@ type TestCommandContext struct {
Reader Reader Reader Reader
} }
func LoadVersionMessages(ctx *TestCommandContext, args []string, cmd *cobra.Command) error {
outputFlag, _ := cmd.Flags().GetString("output")
if (outputFlag != "json") && (outputFlag != "yaml") && (outputFlag != "xml") {
messages := ctx.Messager.LoadVersionMessages(ctx.CliVersion)
for msg := range messages {
ctx.Printer.PrintMessage(msg.MessageText+"\n", msg.MessageColor)
}
}
return nil
}
func SetSilentMode(cmd *cobra.Command) {
cmd.SilenceUsage = true
cmd.SilenceErrors = true
}
func New(ctx *TestCommandContext) *cobra.Command { func New(ctx *TestCommandContext) *cobra.Command {
testCommandFlags := NewTestCommandFlags() testCommandFlags := NewTestCommandFlags()
testCommand := &cobra.Command{ testCommand := &cobra.Command{
...@@ -145,22 +162,17 @@ func New(ctx *TestCommandContext) *cobra.Command { ...@@ -145,22 +162,17 @@ func New(ctx *TestCommandContext) *cobra.Command {
errMessage := "Requires at least 1 arg\n" errMessage := "Requires at least 1 arg\n"
return fmt.Errorf(errMessage) return fmt.Errorf(errMessage)
} }
err := testCommandFlags.Validate()
if err != nil {
return err
}
return nil return nil
}, },
PreRunE: func(cmd *cobra.Command, args []string) error { PreRunE: func(cmd *cobra.Command, args []string) error {
outputFlag, _ := cmd.Flags().GetString("output") return LoadVersionMessages(ctx, args, cmd)
if (outputFlag != "json") && (outputFlag != "yaml") && (outputFlag != "xml") {
messages := ctx.Messager.LoadVersionMessages(ctx.CliVersion)
for msg := range messages {
ctx.Printer.PrintMessage(msg.MessageText+"\n", msg.MessageColor)
}
}
return nil
}, },
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
cmd.SilenceUsage = true SetSilentMode(cmd)
cmd.SilenceErrors = true
var err error = nil var err error = nil
defer func() { defer func() {
if err != nil { if err != nil {
...@@ -168,37 +180,6 @@ func New(ctx *TestCommandContext) *cobra.Command { ...@@ -168,37 +180,6 @@ func New(ctx *TestCommandContext) *cobra.Command {
} }
}() }()
outputFlag, err := cmd.Flags().GetString("output")
if err != nil {
return err
}
k8sVersion, err := cmd.Flags().GetString("schema-version")
if err != nil {
return err
}
ignoreMissingSchemas, err := cmd.Flags().GetBool("ignore-missing-schemas")
if err != nil {
return err
}
onlyK8sFiles, err := cmd.Flags().GetBool("only-k8s-files")
if err != nil {
return err
}
policy, err := cmd.Flags().GetString("policy")
if err != nil {
return err
}
schemaLocations, err := cmd.Flags().GetStringArray("schema-location")
if err != nil {
return err
}
testCommandFlags := TestCommandFlags{Output: outputFlag, K8sVersion: k8sVersion, IgnoreMissingSchemas: ignoreMissingSchemas, PolicyName: policy, SchemaLocations: schemaLocations, OnlyK8sFiles: onlyK8sFiles}
err = testCommandFlags.Validate() err = testCommandFlags.Validate()
if err != nil { if err != nil {
return err return err
...@@ -209,9 +190,9 @@ func New(ctx *TestCommandContext) *cobra.Command { ...@@ -209,9 +190,9 @@ func New(ctx *TestCommandContext) *cobra.Command {
return err return err
} }
testCommandOptions := generateTestCommandOptions(&testCommandFlags, localConfigContent) testCommandOptions := GenerateTestCommandOptions(testCommandFlags, localConfigContent)
err = test(ctx, args, testCommandOptions) err = Test(ctx, args, testCommandOptions)
if err != nil { if err != nil {
return err return err
} }
...@@ -238,7 +219,7 @@ func (flags *TestCommandFlags) AddFlags(cmd *cobra.Command) { ...@@ -238,7 +219,7 @@ func (flags *TestCommandFlags) AddFlags(cmd *cobra.Command) {
cmd.Flags().BoolVarP(&flags.IgnoreMissingSchemas, "ignore-missing-schemas", "", false, "Ignore missing schemas when executing schema validation step") cmd.Flags().BoolVarP(&flags.IgnoreMissingSchemas, "ignore-missing-schemas", "", false, "Ignore missing schemas when executing schema validation step")
} }
func generateTestCommandOptions(testCommandFlags *TestCommandFlags, localConfigContent *localConfig.ConfigContent) *TestCommandOptions { func GenerateTestCommandOptions(testCommandFlags *TestCommandFlags, localConfigContent *localConfig.ConfigContent) *TestCommandOptions {
k8sVersion := testCommandFlags.K8sVersion k8sVersion := testCommandFlags.K8sVersion
if k8sVersion == "" { if k8sVersion == "" {
k8sVersion = localConfigContent.SchemaVersion k8sVersion = localConfigContent.SchemaVersion
...@@ -271,7 +252,7 @@ func validateK8sVersionFormatIfProvided(k8sVersion string) error { ...@@ -271,7 +252,7 @@ func validateK8sVersionFormatIfProvided(k8sVersion string) error {
} }
} }
func test(ctx *TestCommandContext, paths []string, options *TestCommandOptions) error { func Test(ctx *TestCommandContext, paths []string, options *TestCommandOptions) error {
if paths[0] == "-" { if paths[0] == "-" {
if len(paths) > 1 { if len(paths) > 1 {
......
...@@ -243,7 +243,7 @@ func test_testCommand_version_flags_validation(t *testing.T, ctx *TestCommandCon ...@@ -243,7 +243,7 @@ func test_testCommand_version_flags_validation(t *testing.T, ctx *TestCommandCon
} }
func test_testCommand_no_flags(t *testing.T, evaluator *mockEvaluator, k8sValidator *K8sValidatorMock, filesConfigurations []*extractor.FileConfigurations, evaluationResponse *cliClient.CreateEvaluationResponse, ctx *TestCommandContext) { func test_testCommand_no_flags(t *testing.T, evaluator *mockEvaluator, k8sValidator *K8sValidatorMock, filesConfigurations []*extractor.FileConfigurations, evaluationResponse *cliClient.CreateEvaluationResponse, ctx *TestCommandContext) {
test(ctx, []string{"8/*"}, &TestCommandOptions{K8sVersion: "1.18.0", Output: "", PolicyName: "Default", Token: "134kh"}) _ = Test(ctx, []string{"8/*"}, &TestCommandOptions{K8sVersion: "1.18.0", Output: "", PolicyName: "Default", Token: "134kh"})
k8sValidator.AssertCalled(t, "ValidateResources", mock.Anything, 100) k8sValidator.AssertCalled(t, "ValidateResources", mock.Anything, 100)
evaluator.AssertCalled(t, "CreateEvaluation", "134kh", "", "1.18.0", "Default") evaluator.AssertCalled(t, "CreateEvaluation", "134kh", "", "1.18.0", "Default")
...@@ -251,28 +251,28 @@ func test_testCommand_no_flags(t *testing.T, evaluator *mockEvaluator, k8sValida ...@@ -251,28 +251,28 @@ func test_testCommand_no_flags(t *testing.T, evaluator *mockEvaluator, k8sValida
} }
func test_testCommand_json_output(t *testing.T, evaluator *mockEvaluator, k8sValidator *K8sValidatorMock, filesConfigurations []*extractor.FileConfigurations, evaluationResponse *cliClient.CreateEvaluationResponse, ctx *TestCommandContext) { func test_testCommand_json_output(t *testing.T, evaluator *mockEvaluator, k8sValidator *K8sValidatorMock, filesConfigurations []*extractor.FileConfigurations, evaluationResponse *cliClient.CreateEvaluationResponse, ctx *TestCommandContext) {
test(ctx, []string{"8/*"}, &TestCommandOptions{Output: "json"}) _ = Test(ctx, []string{"8/*"}, &TestCommandOptions{Output: "json"})
k8sValidator.AssertCalled(t, "ValidateResources", mock.Anything, 100) k8sValidator.AssertCalled(t, "ValidateResources", mock.Anything, 100)
evaluator.AssertCalled(t, "Evaluate", filesConfigurations, mock.Anything, mock.Anything) evaluator.AssertCalled(t, "Evaluate", filesConfigurations, mock.Anything, mock.Anything)
} }
func test_testCommand_yaml_output(t *testing.T, evaluator *mockEvaluator, k8sValidator *K8sValidatorMock, filesConfigurations []*extractor.FileConfigurations, evaluationResponse *cliClient.CreateEvaluationResponse, ctx *TestCommandContext) { func test_testCommand_yaml_output(t *testing.T, evaluator *mockEvaluator, k8sValidator *K8sValidatorMock, filesConfigurations []*extractor.FileConfigurations, evaluationResponse *cliClient.CreateEvaluationResponse, ctx *TestCommandContext) {
test(ctx, []string{"8/*"}, &TestCommandOptions{Output: "yaml"}) _ = Test(ctx, []string{"8/*"}, &TestCommandOptions{Output: "yaml"})
k8sValidator.AssertCalled(t, "ValidateResources", mock.Anything, 100) k8sValidator.AssertCalled(t, "ValidateResources", mock.Anything, 100)
evaluator.AssertCalled(t, "Evaluate", filesConfigurations, mock.Anything, mock.Anything) evaluator.AssertCalled(t, "Evaluate", filesConfigurations, mock.Anything, mock.Anything)
} }
func test_testCommand_xml_output(t *testing.T, evaluator *mockEvaluator, k8sValidator *K8sValidatorMock, filesConfigurations []*extractor.FileConfigurations, evaluationResponse *cliClient.CreateEvaluationResponse, ctx *TestCommandContext) { func test_testCommand_xml_output(t *testing.T, evaluator *mockEvaluator, k8sValidator *K8sValidatorMock, filesConfigurations []*extractor.FileConfigurations, evaluationResponse *cliClient.CreateEvaluationResponse, ctx *TestCommandContext) {
test(ctx, []string{"8/*"}, &TestCommandOptions{Output: "xml"}) _ = Test(ctx, []string{"8/*"}, &TestCommandOptions{Output: "xml"})
k8sValidator.AssertCalled(t, "ValidateResources", mock.Anything, 100) k8sValidator.AssertCalled(t, "ValidateResources", mock.Anything, 100)
evaluator.AssertCalled(t, "Evaluate", filesConfigurations, mock.Anything, mock.Anything) evaluator.AssertCalled(t, "Evaluate", filesConfigurations, mock.Anything, mock.Anything)
} }
func test_testCommand_only_k8s_files(t *testing.T, k8sValidator *K8sValidatorMock, filesConfigurations []*extractor.FileConfigurations, evaluationId int, ctx *TestCommandContext) { func test_testCommand_only_k8s_files(t *testing.T, k8sValidator *K8sValidatorMock, filesConfigurations []*extractor.FileConfigurations, evaluationId int, ctx *TestCommandContext) {
test(ctx, []string{"8/*"}, &TestCommandOptions{OnlyK8sFiles: true}) _ = Test(ctx, []string{"8/*"}, &TestCommandOptions{OnlyK8sFiles: true})
k8sValidator.AssertCalled(t, "ValidateResources", mock.Anything, 100) k8sValidator.AssertCalled(t, "ValidateResources", mock.Anything, 100)
k8sValidator.AssertCalled(t, "GetK8sFiles", mock.Anything, 100) k8sValidator.AssertCalled(t, "GetK8sFiles", mock.Anything, 100)
......
...@@ -22,6 +22,8 @@ require ( ...@@ -22,6 +22,8 @@ require (
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
) )
require sigs.k8s.io/yaml v1.2.0 // indirect
require ( require (
github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect github.com/StackExchange/wmi v0.0.0-20210224194228-fe8f1750fd46 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
...@@ -38,7 +40,7 @@ require ( ...@@ -38,7 +40,7 @@ require (
github.com/pelletier/go-toml v1.2.0 // indirect github.com/pelletier/go-toml v1.2.0 // indirect
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/afero v1.1.2 // indirect github.com/spf13/afero v1.8.1 // indirect
github.com/spf13/cast v1.3.0 // indirect github.com/spf13/cast v1.3.0 // indirect
github.com/spf13/jwalterweatherman v1.0.0 // indirect github.com/spf13/jwalterweatherman v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
...@@ -48,8 +50,8 @@ require ( ...@@ -48,8 +50,8 @@ require (
github.com/tklauser/numcpus v0.2.2 // indirect github.com/tklauser/numcpus v0.2.2 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 // indirect golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
golang.org/x/text v0.3.3 // indirect golang.org/x/text v0.3.7 // indirect
gopkg.in/ini.v1 v1.51.0 // indirect gopkg.in/ini.v1 v1.51.0 // indirect
) )
......
This diff is collapsed.
package executor
import (
"bytes"
"errors"
"fmt"
"os"
"os/exec"
)
// CommandOutput is RunCommand result object
type CommandOutput struct {
ResultOutput bytes.Buffer
ErrorOutput bytes.Buffer
}
type CommandRunner struct {
}
func CreateNewCommandRunner() *CommandRunner {
return &CommandRunner{}
}
// BuildCommandDescription executes Cmd strcuts and returns a human-readable description of it
func (c *CommandRunner) BuildCommandDescription(dir string, name string, args []string) string {
command := exec.Command(name, args...)
return command.String()
}
// RunCommand executes Cmd struct with given named program and arguments and returns CommandOutput
func (c *CommandRunner) RunCommand(name string, args []string) (CommandOutput, error) {
var commandOutputBuffer, commandErrorBuffer bytes.Buffer
command := exec.Command(name, args...)
command.Stdout = &commandOutputBuffer
command.Stderr = &commandErrorBuffer
err := command.Run()
if err != nil {
return CommandOutput{
ResultOutput: commandOutputBuffer,
ErrorOutput: commandErrorBuffer,
}, fmt.Errorf("command output:%s, err:%s", commandOutputBuffer.String(), commandErrorBuffer.String())
}
return CommandOutput{
ResultOutput: commandOutputBuffer,
ErrorOutput: commandErrorBuffer,
}, nil
}
func (c *CommandRunner) ExecuteKustomizeBin(args []string) ([]byte, error) {
if c.commandExists("kustomize") {
commandOutput, err := c.RunCommand("kustomize", append([]string{"build"}, args...))
if err != nil {
return nil, fmt.Errorf("kustomize build errored: %s",
commandOutput.ErrorOutput.String())
}
return commandOutput.ResultOutput.Bytes(), nil
} else if c.commandExists("kubectl") {
commandOutput, err := c.RunCommand("kubectl", append([]string{"kustomize"}, args...))
if err != nil {
return nil, fmt.Errorf("kubectl kustomize errored: %s",
commandOutput.ErrorOutput.String())
}
return commandOutput.ResultOutput.Bytes(), nil
} else {
return nil, errors.New("kubectl or kustomize is not installed")
}
}
func (c *CommandRunner) commandExists(cmd string) bool {
_, err := exec.LookPath(cmd)
return err == nil
}
func (c *CommandRunner) CreateTempFile(tempFilePrefix string, content []byte) (string, error) {
tempFile, err := os.CreateTemp("", fmt.Sprintf("%s_*.yaml", tempFilePrefix))
if err != nil {
return "", err
}
_, err = tempFile.Write(content)
if err != nil {
return "", err
}
return tempFile.Name(), nil
}
package executor
import (
"errors"
"reflect"
"testing"
"github.com/stretchr/testify/mock"
)
func TestCommandRunner_RunCommand(t *testing.T) {
type args struct {
name string
args []string
}
tests := []struct {
name string
c *CommandRunner
args args
want CommandOutput
wantErr bool
}{
// TODO: Add test cases.
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &CommandRunner{}
got, err := c.RunCommand(tt.args.name, tt.args.args)
if (err != nil) != tt.wantErr {
t.Errorf("CommandRunner.RunCommand() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("CommandRunner.RunCommand() = %v, want %v", got, tt.want)
}
})
}
}
type commandRunnerMock struct {
mock.Mock
}
func (c *commandRunnerMock) RunCommand(name string, arguments []string) (CommandOutput, error) {
args := c.Called(name, arguments)
return args.Get(0).(CommandOutput), args.Error(1)
}
func TestCommandRunner_ExecuteKustomizeBin(t *testing.T) {
commandRunner := new(commandRunnerMock)
commandRunner.On("RunCommand", "kustomize", []string{"build"}).Return(CommandOutput{})
type args struct {
args []string
}
tests := []struct {
name string
c *CommandRunner
args args
want []byte
wantErr bool
err error
}{
{
name: "should return error if kustomize or kubectl is not installed",
c: &CommandRunner{},
args: args{
args: []string{"bad_arg"},
},
want: nil,
wantErr: true,
err: errors.New("kubectl or kustomize is not installed"),
},
{
name: "should return error if kustomize or kubectl is not installed",
c: CreateNewCommandRunner(),
args: args{
args: []string{"bad_arg"},
},
want: nil,
wantErr: true,
err: errors.New("kubectl or kustomize is not installed"),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &CommandRunner{}
got, err := c.ExecuteKustomizeBin(tt.args.args)
if err != nil {
if !tt.wantErr {
t.Errorf("CommandRunner.ExecuteKustomizeBin() error = %v, wantErr %v", err, tt.wantErr)
return
}
} else {
if !reflect.DeepEqual(err, tt.err) {
t.Errorf("CommandRunner.ExecuteKustomizeBin() = err %v, want %v", err, tt.err)
}
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("CommandRunner.ExecuteKustomizeBin() = %v, want %v", got, tt.want)
}
})
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment