Skip to content

Commit

Permalink
Add --continue-on-error support for the grr export command
Browse files Browse the repository at this point in the history
  • Loading branch information
K-Phoen committed Jan 17, 2025
1 parent d2a1caa commit 478cd70
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 53 deletions.
23 changes: 19 additions & 4 deletions cmd/grr/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,15 +427,18 @@ func serveCmd(registry grizzly.Registry) *cli.Command {

func exportCmd(registry grizzly.Registry) *cli.Command {
cmd := &cli.Command{
Use: "export <resource-path> <dashboard-dir>",
Use: "export <resource-path> <export-dir>",
Short: "render resources and save to a directory",
Args: cli.ArgsExact(2),
}
var opts Opts
var continueOnError bool

cmd.Flags().BoolVarP(&continueOnError, "continue-on-error", "e", false, "don't stop exporting on error")

cmd.Run = func(cmd *cli.Command, args []string) error {
resourcePath := args[0]
dashboardDir := args[1]
exportDir := args[1]
resourceKind, folderUID, err := getOnlySpec(opts)
if err != nil {
return err
Expand All @@ -448,7 +451,7 @@ func exportCmd(registry grizzly.Registry) *cli.Command {

targets := currentContext.GetTargets(opts.Targets)

resources, err := grizzly.DefaultParser(registry, targets, opts.JsonnetPaths).Parse(resourcePath, grizzly.ParserOptions{
resources, err := grizzly.DefaultParser(registry, targets, opts.JsonnetPaths, grizzly.ParserContinueOnError(continueOnError)).Parse(resourcePath, grizzly.ParserOptions{
DefaultResourceKind: resourceKind,
DefaultFolderUID: folderUID,
})
Expand All @@ -461,7 +464,19 @@ func exportCmd(registry grizzly.Registry) *cli.Command {
return err
}

return grizzly.Export(registry, dashboardDir, resources, onlySpec, format)
eventsRecorder := getEventsRecorder(opts)

err = grizzly.Export(eventsRecorder, registry, exportDir, resources, onlySpec, format, continueOnError)

notifier.Info(nil, eventsRecorder.Summary().AsString("resource"))

// errors are already displayed by the `eventsRecorder`, so we return a
// "silent" one to ensure that the exit code will be non-zero
if err != nil {
return silentError{Err: err}
}

return nil
}
cmd = initialiseOnlySpec(cmd, &opts)
return initialiseCmd(cmd, &opts)
Expand Down
17 changes: 17 additions & 0 deletions internal/utils/fs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package utils

import (
"os"
)

func EnsureDirectoryExists(directory string, perm os.FileMode) error {
_, err := os.Stat(directory)
if err == nil {
return nil
}
if !os.IsNotExist(err) {
return err
}

return os.MkdirAll(directory, perm)
}
7 changes: 3 additions & 4 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"strconv"
"strings"

"github.com/grafana/grizzly/internal/utils"
"github.com/kirsle/configdir"
"github.com/spf13/viper"
"gopkg.in/yaml.v3"
Expand Down Expand Up @@ -342,10 +343,8 @@ func Write() error {
// Ensure that our configuration directory exists: viper only takes care of
// creating the file.
configDir := configdir.LocalConfig("grizzly")
if _, err := os.Stat(configDir); os.IsNotExist(err) {
if err := os.MkdirAll(configDir, 0700); err != nil {
return err
}
if err := utils.EnsureDirectoryExists(configDir, 0700); err != nil {
return err
}

// Viper failed because no configuration file exists in the "config path".
Expand Down
10 changes: 0 additions & 10 deletions pkg/grizzly/notifier/notifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package notifier

import (
"fmt"
"os"

"github.com/fatih/color"
)
Expand Down Expand Up @@ -53,15 +52,6 @@ func Info(obj fmt.Stringer, msg string) {
}
}

// Info announces a message in green (to stderr)
func InfoStderr(obj fmt.Stringer, msg string) {
if obj == nil {
os.Stderr.WriteString(green(msg) + "\n")
} else {
os.Stderr.WriteString(fmt.Sprintf("%s %s\n", obj.String(), green(msg)))
}
}

// Warn announces a message in yellow
func Warn(obj fmt.Stringer, msg string) {
if obj == nil {
Expand Down
95 changes: 60 additions & 35 deletions pkg/grizzly/workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strings"
"text/tabwriter"

"github.com/grafana/grizzly/internal/utils"
"github.com/grafana/grizzly/pkg/grizzly/notifier"
"github.com/grafana/grizzly/pkg/term"
"github.com/hashicorp/go-multierror"
Expand Down Expand Up @@ -525,50 +526,74 @@ func Watch(registry Registry, watchDir string, resourcePath string, parser Parse
}

// Export renders Jsonnet resources then saves them to a directory
func Export(registry Registry, exportDir string, resources Resources, onlySpec bool, outputFormat string) error {
if _, err := os.Stat(exportDir); os.IsNotExist(err) {
err = os.Mkdir(exportDir, 0755)
if err != nil {
return err
}
func Export(eventsRecorder EventsRecorder, registry Registry, exportDir string, resources Resources, onlySpec bool, outputFormat string, continueOnError bool) error {
if err := utils.EnsureDirectoryExists(exportDir, 0755); err != nil {
return err
}

var finalErr error
for _, resource := range resources.AsList() {
updatedResourceBytes, _, extension, err := Format(registry, "", &resource, outputFormat, onlySpec)
err := exportResource(eventsRecorder, registry, exportDir, resource, onlySpec, outputFormat)
if err != nil {
return err
}
finalErr = multierror.Append(finalErr, err)

dir := fmt.Sprintf("%s/%s", exportDir, resource.Kind())
if _, err := os.Stat(dir); os.IsNotExist(err) {
err = os.Mkdir(dir, 0755)
if err != nil {
return err
}
}
path := fmt.Sprintf("%s/%s.%s", dir, resource.Name(), extension)
eventsRecorder.Record(Event{
Type: ResourceFailure,
ResourceRef: resource.Ref().String(),
Details: err.Error(),
})

existingResourceBytes, err := os.ReadFile(path)
isNotExist := os.IsNotExist(err)
if err != nil && !isNotExist {
return err
}
updatedResource := string(updatedResourceBytes)
existingResource := string(existingResourceBytes)
if existingResource == updatedResource {
notifier.NoChanges(resource)
} else {
err = os.WriteFile(path, []byte(updatedResource), 0644)
if err != nil {
return err
}
if isNotExist {
notifier.Added(resource)
} else {
notifier.Updated(resource)
if !continueOnError {
return finalErr
}
}
}

return finalErr
}

func exportResource(eventsRecorder EventsRecorder, registry Registry, exportDir string, resource Resource, onlySpec bool, outputFormat string) error {
updatedResourceBytes, _, extension, err := Format(registry, "", &resource, outputFormat, onlySpec)
if err != nil {
return err
}

dir := fmt.Sprintf("%s/%s", exportDir, resource.Kind())
if err := utils.EnsureDirectoryExists(dir, 0755); err != nil {
return err
}

path := fmt.Sprintf("%s/%s.%s", dir, resource.Name(), extension)

existingResourceBytes, err := os.ReadFile(path)
isNotExist := os.IsNotExist(err)
if err != nil && !isNotExist {
return err
}

if string(existingResourceBytes) == string(updatedResourceBytes) {
eventsRecorder.Record(Event{
Type: ResourceNotChanged,
ResourceRef: resource.Ref().String(),
})
return nil
}

err = os.WriteFile(path, updatedResourceBytes, 0644)
if err != nil {
return err
}

eventType := ResourceUpdated
if isNotExist {
eventType = ResourceAdded
}

eventsRecorder.Record(Event{
Type: eventType,
ResourceRef: resource.Ref().String(),
})

return nil
}

Expand Down

0 comments on commit 478cd70

Please sign in to comment.