Files
fission-src/pkg/fission-cli/cmd/spec/validate.go
T
Ta-Ching ChenandGitHub 93d4b88d84 Make CLI functions return error instead of fatal out (#1379)
Before this PR, CLI functions fatal out when encountering error
instead of returning it. Such behavior makes it hard to reuse 
the functions nor writing unit tests. This PR aims to make functions 
return errors instead of error out.
2019-11-05 16:50:13 +08:00

149 lines
3.9 KiB
Go

/*
Copyright 2019 The Fission Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
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 spec
import (
"bytes"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
"github.com/fission/fission/pkg/controller/client"
"github.com/fission/fission/pkg/fission-cli/cliwrapper/cli"
"github.com/fission/fission/pkg/fission-cli/util"
)
type ValidateSubCommand struct {
client *client.Client
}
// Validate parses a set of specs and checks for references to
// resources that don't exist.
func Validate(flags cli.Input) error {
c, err := util.GetServer(flags)
if err != nil {
return err
}
opts := &ValidateSubCommand{
client: c,
}
return opts.do(flags)
}
func (opts *ValidateSubCommand) do(flags cli.Input) error {
return opts.run(flags)
}
func (opts *ValidateSubCommand) run(flags cli.Input) error {
// this will error on parse errors and on duplicates
specDir := util.GetSpecDir(flags)
fr, err := ReadSpecs(specDir)
if err != nil {
return errors.Wrap(err, "error reading specs")
}
// this does the rest of the checks, like dangling refs
err = fr.Validate(flags)
if err != nil {
return errors.Wrap(err, "error validating specs")
}
return nil
}
// ReadSpecs reads all specs in the specified directory and returns a parsed set of
// fission resources.
func ReadSpecs(specDir string) (*FissionResources, error) {
// make sure spec directory exists before continue
if _, err := os.Stat(specDir); os.IsNotExist(err) {
return nil, errors.Errorf("Spec directory %v doesn't exist. "+
"Please check directory path or run \"fission spec init\" to create it.", specDir)
}
fr := FissionResources{
Packages: make([]fv1.Package, 0),
Functions: make([]fv1.Function, 0),
Environments: make([]fv1.Environment, 0),
HttpTriggers: make([]fv1.HTTPTrigger, 0),
KubernetesWatchTriggers: make([]fv1.KubernetesWatchTrigger, 0),
TimeTriggers: make([]fv1.TimeTrigger, 0),
MessageQueueTriggers: make([]fv1.MessageQueueTrigger, 0),
SourceMap: SourceMap{
Locations: make(map[string](map[string](map[string]Location))),
},
}
var result *multierror.Error
// Users can organize the specdir into subdirs if they want to.
err := filepath.Walk(specDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// For now just read YAML files. We'll add jsonnet at some point. Skip
// unsupported files.
if !(strings.HasSuffix(path, ".yaml") || strings.HasSuffix(path, ".yml")) {
return nil
}
// read
b, err := ioutil.ReadFile(path)
if err != nil {
result = multierror.Append(result, err)
return nil
}
// handle the case where there are multiple YAML docs per file. go-yaml
// doesn't support this directly, yet.
docs := bytes.Split(b, []byte("\n---"))
lines := 1
for _, doc := range docs {
d := []byte(strings.TrimSpace(string(doc)))
if len(d) != 0 {
// parse this document and add whatever is in it to fr
err = fr.ParseYaml(d, &Location{
Path: path,
Line: lines,
})
if err != nil {
// collect all errors so user can fix them all
result = multierror.Append(result, err)
}
}
// the separator occupies one line, hence the +1
lines += strings.Count(string(doc), "\n") + 1
}
return nil
})
if err != nil {
return nil, err
}
if err = result.ErrorOrNil(); err != nil {
return nil, err
}
return &fr, nil
}