cmd/root.go (272 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 cmd import ( "os" "path" "path/filepath" "strings" "github.com/apache/openwhisk-client-go/whisk" "github.com/apache/openwhisk-wskdeploy/dependencies" "github.com/apache/openwhisk-wskdeploy/deployers" "github.com/apache/openwhisk-wskdeploy/runtimes" "github.com/apache/openwhisk-wskdeploy/utils" "github.com/apache/openwhisk-wskdeploy/wskderrors" "github.com/apache/openwhisk-wskdeploy/wski18n" "github.com/apache/openwhisk-wskdeploy/wskprint" "github.com/spf13/cobra" ) var stderr = "" // Whisk Deploy has root command: wskdeploy // wskdeploy is being created using Cobra Library var RootCmd = &cobra.Command{ Use: "wskdeploy", SilenceErrors: true, SilenceUsage: true, Short: wski18n.T(wski18n.ID_CMD_DESC_SHORT_ROOT), Long: wski18n.T(wski18n.ID_CMD_DESC_LONG_ROOT), RunE: RootCmdImp, } func RootCmdImp(cmd *cobra.Command, args []string) error { return Deploy(cmd) } // Execute adds all child commands to the root command sets flags appropriately. // This is called by main.main(). It only needs to happen once to the rootCmd. func Execute() { var err error os.Args, utils.Flags.Param, err = parseArgsForParams(os.Args) if err != nil { wskprint.PrintOpenWhiskError(err.Error()) os.Exit(-1) } if err = RootCmd.Execute(); err != nil { wskprint.PrintOpenWhiskFromError(err) os.Exit(-1) } } func init() { cobra.OnInitialize(initConfig) // Defining Persistent Flags of Whisk Deploy Root command (wskdeploy) // Persistent flags are global in terms of its availability and acceptable // with any other Whisk Deploy command e.g. undeploy, export, etc. // TODO() Publish command, not completed // TODO() Report command, not completed // TODO() have a single function that conditionally (i.e., Trace=true) prints ALL Flags RootCmd.PersistentFlags().StringVar(&utils.Flags.CfgFile, FLAG_CONFIG, "", wski18n.T(wski18n.ID_CMD_FLAG_CONFIG)) RootCmd.PersistentFlags().StringVarP(&utils.Flags.ProjectPath, FLAG_PROJECT, FLAG_PROJECT_SHORT, ".", wski18n.T(wski18n.ID_CMD_FLAG_PROJECT)) RootCmd.PersistentFlags().StringVarP(&utils.Flags.ManifestPath, FLAG_MANIFEST, FLAG_MANIFEST_SHORT, "", wski18n.T(wski18n.ID_CMD_FLAG_MANIFEST)) RootCmd.PersistentFlags().StringVarP(&utils.Flags.DeploymentPath, FLAG_DEPLOYMENT, FLAG_DEPLOYMENT_SHORT, "", wski18n.T(wski18n.ID_CMD_FLAG_DEPLOYMENT)) RootCmd.PersistentFlags().BoolVarP(&utils.Flags.Strict, FLAG_STRICT, FLAG_STRICT_SHORT, false, wski18n.T(wski18n.ID_CMD_FLAG_STRICT)) RootCmd.PersistentFlags().BoolVarP(&utils.Flags.Preview, FLAG_PREVIEW, "", false, wski18n.T(wski18n.ID_CMD_FLAG_PREVIEW)) RootCmd.PersistentFlags().BoolVarP(&utils.Flags.Verbose, FLAG_VERBOSE, FLAG_VERBOSE_SHORT, false, wski18n.T(wski18n.ID_CMD_FLAG_VERBOSE)) RootCmd.PersistentFlags().StringVarP(&utils.Flags.ApiHost, FLAG_API_HOST, "", "", wski18n.T(wski18n.ID_CMD_FLAG_API_HOST)) RootCmd.PersistentFlags().StringVarP(&utils.Flags.Namespace, FLAG_NAMESPACE, FLAG_NAMESPACE_SHORT, "", wski18n.T(wski18n.ID_CMD_FLAG_NAMESPACE)) RootCmd.PersistentFlags().StringVarP(&utils.Flags.Auth, FLAG_AUTH, FLAG_AUTH_SHORT, "", wski18n.T(wski18n.ID_CMD_FLAG_AUTH_KEY)) RootCmd.PersistentFlags().StringVar(&utils.Flags.ApiVersion, FLAG_APIVERSION, "", wski18n.T(wski18n.ID_CMD_FLAG_API_VERSION)) RootCmd.PersistentFlags().StringVarP(&utils.Flags.Key, FLAG_KEY, FLAG_KEY_SHORT, "", wski18n.T(wski18n.ID_CMD_FLAG_KEY_FILE)) RootCmd.PersistentFlags().StringVarP(&utils.Flags.Cert, FLAG_CERT, FLAG_CERT_SHORT, "", wski18n.T(wski18n.ID_CMD_FLAG_CERT_FILE)) RootCmd.PersistentFlags().BoolVarP(&utils.Flags.Managed, FLAG_MANAGED, "", false, wski18n.T(wski18n.ID_CMD_FLAG_MANAGED)) RootCmd.PersistentFlags().StringVarP(&utils.Flags.ProjectName, FLAG_PROJECTNAME, "", "", wski18n.T(wski18n.ID_CMD_FLAG_PROJECTNAME)) RootCmd.PersistentFlags().BoolVarP(&utils.Flags.Trace, FLAG_TRACE, FLAG_TRACE_SHORT, false, wski18n.T(wski18n.ID_CMD_FLAG_TRACE)) RootCmd.PersistentFlags().StringSliceVarP(&utils.Flags.Param, FLAG_PARAM, "", []string{}, wski18n.T(wski18n.ID_CMD_FLAG_PARAM)) RootCmd.PersistentFlags().StringVarP(&utils.Flags.ParamFile, FLAG_PARAMFILE, FLAG_PARAMFILE_SHORT, "", wski18n.T(wski18n.ID_CMD_FLAG_PARAM_FILE)) RootCmd.PersistentFlags().MarkHidden(FLAG_TRACE) } // initConfig reads in config file and ENV variables if set. func initConfig() { userHome := utils.GetHomeDirectory() defaultPath := path.Join(userHome, whisk.DEFAULT_LOCAL_CONFIG) // Precedence order for reading the configuration file should be: // 1. --config // 2. ENV variable WSK_CONFIG_FILE // 3. Default $HOME/.wskprops cfgFiles := []string{ utils.Flags.CfgFile, os.Getenv("WSK_CONFIG_FILE"), defaultPath, } for _, cfgFile := range cfgFiles { if cfgFile != "" { // Read the file as a wskprops file, to check if it is valid. _, err := whisk.ReadProps(cfgFile) if err != nil { warn := wski18n.T(wski18n.ID_WARN_CONFIG_INVALID_X_path_X, map[string]interface{}{ wski18n.KEY_PATH: utils.Flags.CfgFile}) wskprint.PrintOpenWhiskWarning(warn) } else { utils.Flags.CfgFile = cfgFile break } } } } // TODO() add Trace of runtimes found at apihost func setSupportedRuntimes(apiHost string) error { op, err := runtimes.ParseOpenWhisk(apiHost) if err != nil { return err } runtimes.SupportedRunTimes = runtimes.ConvertToMap(op) runtimes.DefaultRunTimes = runtimes.DefaultRuntimes(op) runtimes.FileExtensionRuntimeKindMap = runtimes.FileExtensionRuntimes(op) runtimes.FileRuntimeExtensionsMap = runtimes.FileRuntimeExtensions(op) return nil } func displayCommandUsingFilenameMessage(command string, filetype string, path string) { msg := wski18n.T(wski18n.ID_MSG_COMMAND_USING_X_cmd_X_filetype_X_path_X, map[string]interface{}{ wski18n.KEY_CMD: command, wski18n.KEY_FILE_TYPE: filetype, wski18n.KEY_PATH: path}) wskprint.PrintlnOpenWhiskVerbose(utils.Flags.Verbose, msg) } func loadDefaultManifestFileFromProjectPath(command string, projectPath string, cmd *cobra.Command) (error, bool) { if _, err := os.Stat(path.Join(projectPath, utils.ManifestFileNameYaml)); err == nil { utils.Flags.ManifestPath = path.Join(projectPath, utils.ManifestFileNameYaml) } else if _, err := os.Stat(path.Join(projectPath, utils.ManifestFileNameYml)); err == nil { utils.Flags.ManifestPath = path.Join(projectPath, utils.ManifestFileNameYml) } else { stderr = wski18n.T(wski18n.ID_ERR_MANIFEST_FILE_NOT_FOUND_X_path_X, map[string]interface{}{wski18n.KEY_PATH: projectPath}) if cmd != nil { stdout := stderr + wski18n.T(cmd.UsageString()) wskprint.PrintlnOpenWhiskOutput(stdout) return nil, true } return wskderrors.NewErrorManifestFileNotFound(projectPath, stderr), false } displayCommandUsingFilenameMessage(command, wski18n.MANIFEST_FILE, utils.Flags.ManifestPath) return nil, false } func loadDefaultDeploymentFileFromProjectPath(command string, projectPath string) error { if _, err := os.Stat(path.Join(projectPath, utils.DeploymentFileNameYaml)); err == nil { utils.Flags.DeploymentPath = path.Join(projectPath, utils.DeploymentFileNameYaml) } else if _, err := os.Stat(path.Join(projectPath, utils.DeploymentFileNameYml)); err == nil { utils.Flags.DeploymentPath = path.Join(projectPath, utils.DeploymentFileNameYml) } displayCommandUsingFilenameMessage(command, wski18n.DEPLOYMENT_FILE, utils.Flags.DeploymentPath) return nil } func Deploy(cmd *cobra.Command) error { // Convey flags for verbose and trace to Go client whisk.SetVerbose(utils.Flags.Verbose) whisk.SetDebug(utils.Flags.Trace) project_Path := strings.TrimSpace(utils.Flags.ProjectPath) if len(project_Path) == 0 { project_Path = utils.DEFAULT_PROJECT_PATH } projectPath, _ := filepath.Abs(project_Path) // If manifest filename is not provided, attempt to load default manifests from project path // default manifests are manifest.yaml and manifest.yml // return failure if none of the default manifest files were found if utils.Flags.ManifestPath == "" { if err, returnRoot := loadDefaultManifestFileFromProjectPath(wski18n.CMD_DEPLOY, projectPath, cmd); err != nil { return err } else if returnRoot == true { return nil } } // If deployment filename is not provided, attempt to load default deployment files from project path // default deployments are deployment.yaml and deployment.yml // continue processing manifest file, even if none of the default // deployment files were found as deployment files are optional if utils.Flags.DeploymentPath == "" { if err := loadDefaultDeploymentFileFromProjectPath(wski18n.CMD_DEPLOY, projectPath); err != nil { return err } } if utils.MayExists(utils.Flags.ManifestPath) { // Create an instance of ServiceDeployer var deployer = deployers.NewServiceDeployer() // Set Project Path, Manifest Path, and Deployment Path of ServiceDeployer deployer.ProjectPath = projectPath deployer.ManifestPath = utils.Flags.ManifestPath deployer.DeploymentPath = utils.Flags.DeploymentPath deployer.Preview = utils.Flags.Preview deployer.Report = utils.Flags.Report // master record of any dependency that has been downloaded deployer.DependencyMaster = make(map[string]dependencies.DependencyRecord) // Read credentials from Configuration file, manifest file or deployment file clientConfig, error := deployers.NewWhiskConfig( utils.Flags.CfgFile, utils.Flags.DeploymentPath, utils.Flags.ManifestPath) if error != nil { return error } whiskClient, error := deployers.CreateNewClient(clientConfig) if error != nil { return error } deployer.Client = whiskClient deployer.ClientConfig = clientConfig // The auth, apihost and namespace have been chosen, so that we can check the supported runtimes here. err := setSupportedRuntimes(clientConfig.Host) if err != nil { return err } // Construct Deployment Plan err = deployer.ConstructDeploymentPlan() if err != nil { return err } // Deploy all OW entities err = deployer.Deploy() if err != nil { return err } else { return nil } } else { errString := wski18n.T(wski18n.ID_ERR_MANIFEST_FILE_NOT_FOUND_X_path_X, map[string]interface{}{wski18n.KEY_PATH: utils.Flags.ManifestPath}) whisk.Debug(whisk.DbgError, errString) return wskderrors.NewErrorManifestFileNotFound(utils.Flags.ManifestPath, errString) } } func Undeploy(cmd *cobra.Command) error { // Convey flags for verbose and trace to Go client whisk.SetVerbose(utils.Flags.Verbose) whisk.SetDebug(utils.Flags.Trace) if len(utils.Flags.ProjectName) != 0 { var deployer = deployers.NewServiceDeployer() deployer.Preview = utils.Flags.Preview clientConfig, error := deployers.NewWhiskConfig(utils.Flags.CfgFile, "", "") if error != nil { return error } whiskClient, error := deployers.CreateNewClient(clientConfig) if error != nil { return error } deployer.Client = whiskClient deployer.ClientConfig = clientConfig // The auth, apihost and namespace have been chosen, so that we can check the supported runtimes here. err := setSupportedRuntimes(clientConfig.Host) if err != nil { return err } err = deployer.UnDeployProject() if err != nil { return err } return nil } project_Path := strings.TrimSpace(utils.Flags.ProjectPath) if len(project_Path) == 0 { project_Path = utils.DEFAULT_PROJECT_PATH } projectPath, _ := filepath.Abs(project_Path) // If manifest filename is not provided, attempt to load default manifests from project path if utils.Flags.ManifestPath == "" { if err, returnRoot := loadDefaultManifestFileFromProjectPath(wski18n.CMD_UNDEPLOY, projectPath, cmd); err != nil { return err } else if returnRoot == true { return nil } } if utils.Flags.DeploymentPath == "" { if err := loadDefaultDeploymentFileFromProjectPath(wski18n.CMD_UNDEPLOY, projectPath); err != nil { return err } } if utils.FileExists(utils.Flags.ManifestPath) { var deployer = deployers.NewServiceDeployer() deployer.ProjectPath = utils.Flags.ProjectPath deployer.ManifestPath = utils.Flags.ManifestPath deployer.DeploymentPath = utils.Flags.DeploymentPath deployer.Preview = utils.Flags.Preview clientConfig, error := deployers.NewWhiskConfig(utils.Flags.CfgFile, utils.Flags.DeploymentPath, utils.Flags.ManifestPath) if error != nil { return error } whiskClient, error := deployers.CreateNewClient(clientConfig) if error != nil { return error } deployer.Client = whiskClient deployer.ClientConfig = clientConfig // The auth, apihost and namespace have been chosen, so that we can check the supported runtimes here. err := setSupportedRuntimes(clientConfig.Host) if err != nil { return err } verifiedPlan, err := deployer.ConstructUnDeploymentPlan() if err != nil { return err } err = deployer.UnDeploy(verifiedPlan) if err != nil { return err } else { return nil } } else { errString := wski18n.T(wski18n.ID_ERR_MANIFEST_FILE_NOT_FOUND_X_path_X, map[string]interface{}{wski18n.KEY_PATH: utils.Flags.ManifestPath}) return wskderrors.NewErrorManifestFileNotFound(utils.Flags.ManifestPath, errString) } }