blob: 1974a128808d51297d4cd09c57dd941e4672dc1e [file] [log] [blame]
/*
* 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 commands
import (
"errors"
"fmt"
"io"
"os"
"strings"
"github.com/spf13/cobra"
"github.com/apache/incubator-openwhisk-cli/wski18n"
"github.com/apache/incubator-openwhisk-client-go/whisk"
)
// sdkCmd represents the sdk command
var sdkCmd = &cobra.Command{
Use: "sdk",
Short: wski18n.T("work with the sdk"),
}
type sdkInfo struct {
UrlPath string
FileName string
isGzTar bool
IsGzip bool
IsZip bool
IsTar bool
Unpack bool
UnpackDir string
}
var sdkMap map[string]*sdkInfo
const SDK_DOCKER_COMPONENT_NAME string = "docker"
const SDK_IOS_COMPONENT_NAME string = "ios"
const BASH_AUTOCOMPLETE_FILENAME string = "wsk_cli_bash_completion.sh"
var sdkInstallCmd = &cobra.Command{
Use: "install COMPONENT",
Short: wski18n.T("install SDK artifacts"),
Long: wski18n.T("install SDK artifacts, where valid COMPONENT values are docker, ios, and bashauto"),
SilenceUsage: true,
SilenceErrors: true,
PreRunE: SetupClientConfig,
RunE: func(cmd *cobra.Command, args []string) error {
var err error
if len(args) != 1 {
whisk.Debug(whisk.DbgError, "Invalid number of arguments: %d\n", len(args))
errStr := wski18n.T("The SDK component argument is missing. One component (docker, ios, or bashauto) must be specified")
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
return werr
}
component := strings.ToLower(args[0])
switch component {
case "docker":
err = dockerInstall()
case "ios":
err = iOSInstall()
case "bashauto":
if Flags.sdk.stdout {
if err = WskCmd.GenBashCompletion(os.Stdout); err != nil {
whisk.Debug(whisk.DbgError, "GenBashCompletion error: %s\n", err)
errStr := wski18n.T("Unable to output bash command completion {{.err}}",
map[string]interface{}{"err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
} else {
err = WskCmd.GenBashCompletionFile(BASH_AUTOCOMPLETE_FILENAME)
if err != nil {
whisk.Debug(whisk.DbgError, "GenBashCompletionFile('%s`) error: \n", BASH_AUTOCOMPLETE_FILENAME, err)
errStr := wski18n.T("Unable to generate '{{.name}}': {{.err}}",
map[string]interface{}{"name": BASH_AUTOCOMPLETE_FILENAME, "err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
fmt.Printf(
wski18n.T("bash_completion_msg",
map[string]interface{}{"name": BASH_AUTOCOMPLETE_FILENAME}))
}
default:
whisk.Debug(whisk.DbgError, "Invalid component argument '%s'\n", component)
errStr := wski18n.T("The SDK component argument '{{.component}}' is invalid. Valid components are docker, ios and bashauto",
map[string]interface{}{"component": component})
err = whisk.MakeWskError(errors.New(errStr), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.DISPLAY_USAGE)
}
if err != nil {
return err
}
return nil
},
}
func dockerInstall() error {
var err error
targetFile := sdkMap[SDK_DOCKER_COMPONENT_NAME].FileName
if _, err = os.Stat(targetFile); err == nil {
whisk.Debug(whisk.DbgError, "os.Stat reports file '%s' exists\n", targetFile)
errStr := wski18n.T("The file '{{.name}}' already exists. Delete it and retry.",
map[string]interface{}{"name": targetFile})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
if err = sdkInstall(SDK_DOCKER_COMPONENT_NAME); err != nil {
whisk.Debug(whisk.DbgError, "sdkInstall(%s) failed: %s\n", SDK_DOCKER_COMPONENT_NAME, err)
errStr := wski18n.T("The {{.component}} SDK installation failed: {{.err}}",
map[string]interface{}{"component": SDK_DOCKER_COMPONENT_NAME, "err": err})
werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
fmt.Println(wski18n.T("The docker skeleton is now installed at the current directory."))
return nil
}
func iOSInstall() error {
var err error
if err = sdkInstall(SDK_IOS_COMPONENT_NAME); err != nil {
whisk.Debug(whisk.DbgError, "sdkInstall(%s) failed: %s\n", SDK_IOS_COMPONENT_NAME, err)
errStr := wski18n.T("The {{.component}} SDK installation failed: {{.err}}",
map[string]interface{}{"component": SDK_IOS_COMPONENT_NAME, "err": err})
werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
fmt.Printf(
wski18n.T("Downloaded OpenWhisk iOS starter app. Unzip '{{.name}}' and open the project in Xcode.\n",
map[string]interface{}{"name": sdkMap[SDK_IOS_COMPONENT_NAME].FileName}))
return nil
}
func sdkInstall(componentName string) error {
targetFile := sdkMap[componentName].FileName
if _, err := os.Stat(targetFile); err == nil {
whisk.Debug(whisk.DbgError, "os.Stat reports file '%s' exists\n", targetFile)
errStr := wski18n.T("The file '{{.name}}' already exists. Delete it and retry.",
map[string]interface{}{"name": targetFile})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
resp, err := Client.Sdks.Install(sdkMap[componentName].UrlPath)
if err != nil {
whisk.Debug(whisk.DbgError, "Client.Sdks.Install(%s) failed: %s\n", sdkMap[componentName].UrlPath, err)
errStr := wski18n.T("Unable to retrieve '{{.urlpath}}' SDK: {{.err}}",
map[string]interface{}{"urlpath": sdkMap[componentName].UrlPath, "err": err})
werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
if resp.Body == nil {
whisk.Debug(whisk.DbgError, "SDK Install HTTP response has no body\n")
errStr := wski18n.T("Server failed to send the '{{.component}}' SDK: {{.err}}",
map[string]interface{}{"name": componentName, "err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXIT_CODE_ERR_NETWORK, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
// Create the SDK file
sdkfile, err := os.Create(targetFile)
if err != nil {
whisk.Debug(whisk.DbgError, "os.Create(%s) failure: %s\n", targetFile, err)
errStr := wski18n.T("Error creating SDK file '{{.name}}': {{.err}}",
map[string]interface{}{"name": targetFile, "err": err})
werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
// Read the HTTP response body and write it to the SDK file
whisk.Debug(whisk.DbgInfo, "Reading SDK file from HTTP response body\n")
_, err = io.Copy(sdkfile, resp.Body)
if err != nil {
whisk.Debug(whisk.DbgError, "io.Copy() of resp.Body into sdkfile failure: %s\n", err)
errStr := wski18n.T("Error copying server response into file: {{.err}}",
map[string]interface{}{"err": err})
werr := whisk.MakeWskErrorFromWskError(errors.New(errStr), err, whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
sdkfile.Close()
return werr
}
sdkfile.Close() // Don't use 'defer' since this file might need to be deleted after unpack
// At this point, the entire file is downloaded from the server
// Check if there is any special post-download processing (i.e. unpack)
if sdkMap[componentName].Unpack {
// Make sure the target directory does not already exist
defer os.Remove(targetFile)
targetdir := sdkMap[componentName].UnpackDir
if _, err = os.Stat(targetdir); err == nil {
whisk.Debug(whisk.DbgError, "os.Stat reports that directory '%s' exists\n", targetdir)
errStr := wski18n.T("The directory '{{.name}}' already exists. Delete it and retry.",
map[string]interface{}{"name": targetdir})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
// If the packed SDK is a .tgz file, unpack it in two steps
// 1. UnGzip into temp .tar file
// 2. Untar the contents into the current folder
if sdkMap[componentName].isGzTar {
whisk.Debug(whisk.DbgInfo, "unGzipping downloaded file\n")
err := unpackGzip(targetFile, "temp.tar")
if err != nil {
whisk.Debug(whisk.DbgError, "unpackGzip(%s,temp.tar) failure: %s\n", targetFile, err)
errStr := wski18n.T("Error unGzipping file '{{.name}}': {{.err}}",
map[string]interface{}{"name": targetFile, "err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
defer os.Remove("temp.tar")
whisk.Debug(whisk.DbgInfo, "unTarring unGzipped file\n")
err = unpackTar("temp.tar")
if err != nil {
whisk.Debug(whisk.DbgError, "unpackTar(temp.tar) failure: %s\n", err)
errStr := wski18n.T("Error untarring file '{{.name}}': {{.err}}",
map[string]interface{}{"name": "temp.tar", "err": err})
werr := whisk.MakeWskError(errors.New(errStr), whisk.EXIT_CODE_ERR_GENERAL, whisk.DISPLAY_MSG, whisk.NO_DISPLAY_USAGE)
return werr
}
}
// Future SDKs may require other unpacking procedures not yet covered here....
}
return nil
}
func init() {
sdkInstallCmd.Flags().BoolVarP(&Flags.sdk.stdout, "stdout", "s", false, wski18n.T("prints bash command completion script to stdout"))
sdkCmd.AddCommand(sdkInstallCmd)
sdkMap = make(map[string]*sdkInfo)
sdkMap["docker"] = &sdkInfo{UrlPath: "blackbox.tar.gz", FileName: "blackbox.tar.gz", isGzTar: true, Unpack: true, UnpackDir: "dockerSkeleton"}
sdkMap["ios"] = &sdkInfo{UrlPath: "OpenWhiskIOSStarterApp.zip", FileName: "OpenWhiskIOSStarterApp.zip", IsZip: true, Unpack: false}
}