| /* |
| * 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 common |
| |
| import ( |
| "bytes" |
| "errors" |
| "fmt" |
| "os" |
| "os/exec" |
| "path" |
| "path/filepath" |
| "strings" |
| "time" |
| |
| "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/fatih/color" |
| "github.com/mattn/go-colorable" |
| ) |
| |
| const ( |
| cmd = "wskdeploy" |
| BLUEMIX_APIHOST = "BLUEMIX_APIHOST" |
| BLUEMIX_NAMESPACE = "BLUEMIX_NAMESPACE" |
| BLUEMIX_AUTH = "BLUEMIX_AUTH" |
| ) |
| |
| type Wskdeploy struct { |
| Path string |
| Dir string |
| } |
| |
| func NewWskdeploy() *Wskdeploy { |
| return NewWskWithPath(os.Getenv("GOPATH") + "/src/github.com/apache/openwhisk-wskdeploy/") |
| } |
| |
| func GetWskpropsFromEnvVars(apiHost string, namespace string, authKey string) *whisk.Wskprops { |
| return GetWskpropsFromValues(os.Getenv(apiHost), os.Getenv(namespace), os.Getenv(authKey), "v1") |
| } |
| |
| func GetWskpropsFromValues(apiHost string, namespace string, authKey string, version string) *whisk.Wskprops { |
| dep := whisk.Wskprops{ |
| APIHost: apiHost, |
| AuthKey: authKey, |
| Namespace: namespace, |
| AuthAPIGWKey: "", |
| APIGWSpaceSuid: "", |
| Apiversion: version, |
| Key: "", |
| Cert: "", |
| Source: "", |
| } |
| return &dep |
| } |
| |
| func ValidateWskprops(wskprops *whisk.Wskprops) error { |
| if len(wskprops.APIHost) == 0 { |
| return errors.New("Missing APIHost for wskprops.") |
| } |
| if len(wskprops.Namespace) == 0 { |
| return errors.New("Missing Namespace for wskprops.") |
| } |
| if len(wskprops.AuthKey) == 0 { |
| return errors.New("Missing AuthKey for wskprops.") |
| } |
| return nil |
| } |
| |
| func NewWskWithPath(path string) *Wskdeploy { |
| var dep Wskdeploy |
| dep.Path = cmd |
| dep.Dir = path |
| return &dep |
| } |
| |
| func printCommand(cmd *exec.Cmd) { |
| fmt.Printf("==> Executing: %s\n", strings.Join(cmd.Args, " ")) |
| } |
| |
| func printError(err string) { |
| outputStream := colorable.NewColorableStderr() |
| if len(err) > 0 { |
| fmt.Fprintf(outputStream, "==> Error: %s.\n", color.RedString(err)) |
| } else { |
| fmt.Fprintf(outputStream, "==> Error: %s.\n", color.RedString("No error message")) |
| } |
| } |
| |
| func printOutput(outs string) { |
| if len(outs) > 0 { |
| fmt.Printf("==> Output: %s.\n", outs) |
| } |
| } |
| |
| func (wskdeploy *Wskdeploy) RunCommand(s ...string) (string, error) { |
| command := exec.Command(wskdeploy.Path, s...) |
| command.Dir = wskdeploy.Dir |
| |
| printCommand(command) |
| |
| var outb, errb bytes.Buffer |
| command.Stdout = &outb |
| command.Stderr = &errb |
| err := command.Run() |
| |
| var returnError error = nil |
| if err != nil { |
| // Note: (sub-command) is always at Args[0] which is guaranteed to be not empty |
| returnError = wskderrors.NewCommandError(command.Args[0], errb.String()) |
| } |
| printOutput(outb.String()) |
| if returnError != nil { |
| printError(returnError.Error()) |
| } |
| return outb.String(), returnError |
| } |
| |
| func (wskdeploy *Wskdeploy) Deploy(manifestPath string, deploymentPath string) (string, error) { |
| return wskdeploy.RunCommand("-m", manifestPath, "-d", deploymentPath) |
| } |
| |
| func (wskdeploy *Wskdeploy) DeployManifestVerbose(manifestPath string) (string, error) { |
| return wskdeploy.RunCommand("-m", manifestPath, "-v") |
| } |
| |
| func (wskdeploy *Wskdeploy) DeployWithCredentials(manifestPath string, deploymentPath string, wskprops *whisk.Wskprops) (string, error) { |
| return wskdeploy.RunCommand("-m", manifestPath, "-d", deploymentPath, "--auth", wskprops.AuthKey, |
| "--namespace", wskprops.Namespace, "--apihost", wskprops.APIHost, "--apiversion", wskprops.Apiversion) |
| } |
| |
| func (wskdeploy *Wskdeploy) Undeploy(manifestPath string, deploymentPath string) (string, error) { |
| return wskdeploy.RunCommand("undeploy", "-m", manifestPath, "-d", deploymentPath) |
| } |
| |
| func (wskdeploy *Wskdeploy) UndeployWithCredentials(manifestPath string, deploymentPath string, wskprops *whisk.Wskprops) (string, error) { |
| return wskdeploy.RunCommand("undeploy", "-m", manifestPath, "-d", deploymentPath, "--auth", wskprops.AuthKey, |
| "--namespace", wskprops.Namespace, "--apihost", wskprops.APIHost, "--apiversion", wskprops.Apiversion) |
| } |
| |
| func (wskdeploy *Wskdeploy) DeployProjectPathOnly(projectPath string) (string, error) { |
| return wskdeploy.RunCommand("-p", projectPath) |
| } |
| |
| func (wskdeploy *Wskdeploy) UndeployProjectPathOnly(projectPath string) (string, error) { |
| return wskdeploy.RunCommand("undeploy", "-p", projectPath) |
| |
| } |
| |
| func (wskdeploy *Wskdeploy) DeployManifestPathOnly(manifestpath string) (string, error) { |
| return wskdeploy.RunCommand("-m", manifestpath) |
| } |
| |
| func (wskdeploy *Wskdeploy) UndeployManifestPathOnly(manifestpath string) (string, error) { |
| return wskdeploy.RunCommand("undeploy", "-m", manifestpath) |
| } |
| |
| func (Wskdeploy *Wskdeploy) ManagedDeploymentOnlyManifest(manifestPath string) (string, error) { |
| return Wskdeploy.RunCommand("sync", "-m", manifestPath) |
| } |
| |
| func (Wskdeploy *Wskdeploy) ManagedDeploymentManifestAndProject(manifestPath string, projectName string) (string, error) { |
| return Wskdeploy.RunCommand("sync", "-m", manifestPath, "--projectname", projectName) |
| } |
| |
| func (Wskdeploy *Wskdeploy) ManagedDeploymentManifestAndProjectWithCredentials(manifestPath string, projectName string, wskprops *whisk.Wskprops) (string, error) { |
| return Wskdeploy.RunCommand("sync", "-m", manifestPath, "--projectname", projectName, "--auth", wskprops.AuthKey, |
| "--namespace", wskprops.Namespace, "--apihost", wskprops.APIHost, "--apiversion", wskprops.Apiversion) |
| } |
| |
| func (Wskdeploy *Wskdeploy) ManagedDeployment(manifestPath string, deploymentPath string) (string, error) { |
| return Wskdeploy.RunCommand("sync", "-m", manifestPath, "-d", deploymentPath) |
| } |
| |
| func (Wskdeploy *Wskdeploy) HeadlessManagedDeployment(manifestPath string, deploymentPath string, name string) (string, error) { |
| return Wskdeploy.RunCommand("sync", "-m", manifestPath, "-d", deploymentPath, "--projectname", name) |
| } |
| |
| // Note: this test often fails due to server-related issues trying to access |
| // newly created objects (e.g., primarily Rules) too quickly which often lose Annotations. |
| // causing SIGSEGV errors. |
| // It also sometimes fails accessing local storage (for tmp directories and files |
| // that are created in Travis (constrained, shared environments). |
| // You can add the "-t" or "--trace" to the RunCommand to get extensive trace on all HTTP calls |
| func (wskdeploy *Wskdeploy) ExportProject(projectName string, targetManifestPath string) (string, error) { |
| // TODO: There is a server-side timing issue with Annotations being |
| // retrieved too soon after creation; need to explore in OW main |
| time.Sleep(3 * time.Second) // should it sleep for few seconds before export?! |
| return wskdeploy.RunCommand("export", "-m", targetManifestPath, "--projectname", projectName) |
| } |
| |
| func (wskdeploy *Wskdeploy) ExportProjectWithCredentials(projectName string, targetManifestPath string, wskprops *whisk.Wskprops) (string, error) { |
| // TODO: There is a server-side timing issue with Annotations being |
| // retrieved too soon after creation; need to explore in OW main |
| time.Sleep(3 * time.Second) // should it sleep for few seconds before export?! |
| return wskdeploy.RunCommand("export", "-m", targetManifestPath, "--projectname", projectName, "--auth", wskprops.AuthKey, |
| "--namespace", wskprops.Namespace, "--apihost", wskprops.APIHost, "--apiversion", wskprops.Apiversion) |
| } |
| |
| // This method is only for testing |
| // This method will mock a construction of deployment plan, creating all the memory objects |
| // This method CANNOT be used for real deployment! |
| // Param manifestPath & deploymentPath MUST be the absolute path. |
| func (wskdeploy *Wskdeploy) GetDeploymentObjects(manifestPath string, deploymentPath string) (*deployers.DeploymentProject, error) { |
| //create ServiceDeployer and set default values |
| deployer := deployers.NewServiceDeployer() |
| deployer.ProjectPath = filepath.Dir(manifestPath) |
| deployer.ManifestPath = manifestPath |
| deployer.DeploymentPath = deploymentPath |
| deployer.DependencyMaster = make(map[string]dependencies.DependencyRecord) |
| |
| //create client config with namespace, apihost, authkey and etc. |
| //these values might be mock values because it's only for testing |
| userHome := utils.GetHomeDirectory() |
| defaultPath := path.Join(userHome, whisk.DEFAULT_LOCAL_CONFIG) |
| clientConfig, err := deployers.NewWhiskConfig(defaultPath, deploymentPath, manifestPath) |
| if err != nil { |
| return nil, err |
| } |
| deployer.ClientConfig = clientConfig |
| |
| //setSupportedRuntimes(apiHost string) |
| //only for testing, mock values |
| op, err := runtimes.ParseOpenWhisk(clientConfig.Host) |
| if err == nil { |
| runtimes.SupportedRunTimes = runtimes.ConvertToMap(op) |
| runtimes.DefaultRunTimes = runtimes.DefaultRuntimes(op) |
| runtimes.FileExtensionRuntimeKindMap = runtimes.FileExtensionRuntimes(op) |
| } |
| |
| //invoke ConstructDeploymentPlan to create the in memory objects for deployment |
| err = deployer.ConstructDeploymentPlan() |
| if err != nil { |
| return nil, err |
| } |
| //return the deployment objects |
| return deployer.Deployment, nil |
| } |