commands/sdk.go (202 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 commands import ( "errors" "fmt" "io" "os" "strings" "github.com/spf13/cobra" "github.com/apache/openwhisk-cli/wski18n" "github.com/apache/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: %s\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} }