gather create parameters into a structure

This commit is contained in:
Luke K 2020-05-23 03:30:40 +00:00
parent fd27affb4b
commit c12c5c17d2
No known key found for this signature in database
GPG Key ID: 4896F75BAF2E1966
2 changed files with 87 additions and 30 deletions

View File

@ -12,6 +12,16 @@ import (
"github.com/boson-project/faas/kubectl"
)
// The create command invokes the Service Funciton Client to create a new,
// functional, deployed service function with a noop implementation. It
// can be optionally created only locally (no deploy) using --local.
var createCmd = &cobra.Command{
Use: "create <language>",
Short: "Create a Service Function",
SuggestFor: []string{"init", "new"},
RunE: create,
}
func init() {
// Add the `create` command as a subcommand to root.
root.AddCommand(createCmd)
@ -34,71 +44,105 @@ func init() {
}
// The create command invokes the Service Funciton Client to create a new,
// functional, deployed service function with a noop implementation. It
// can be optionally created only locally (no deploy) using --local.
var createCmd = &cobra.Command{
Use: "create <language>",
Short: "Create a Service Function",
SuggestFor: []string{"init", "new"},
RunE: create,
// The create command expects several parameters, most of which can be
// defaulted. When an interactive terminal is detected, these parameters,
// which are gathered into this config object, are passed through the shell
// allowing the user to interactively confirm and optionally modify values.
type createConfig struct {
// Verbose mode instructs the system to output detailed logs as the command
// progresses.
Verbose bool
// Local mode flag only builds a function locally, with no deployed
// counterpart.
Local bool
// Internal only flag. A public route will not be allocated and the domain
// suffix will be a .local (depending on underlying cluster configuration)
Internal bool
// Name of the service in DNS-compatible format (ex myfunc.example.com)
Name string
// Registry of containers (ex. quay.io or hub.docker.com)
Registry string
// Namespace within the container registry within which interstitial built
// images will be stored by their canonical name.
Namespace string
// Language is the first argument, and specifies the resultant Function
// implementation language.
Language string
// Path of the Function implementation on local disk. Defaults to current
// working directory of the process.
Path string
}
// create a new service function using the client about config.
func create(cmd *cobra.Command, args []string) (err error) {
// Assert a language parameter was provided
if len(args) == 0 {
return errors.New("'faas create' requires a language argument.")
}
// Assemble parameters for use in client's 'create' invocation.
var (
language = args[0] // language is the first argument
path = "" // path is defaulted to current working directory.
local = viper.GetBool("local") // Only perform local creation steps
internal = viper.GetBool("internal") // Do not expose publicly (internal route only)
name = viper.GetString("name") // Explicit name override (by default path-derives)
verbose = viper.GetBool("verbose") // Verbose logging
registry = viper.GetString("registry") // Registry (ex: docker.io)
namespace = viper.GetString("namespace") // namespace at registry (user or org name)
)
// Create a deafult configuration populated first with environment variables,
// followed by overrides by flags.
var config = createConfig{
Verbose: viper.GetBool("verbose"),
Local: viper.GetBool("local"),
Internal: viper.GetBool("internal"),
Name: viper.GetString("name"),
Registry: viper.GetString("registry"),
Namespace: viper.GetString("namespace"),
Language: args[0],
Path: ".", // will be expanded to process current working dir.
}
// If path is provided
if len(args) == 2 {
path = args[1]
config.Path = args[1]
}
// Namespace can not be defaulted.
if namespace == "" {
if config.Namespace == "" {
return errors.New("image registry namespace (--namespace or FAAS_NAMESPACE is required)")
}
// If we are running as an interactive terminal, allow the user
// to mutate default config prior to execution.
if isInteractive() {
config = gatherFromUser(config)
}
// Initializer creates a deployable noop function implementation in the
// configured path.
initializer := appsody.NewInitializer()
initializer.Verbose = verbose
initializer.Verbose = config.Verbose
// Builder creates images from function source.
builder := appsody.NewBuilder(registry, namespace)
builder.Verbose = verbose
builder := appsody.NewBuilder(config.Registry, config.Namespace)
builder.Verbose = config.Verbose
// Pusher of images
pusher := docker.NewPusher()
pusher.Verbose = verbose
pusher.Verbose = config.Verbose
// Deployer of built images.
deployer := kubectl.NewDeployer()
deployer.Verbose = verbose
deployer.Verbose = config.Verbose
// Instantiate a client, specifying concrete implementations for
// Initializer and Deployer, as well as setting the optional verbosity param.
client, err := faas.New(
faas.WithVerbose(verbose),
faas.WithVerbose(config.Verbose),
faas.WithInitializer(initializer),
faas.WithBuilder(builder),
faas.WithPusher(pusher),
faas.WithDeployer(deployer),
faas.WithLocal(local), // local template only (no cluster deployment)
faas.WithInternal(internal), // if deployed, no publicly accessible route.
faas.WithLocal(config.Local), // local template only (no cluster deployment)
faas.WithInternal(config.Internal), // if deployed, no publicly accessible route.
)
if err != nil {
return
@ -108,5 +152,10 @@ func create(cmd *cobra.Command, args []string) (err error) {
// Returns the final address.
// Name can be empty string (path-dervation will be attempted)
// Path can be empty, defaulting to current working directory.
return client.Create(language, name, path)
return client.Create(config.Language, config.Name, config.Path)
}
func gatherFromUser(config createConfig) createConfig {
// TODO
return config
}

View File

@ -62,3 +62,11 @@ func Execute() {
os.Exit(1)
}
}
// isInteractive returns whether or not the currently attached process terminal
// is interactive. Used for determining whether or not to interactively prompt
// the user to confirm default choices, etc.
func isInteractive() bool {
fi, err := os.Stdin.Stat()
return err == nil && ((fi.Mode() & os.ModeCharDevice) != 0)
}