Skip to content

Commit

Permalink
Make passing of files and directories as arguments cohesive across os…
Browse files Browse the repository at this point in the history
…lo commands (#221)

Co-authored-by: Filip Piwowarczyk <[email protected]>
  • Loading branch information
programmer04 and fpiwowarczyk authored Mar 23, 2023
1 parent 4aec85d commit 31ba9f5
Show file tree
Hide file tree
Showing 15 changed files with 262 additions and 66 deletions.
41 changes: 13 additions & 28 deletions cmd/oslo/convert/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,17 @@ package convert

import (
"fmt"
"os"
"path/filepath"

"github.com/spf13/cobra"

"github.com/OpenSLO/oslo/internal/pkg/convert"
"github.com/OpenSLO/oslo/pkg/discoverfiles"
)

// NewConvertCmd returns a new command for formatting a file.
func NewConvertCmd() *cobra.Command {
var files []string
var directory string
var passedFilePaths []string
var recursive bool
var format string
var project string

Expand All @@ -50,44 +49,34 @@ You can also convert a directory of files:
oslo convert -d path/to/directory -o nobl9
The output is written to standard output. If you want to write to a file, you can redirect the output:
The output is written to standard output. If you want to write to a file, you can redirect the output:
oslo convert -f file.yaml -o nobl9 > output.yaml
`,
Args: cobra.MinimumNArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
// If a directory is provided, read all files in the directory.
if directory != "" {
dirFiles, err := os.ReadDir(directory)
if err != nil {
return err
}
for _, file := range dirFiles {
files = append(files, filepath.Join(directory, file.Name()))
}
discoveredFilePaths, err := discoverfiles.DiscoverFilePaths(passedFilePaths, recursive)
if err != nil {
return err
}

// Remove duplicates from the list of files so we are only
// processing each file once.
files = convert.RemoveDuplicates(files)

// Convert the files to the specified format.
discoveredFilePaths = convert.RemoveDuplicates(discoveredFilePaths)
switch format {
case "nobl9":
if err := convert.Nobl9(cmd.OutOrStdout(), files, project); err != nil {
if err := convert.Nobl9(cmd.OutOrStdout(), discoveredFilePaths, project); err != nil {
return err
}
default:
return fmt.Errorf("unsupported format: %s", format)
}

return nil
},
}

convertCmd.Flags().StringArrayVarP(&files, "file", "f", []string{}, "The file(s) to format.")
convertCmd.Flags().StringVarP(&directory, "directory", "d", "", "The directory to format.")
discoverfiles.RegisterFileRelatedFlags(convertCmd, &passedFilePaths, &recursive)
convertCmd.Flags().StringVarP(&format, "output", "o", "", "The output format to convert to.")
if err := convertCmd.MarkFlagRequired("output"); err != nil {
panic(err)
}
convertCmd.Flags().StringVarP(
&project,
"project",
Expand All @@ -96,9 +85,5 @@ The output is written to standard output. If you want to write to a file, you c
"Used for nobl9 output. What project to assign the resources to.",
)

if err := convertCmd.MarkFlagRequired("output"); err != nil {
panic(err)
}

return convertCmd
}
24 changes: 14 additions & 10 deletions cmd/oslo/fmt/fmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,29 @@ limitations under the License.
package fmt

import (
"fmt"
"os"

"github.com/spf13/cobra"

fmtCmd "github.com/OpenSLO/oslo/internal/pkg/fmt"
"github.com/OpenSLO/oslo/internal/pkg/fmt"
"github.com/OpenSLO/oslo/pkg/discoverfiles"
)

// NewFmtCmd returns a new command for formatting a file.
func NewFmtCmd() *cobra.Command {
return &cobra.Command{
var passedFilePaths []string
var recursive bool

fmtCmd := &cobra.Command{
Use: "fmt",
Short: "Formats the provided input into the standard format.",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
if err := fmtCmd.File(cmd.OutOrStdout(), args[0]); err != nil {
fmt.Fprintln(cmd.ErrOrStderr(), err)
os.Exit(1)
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
discoveredFilePaths, err := discoverfiles.DiscoverFilePaths(passedFilePaths, recursive)
if err != nil {
return err
}
return fmt.Files(cmd.OutOrStdout(), discoveredFilePaths)
},
}
discoverfiles.RegisterFileRelatedFlags(fmtCmd, &passedFilePaths, &recursive)
return fmtCmd
}
9 changes: 7 additions & 2 deletions cmd/oslo/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
//nolint:lll
package cmd

import (
Expand Down Expand Up @@ -45,7 +46,9 @@ Usage:
oslo validate [flags]
Flags:
-h, --help help for validate
-f, --file stringArray The file(s) that contain the configurations.
-h, --help help for validate
-R, --recursive Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.
`,
wantErr: false,
},
Expand All @@ -58,7 +61,9 @@ Usage:
oslo fmt [flags]
Flags:
-h, --help help for fmt
-f, --file stringArray The file(s) that contain the configurations.
-h, --help help for fmt
-R, --recursive Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.
`,
wantErr: false,
},
Expand Down
19 changes: 14 additions & 5 deletions cmd/oslo/validate/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,31 @@ import (
"github.com/spf13/cobra"

"github.com/OpenSLO/oslo/internal/pkg/validate"
"github.com/OpenSLO/oslo/pkg/discoverfiles"
)

// NewValidateCmd returns a new cobra.Command for the validate command.
func NewValidateCmd() *cobra.Command {
return &cobra.Command{
var passedFilePaths []string
var recursive bool

validateCmd := &cobra.Command{
Use: "validate",
Short: "Validates your yaml file against the OpenSLO spec.",
Long: `Validates your yaml file against the OpenSLO spec.`,
Args: cobra.MinimumNArgs(1),
Args: cobra.MinimumNArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
if e := validate.Files(args); e != nil {
return e
discoveredFilePaths, err := discoverfiles.DiscoverFilePaths(passedFilePaths, recursive)
if err != nil {
return err
}
if err := validate.Files(discoveredFilePaths); err != nil {
return err
}

fmt.Println("Valid!")
return nil
},
}
discoverfiles.RegisterFileRelatedFlags(validateCmd, &passedFilePaths, &recursive)
return validateCmd
}
18 changes: 16 additions & 2 deletions internal/pkg/fmt/fmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,22 @@ import (
"github.com/OpenSLO/oslo/internal/pkg/yamlutils"
)

// File formats a single file and writes it to the provided writer.
func File(out io.Writer, source string) error {
// Files formats multiple files and writes it to the provided writer, separated with "---".
func Files(out io.Writer, sources []string) error {
count := len(sources)
for i := 0; i < count; i++ {
if err := file(out, sources[i]); err != nil {
return err
}
if i != count-1 {
fmt.Fprintln(out, "---")
}
}
return nil
}

// file formats a single file and writes it to the provided writer.
func file(out io.Writer, source string) error {
// Get the file contents.
content, err := yamlutils.ReadConf(source)
if err != nil {
Expand Down
52 changes: 33 additions & 19 deletions internal/pkg/fmt/fmt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,50 +13,64 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fmt
package fmt_test

import (
"bytes"
"testing"

"github.com/stretchr/testify/assert"

"github.com/OpenSLO/oslo/internal/pkg/fmt"
)

func Test_fmtFile(t *testing.T) {
func TestFiles(t *testing.T) {
t.Parallel()
type args struct {
source string
}
tests := []struct {
name string
args args
files []string
wantOut string
wantErr bool
}{
{
name: "Invalid file",
args: args{
source: "../../../test/v1alpha/invalid-file.yaml",
},
name: "Invalid file",
files: []string{"../../../test/v1alpha/invalid-file.yaml"},
wantErr: true,
wantOut: "",
},
{
name: "Invalid content",
args: args{
source: "../../../test/v1alpha/invalid-service.yaml",
},
name: "Invalid content",
files: []string{"../../../test/v1alpha/invalid-service.yaml"},
wantErr: true,
wantOut: "",
},
{
name: "Passes",
args: args{
source: "../../../test/v1alpha/valid-service.yaml",
},
name: "Passes single file",
files: []string{"../../../test/v1alpha/valid-service.yaml"},
wantErr: false,
wantOut: `apiVersion: openslo/v1alpha
kind: Service
metadata:
name: my-rad-service
displayName: My Rad Service
spec:
description: This is a great description of an even better service.
`,
},
{
name: "Passes multiple files",
files: []string{"../../../test/v1alpha/valid-service.yaml", "../../../test/v1alpha/valid-service.yaml"},
wantErr: false,
wantOut: `apiVersion: openslo/v1alpha
kind: Service
metadata:
name: my-rad-service
displayName: My Rad Service
spec:
description: This is a great description of an even better service.
---
apiVersion: openslo/v1alpha
kind: Service
metadata:
name: my-rad-service
displayName: My Rad Service
Expand All @@ -70,7 +84,7 @@ spec:
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
out := &bytes.Buffer{}
if err := File(out, tt.args.source); (err != nil) != tt.wantErr {
if err := fmt.Files(out, tt.files); (err != nil) != tt.wantErr {
t.Errorf("fmtFile() error = %v, wantErr %v", err, tt.wantErr)
return
}
Expand Down
84 changes: 84 additions & 0 deletions pkg/discoverfiles/discoverfiles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package discoverfiles

import (
"io/fs"
"os"
"path"
"path/filepath"

"github.com/spf13/cobra"
)

// RegisterFileRelatedFlags registers flags --file | -f and --recursive | -R for command
// passed as the argument and make them required.
func RegisterFileRelatedFlags(cmd *cobra.Command, filePaths *[]string, recursive *bool) {
const fileFlag = "file"
cmd.Flags().StringArrayVarP(
filePaths, fileFlag, "f", []string{},
"The file(s) that contain the configurations.",
)
if err := cmd.MarkFlagRequired(fileFlag); err != nil {
panic(err)
}
cmd.Flags().BoolVarP(
recursive, "recursive", "R", false,
"Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.", //nolint:lll
)
}

// DiscoverFilePaths returns all file paths that come from file paths provided as the argument.
// Return directly if they are standard files. For directories list all files available in its
// root or recursively traverse all subdirectories and find files in them when the argument recursive
// is true. For path "-" that indicates standard input return it directly in the same way as other paths.
func DiscoverFilePaths(filePaths []string, recursive bool) ([]string, error) { //nolint:cyclop
var discoveredPaths []string
for _, p := range filePaths {
// Indicates that a file should be read from standard input.
// Code that consumes file paths needs to handle "-"
// by reading from os.Stdin in such case.
if p == "-" {
discoveredPaths = append(discoveredPaths, p)
continue
}

// When path is valid and it's not a directory, use it directly.
fInfo, err := os.Stat(p)
if err != nil {
return nil, err
}
if !fInfo.IsDir() {
discoveredPaths = append(discoveredPaths, p)
continue
}

// When recursive is true and the path is a directory,
// discover all files in it and its subdirectories.
if recursive {
if walkErr := filepath.Walk(p, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
discoveredPaths = append(discoveredPaths, path)
}
return nil
}); walkErr != nil {
return nil, walkErr
}
continue
}

// When recursive is false and the path is a directory,
// get only paths for files in the root of it.
entries, err := os.ReadDir(p)
if err != nil {
return nil, err
}
for _, e := range entries {
if !e.IsDir() {
discoveredPaths = append(discoveredPaths, path.Join(p, e.Name()))
}
}
}
return discoveredPaths, nil
}
Loading

0 comments on commit 31ba9f5

Please sign in to comment.