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.
This commit is contained in:
Ta-Ching Chen
2019-11-05 16:50:13 +08:00
committed by GitHub
parent b0d27ee5d2
commit 93d4b88d84
72 changed files with 894 additions and 632 deletions
+44 -17
View File
@@ -34,10 +34,10 @@ import (
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/cmd"
pkgutil "github.com/fission/fission/pkg/fission-cli/cmd/package/util"
spectypes "github.com/fission/fission/pkg/fission-cli/cmd/spec/types"
"github.com/fission/fission/pkg/fission-cli/log"
"github.com/fission/fission/pkg/fission-cli/consolemsg"
"github.com/fission/fission/pkg/fission-cli/util"
"github.com/fission/fission/pkg/types"
"github.com/fission/fission/pkg/utils"
@@ -57,8 +57,12 @@ type ApplySubCommand struct {
// etc, while doing an apply, they will get a partially applied deployment. However,
// they can retry their apply command once they're back online.
func Apply(flags cli.Input) error {
c, err := util.GetServer(flags)
if err != nil {
return err
}
opts := ApplySubCommand{
client: cmd.GetServer(flags),
client: c,
}
return opts.do(flags)
}
@@ -68,7 +72,7 @@ func (opts *ApplySubCommand) do(flags cli.Input) error {
}
func (opts *ApplySubCommand) run(flags cli.Input) error {
specDir := cmd.GetSpecDir(flags)
specDir := util.GetSpecDir(flags)
deleteResources := flags.Bool("delete")
watchResources := flags.Bool("watch")
@@ -85,35 +89,50 @@ func (opts *ApplySubCommand) run(flags cli.Input) error {
if watchResources {
var err error
watcher, err = fsnotify.NewWatcher()
util.CheckErr(err, "create file watcher")
if err != nil {
return errors.Wrap(err, "error creating file watcher")
}
// add watches
rootDir := filepath.Clean(specDir + "/..")
err = filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error {
util.CheckErr(err, "scan project files")
if err != nil {
return errors.Wrap(err, "error scanning project files")
}
if ignoreFile(path) {
return nil
}
err = watcher.Add(path)
util.CheckErr(err, fmt.Sprintf("watch path %v", path))
if err != nil {
return errors.Wrap(err, fmt.Sprintf("error watching path %v", path))
}
return nil
})
util.CheckErr(err, "scan files to watch")
if err != nil {
return errors.Wrap(err, "error scanning files to watch")
}
}
for {
// read all specs
fr, err := ReadSpecs(specDir)
util.CheckErr(err, "read specs")
if err != nil {
return errors.Wrap(err, "error reading specs")
}
// validate
err = fr.Validate(flags)
util.CheckErr(err, "validate specs")
if err != nil {
return errors.Wrap(err, "error validating specs")
}
// make changes to the cluster based on the specs
pkgMetas, as, err := applyResources(opts.client, specDir, fr, deleteResources)
util.CheckErr(err, "apply specs")
if err != nil {
return errors.Wrap(err, "error applying specs")
}
printApplyStatus(as)
if watchResources || waitForBuild {
@@ -146,7 +165,6 @@ func (opts *ApplySubCommand) run(flags cli.Input) error {
if ignoreFile(e.Name) {
continue waitloop
}
fmt.Printf("Noticed a file change, reapplying specs...\n")
// Builds that finish after this cancellation will be
@@ -154,11 +172,17 @@ func (opts *ApplySubCommand) run(flags cli.Input) error {
pkgWatchCancel()
err = waitForFileWatcherToSettleDown(watcher)
util.CheckErr(err, "watching files")
if err != nil {
return errors.Wrap(err, "error watching files")
}
break waitloop
case err := <-watcher.Errors:
util.CheckErr(err, "watching files")
pkgWatchCancel()
if err != nil {
return errors.Wrap(err, "error watching files")
}
}
}
}
@@ -406,7 +430,7 @@ func localArchiveFromSpec(specDir string, aus *spectypes.ArchiveUploadSpec) (*fv
absGlob := rootDir + "/" + relativeGlob
f, err := filepath.Glob(absGlob)
if err != nil {
log.Info(fmt.Sprintf("Invalid glob in archive %v: %v", aus.Name, relativeGlob))
consolemsg.Info(fmt.Sprintf("Invalid glob in archive %v: %v", aus.Name, relativeGlob))
return nil, err
}
files = append(files, f...)
@@ -454,7 +478,10 @@ func localArchiveFromSpec(specDir string, aus *spectypes.ArchiveUploadSpec) (*fv
// figure out if we're making a literal or a URL-based archive
if size < types.ArchiveLiteralSizeLimit {
contents := pkgutil.GetContents(archiveFileName)
contents, err := pkgutil.GetContents(archiveFileName)
if err != nil {
return nil, err
}
return &fv1.Archive{
Type: fv1.ArchiveTypeLiteral,
Literal: contents,
+5 -2
View File
@@ -19,13 +19,13 @@ package spec
import (
"context"
"fmt"
"os"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
"github.com/fission/fission/pkg/controller/client"
"github.com/fission/fission/pkg/fission-cli/util"
"github.com/fission/fission/pkg/types"
)
@@ -68,7 +68,10 @@ func (w *packageBuildWatcher) watch(ctx context.Context) {
// pull list of packages (TODO: convert to watch)
pkgs, err := w.fclient.PackageList(metav1.NamespaceAll)
util.CheckErr(err, "Getting list of packages")
if err != nil {
fmt.Printf("Getting list of packages: %v", err)
os.Exit(1)
}
// find packages that (a) are in the app spec and (b) have an interesting
// build status (either succeeded or failed; not "none")
+14 -5
View File
@@ -17,9 +17,10 @@ limitations under the License.
package spec
import (
"github.com/pkg/errors"
"github.com/fission/fission/pkg/controller/client"
"github.com/fission/fission/pkg/fission-cli/cliwrapper/cli"
"github.com/fission/fission/pkg/fission-cli/cmd"
"github.com/fission/fission/pkg/fission-cli/util"
)
@@ -29,8 +30,12 @@ type DestroySubCommand struct {
// Destroy destroys everything in the spec.
func Destroy(flags cli.Input) error {
c, err := util.GetServer(flags)
if err != nil {
return err
}
opts := &DestroySubCommand{
client: cmd.GetServer(flags),
client: c,
}
return opts.do(flags)
}
@@ -41,11 +46,13 @@ func (opts *DestroySubCommand) do(flags cli.Input) error {
func (opts *DestroySubCommand) run(flags cli.Input) error {
// get specdir
specDir := cmd.GetSpecDir(flags)
specDir := util.GetSpecDir(flags)
// read everything
fr, err := ReadSpecs(specDir)
util.CheckErr(err, "read specs")
if err != nil {
return errors.Wrap(err, "error reading specs")
}
// set desired state to nothing, but keep the UID so "apply" can find it
emptyFr := FissionResources{}
@@ -53,7 +60,9 @@ func (opts *DestroySubCommand) run(flags cli.Input) error {
// "apply" the empty state
_, _, err = applyResources(opts.client, specDir, &emptyFr, true)
util.CheckErr(err, "delete resources")
if err != nil {
return errors.Wrap(err, "error deleting resources")
}
return nil
}
+17 -7
View File
@@ -23,11 +23,11 @@ import (
"path/filepath"
"github.com/ghodss/yaml"
"github.com/pkg/errors"
uuid "github.com/satori/go.uuid"
"github.com/fission/fission/pkg/controller/client"
"github.com/fission/fission/pkg/fission-cli/cliwrapper/cli"
"github.com/fission/fission/pkg/fission-cli/cmd"
spectypes "github.com/fission/fission/pkg/fission-cli/cmd/spec/types"
"github.com/fission/fission/pkg/fission-cli/util"
)
@@ -38,8 +38,12 @@ type InitSubCommand struct {
}
func Init(flags cli.Input) error {
c, err := util.GetServer(flags)
if err != nil {
return err
}
opts := InitSubCommand{
client: cmd.GetServer(flags),
client: c,
}
return opts.do(flags)
}
@@ -54,13 +58,15 @@ func (opts *InitSubCommand) do(flags cli.Input) error {
func (opts *InitSubCommand) complete(flags cli.Input) error {
// Figure out spec directory
specDir := cmd.GetSpecDir(flags)
specDir := util.GetSpecDir(flags)
name := flags.String("name")
if len(name) == 0 {
// come up with a name using the current dir
dir, err := filepath.Abs(".")
util.CheckErr(err, "get current working directory")
if err != nil {
return errors.Wrap(err, "error getting current working directory")
}
basename := filepath.Base(dir)
name = util.KubifyName(basename)
}
@@ -73,7 +79,9 @@ func (opts *InitSubCommand) complete(flags cli.Input) error {
// Create spec dir
fmt.Printf("Creating fission spec directory '%v'\n", specDir)
err := os.MkdirAll(specDir, 0755)
util.CheckErr(err, fmt.Sprintf("create spec directory '%v'", specDir))
if err != nil {
return errors.Wrapf(err, "create spec directory '%v'", specDir)
}
// Write the deployment config
opts.deployConfig = &spectypes.DeploymentConfig{
@@ -94,7 +102,7 @@ func (opts *InitSubCommand) complete(flags cli.Input) error {
// run just initializes an empty spec directory and adds some
// sample YAMLs in there that might be useful.
func (opts *InitSubCommand) run(flags cli.Input) error {
specDir := cmd.GetSpecDir(flags)
specDir := util.GetSpecDir(flags)
// Add a bit of documentation to the spec dir here
err := ioutil.WriteFile(filepath.Join(specDir, "README"), []byte(SPEC_README), 0644)
@@ -103,7 +111,9 @@ func (opts *InitSubCommand) run(flags cli.Input) error {
}
err = writeDeploymentConfig(specDir, opts.deployConfig)
util.CheckErr(err, "write deployment config")
if err != nil {
return errors.Wrap(err, "error writing deployment config")
}
// Other possible things to do here:
// - add example specs to the dir to make it easy to manually
+15 -12
View File
@@ -31,9 +31,9 @@ import (
fv1 "github.com/fission/fission/pkg/apis/fission.io/v1"
"github.com/fission/fission/pkg/fission-cli/cliwrapper/cli"
"github.com/fission/fission/pkg/fission-cli/cmd"
"github.com/fission/fission/pkg/fission-cli/cmd/spec/types"
"github.com/fission/fission/pkg/fission-cli/log"
"github.com/fission/fission/pkg/fission-cli/consolemsg"
"github.com/fission/fission/pkg/fission-cli/util"
"github.com/fission/fission/pkg/generator/encoder"
v1generator "github.com/fission/fission/pkg/generator/v1"
)
@@ -347,14 +347,17 @@ func (fr *FissionResources) Validate(flags cli.Input) error {
packages[MapKey(pkgMeta)] = true
}
client := cmd.GetServer(flags)
client, err := util.GetServer(flags)
if err != nil {
return err
}
for _, cm := range f.Spec.ConfigMaps {
_, err := client.ConfigMapGet(&metav1.ObjectMeta{
Name: cm.Name,
Namespace: cm.Namespace,
})
if k8serrors.IsNotFound(err) {
log.Warn(fmt.Sprintf("Configmap %s is referred in the spec but not present in the cluster", cm.Name))
consolemsg.Warn(fmt.Sprintf("Configmap %s is referred in the spec but not present in the cluster", cm.Name))
}
}
@@ -364,7 +367,7 @@ func (fr *FissionResources) Validate(flags cli.Input) error {
Namespace: s.Namespace,
})
if k8serrors.IsNotFound(err) {
log.Warn(fmt.Sprintf("Secret %s is referred in the spec but not present in the cluster", s.Name))
consolemsg.Warn(fmt.Sprintf("Secret %s is referred in the spec but not present in the cluster", s.Name))
}
}
@@ -392,7 +395,7 @@ func (fr *FissionResources) Validate(flags cli.Input) error {
}
if len(t.Spec.Host) > 0 {
log.Warn(fmt.Sprintf("Host in HTTPTrigger spec.Host is now marked as deprecated, see 'help' for details"))
consolemsg.Warn(fmt.Sprintf("Host in HTTPTrigger spec.Host is now marked as deprecated, see 'help' for details"))
}
result = multierror.Append(result, t.Validate())
@@ -427,25 +430,25 @@ func (fr *FissionResources) Validate(flags cli.Input) error {
for _, e := range fr.Environments {
environments[fmt.Sprintf("%s:%s", e.Metadata.Name, e.Metadata.Namespace)] = struct{}{}
if ((e.Spec.Runtime.Container != nil) && (e.Spec.Runtime.PodSpec != nil)) || ((e.Spec.Builder.Container != nil) && (e.Spec.Builder.PodSpec != nil)) {
log.Warn("You have provided both - container spec and pod spec and while merging the pod spec will take precedence.")
consolemsg.Warn("You have provided both - container spec and pod spec and while merging the pod spec will take precedence.")
}
// Unlike CLI can change the environment version silently,
// we have to warn the user to modify spec file when this takes place.
if e.Spec.Version < 3 && e.Spec.Poolsize != 0 {
log.Warn("Poolsize can only be configured when environment version equals to 3, default poolsize 3 will be used for creating environment pool.")
consolemsg.Warn("Poolsize can only be configured when environment version equals to 3, default poolsize 3 will be used for creating environment pool.")
}
}
for _, f := range fr.Functions {
if _, ok := environments[fmt.Sprintf("%s:%s", f.Spec.Environment.Name, f.Spec.Environment.Namespace)]; !ok {
log.Warn(fmt.Sprintf("Environment %s is referenced in function %s but not declared in specs", f.Spec.Environment.Name, f.Metadata.Name))
consolemsg.Warn(fmt.Sprintf("Environment %s is referenced in function %s but not declared in specs", f.Spec.Environment.Name, f.Metadata.Name))
}
strategy := f.Spec.InvokeStrategy.ExecutionStrategy
if strategy.ExecutorType == fv1.ExecutorTypeNewdeploy && strategy.SpecializationTimeout < fv1.DefaultSpecializationTimeOut {
log.Warn(fmt.Sprintf("SpecializationTimeout in function spec.InvokeStrategy.ExecutionStrategy should be a value equal to or greater than %v", fv1.DefaultSpecializationTimeOut))
consolemsg.Warn(fmt.Sprintf("SpecializationTimeout in function spec.InvokeStrategy.ExecutionStrategy should be a value equal to or greater than %v", fv1.DefaultSpecializationTimeOut))
}
if f.Spec.FunctionTimeout <= 0 {
log.Warn(fmt.Sprintf("FunctionTimeout in function spec should be a field which should have a value greater than 0"))
consolemsg.Warn(fmt.Sprintf("FunctionTimeout in function spec should be a field which should have a value greater than 0"))
}
}
@@ -574,7 +577,7 @@ func (fr *FissionResources) ParseYaml(b []byte, loc *Location) error {
default:
// no need to error out just because there's some extra files around;
// also good for compatibility.
log.Warn(fmt.Sprintf("Ignoring unknown type %v in %v", tm.Kind, loc))
consolemsg.Warn(fmt.Sprintf("Ignoring unknown type %v in %v", tm.Kind, loc))
}
// add to source map, check for duplicates
+13 -11
View File
@@ -18,19 +18,17 @@ package spec
import (
"bytes"
"fmt"
"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/cmd"
"github.com/fission/fission/pkg/fission-cli/log"
"github.com/fission/fission/pkg/fission-cli/util"
)
@@ -41,8 +39,12 @@ type ValidateSubCommand struct {
// 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: cmd.GetServer(flags),
client: c,
}
return opts.do(flags)
}
@@ -54,16 +56,16 @@ func (opts *ValidateSubCommand) do(flags cli.Input) error {
func (opts *ValidateSubCommand) run(flags cli.Input) error {
// this will error on parse errors and on duplicates
specDir := cmd.GetSpecDir(flags)
specDir := util.GetSpecDir(flags)
fr, err := ReadSpecs(specDir)
util.CheckErr(err, "read specs")
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 {
fmt.Printf("Error validating specs: %v", err)
return nil
return errors.Wrap(err, "error validating specs")
}
return nil
@@ -75,8 +77,8 @@ func ReadSpecs(specDir string) (*FissionResources, error) {
// make sure spec directory exists before continue
if _, err := os.Stat(specDir); os.IsNotExist(err) {
log.Fatal(fmt.Sprintf("Spec directory %v doesn't exist. "+
"Please check directory path or run \"fission spec init\" to create it.", specDir))
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{