Merge the repositories of openwhisk-cli and openwhisk/go-whisk-cli
diff --git a/.appveyor.yml b/.appveyor.yml
new file mode 100644
index 0000000..d4c2d15
--- /dev/null
+++ b/.appveyor.yml
@@ -0,0 +1,13 @@
+clone_folder: c:\gopath\src\github.com\openwhisk\openwhisk-cli
+
+environment:
+ GOPATH: c:\gopath
+
+build: false
+
+install:
+ - echo %PATH%
+ - echo %GOPATH%
+ - set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
+ - go version
+ - go env
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..d32d55d
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,73 @@
+# A Travis CI configuration file.
+
+language: go
+
+matrix:
+ include:
+ - os: linux
+ sudo: required
+ go: 1.7
+ env:
+ secure: 0c6mnduuUXScXl4MWyrT1oAk22SqAGzc+7KNuWUtk6berX189UzVvVY919HS/it/gHgMCMWlixoChzh9k2qYeCeDgTwR1rYWuA1Q/ACNL07vCtCwOMTihdVrOz53dy5l82AJC86cfH8Zr0cpNOyRCbCoSozmJ4GQ0lWG5lbSUD1BbNnENjP6IYAxIOfDid75UZU0iA/ebI27xImATYSrIlK1zImtKH/YYeqbsGq8jP0p9A8mg8ey0ECin+Z26AXiy9UTSvj3ujgxaS0nPwEmXu5FcA6Ad0Pr78TGvBcnv92s+MHBQfnPUuQjibrRPAquxsGN9GCWZ58gWPq4+Fg67B63dR1wtSmNEIIbXkLVuZnb2zuhh8KEHuUDVTZLjgzpVcR0J2NtDGsC09HDb88SHrGV+eFejrUVny8g/nPmvmMofX2IAhDuam7rEGVOUdY3lLDC00uZprDjZdXUBto83VmOG5OGPQ7Xe5tz4Ek+s2y3iTq9qW+BrwLO55SFYe+BTDdfSY1HiPVfc+bRbScPiOYXr+080g9zFulCePlZPrWrBCtALiPE69etGrj5FooY9+gQidpfKJ03Qy8vG4l8wl4kHjnGzFwkbnuAHoEzjMaImvjl5nAC/24z9bVUau3AMKmqZ9XrTjSpGgo10MVZwSnA+kWj7wYLKaQcCWceLrU=
+ services: docker
+ - os: osx
+ go: 1.7
+
+git:
+ depth: 3
+
+install:
+ - export DEPLOY_BUILD_READY=false
+ - go get -u github.com/golang/lint/golint
+ - go get -u github.com/stretchr/testify
+ - go get -u github.com/spf13/viper
+
+script:
+ - make lint
+ - make build
+ - make test
+ - export PATH=$PATH:$TRAVIS_BUILD_DIR;
+ - make native_test;
+ - if [ "$TRAVIS_OS_NAME" == "linux" ] ; then
+ export OPENWHISK_HOME="$(dirname "$TRAVIS_BUILD_DIR")/openwhisk";
+ ./tools/travis/install_openwhisk.sh;
+ make integration_test;
+ fi
+
+after_script:
+ - make clean
+
+after_success:
+ - DEPLOY_BUILD_READY=true
+ # This tag is automatically generated for the latest merged commit in master branch.
+ - if [ "$TRAVIS_BRANCH" == "master" ] && [ "$TRAVIS_EVENT_TYPE" == "push" ] && [ "$TRAVIS_OS_NAME" == "linux" ] ; then
+ git config --global user.email "builds@travis-ci.com";
+ git config --global user.name "Travis CI";
+ export GIT_TAG="latest";
+ git tag -d $GIT_TAG;
+ git tag $GIT_TAG -a -m "Generated tag from Travis CI build $TRAVIS_BUILD_NUMBER";
+ git push -f -q https://$API_TOKEN@github.com/openwhisk/openwhisk-cli $GIT_TAG;
+ fi
+
+before_deploy:
+ - go get github.com/inconshreveable/mousetrap
+ - go get github.com/mattn/go-isatty
+ - export build_file_name=wsk
+ - export zip_file_name=OpenWhisk_CLI
+ - chmod +x tools/travis/build_tag_releases.sh
+ - ./tools/travis/build_tag_releases.sh $build_file_name $zip_file_name
+ - export RELEASE_PKG_FILE=$(ls $zip_file_name-*.zip)
+ - echo "Deploying $RELEASE_PKG_FILE to GitHub releases."
+
+deploy:
+ provider: releases
+ api_key:
+ secure: Yh1aYiM/qIWkPMSVjGUq1g9TjpACjavQ00QAqp4oqghNZc6xBcmdzsfD2VjzVPHleNI1FIZyjJ1x6laRfWBzRkAcJcjUHXA2bO/V0jqePVmgVm75WwTZ/9EaWIJeAg5CQMm5DGS28Yhc60C0ut3ZzKMWGTiKb73UADXPTGd/tjndxjfksX/THXPuInKB9QZesmluBAC2am/x/6J311WA2wqe0p1+9JFwMr8XwIcwzCwgi/d9CFpS1RnVpLE/ORSgmN/dFbZ7A/qVbx377QoxKiEB0jmUwi13f7REFAw18JdgzbQCH3X4HNu9pCJwHEAq3lP2CfmHbAXcViBeji/Xh9PPJVV9TYqO+uT8oPxCPJND1A/3O2xJ8LyZ/FP2bWqG/Ds/8SZCvxfOR/X77opUeZ4qAp7HJMVCsFi3TsnmzxCe0BOxCppVJLhoSZ2rOAPJi9mKgS/Z/VA5VhNNmnPtkReEWK4vT9h3/iCwv9anvC0RKeLckSHpCm5C5otNXtV4L990fL5L5krMatxynHnCmmhYeLg/Ns+5ncax58Y8hmhnhzTqbPGHpe79bJRfvwRI9lboq7kEj4x5O/M16TKRfQ8ZU5UHvrCPdlTfT7NUXRGZkvWX20X6Ta/DRROTF+xZGiq7da3Oi+xyNDx/LmymfR49thjzgIPXVZolknGYQ9Q=
+ file_glob: true
+ file: ${zip_file_name}-*.zip
+ overwrite: true
+ skip_cleanup: true
+ on:
+ repo: openwhisk/openwhisk-cli
+ tags: true
+ condition: "$DEPLOY_BUILD_READY = true"
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..17a2556
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,27 @@
+FROM golang:1.7
+
+# Install zip
+RUN apt-get -y update && \
+ apt-get -y install zip
+
+ENV GOPATH=/
+
+# Download and install tools
+RUN echo "Installing the godep tool"
+RUN go get github.com/tools/godep
+
+ADD . /src/github.com/openwhisk/openwhisk-cli
+
+# Load all of the dependencies from the previously generated/saved godep generated godeps.json file
+RUN echo "Restoring Go dependencies"
+RUN cd /src/github.com/openwhisk/openwhisk-cli && /bin/godep restore -v
+
+# wsk binary will be placed under a build folder
+RUN mkdir /src/github.com/openwhisk/openwhisk-cli/build
+
+ARG CLI_OS
+ARG CLI_ARCH
+
+# Build the Go wsk CLI binaries and compress resultant binaries
+RUN chmod +x /src/github.com/openwhisk/openwhisk-cli/build.sh
+RUN cd /src/github.com/openwhisk/openwhisk-cli && ./build.sh
diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json
index 28fc9d2..16f6be8 100644
--- a/Godeps/Godeps.json
+++ b/Godeps/Godeps.json
@@ -1,10 +1,7 @@
{
- "ImportPath": "go-whisk-cli",
- "GoVersion": "go1.6",
+ "ImportPath": "github.com/openwhisk/openwhisk-cli",
+ "GoVersion": "go1.",
"GodepVersion": "v74",
- "Packages": [
- "go-whisk-cli"
- ],
"Deps": [
{
"ImportPath": "github.com/cloudfoundry/jibber_jabber",
@@ -29,12 +26,12 @@
},
{
"ImportPath": "github.com/mattn/go-colorable",
- "Comment": "v0.0.5",
- "Rev": "9056b7a9f2d1f2d96498d6d146acd1f9d5ed3d59"
+ "Comment": "v0.0.6-9-gd228849",
+ "Rev": "d228849504861217f796da67fae4f6e347643f15"
},
{
"ImportPath": "github.com/mattn/go-isatty",
- "Rev": "56b76bdf51f7708750eac80fa38b952bb9f32639"
+ "Rev": "66b8e73f3f5cda9f96b69efd03dd3d7fc4a5cdb8"
},
{
"ImportPath": "github.com/mitchellh/go-homedir",
@@ -75,6 +72,14 @@
{
"ImportPath": "gopkg.in/yaml.v2",
"Rev": "e4d366fc3c7938e2958e662b4258c7a89e1f0e3e"
- }
+ },
+ {
+ "ImportPath": "github.com/openwhisk/openwhisk-client-go/whisk",
+ "Rev": "c91a7494986d5f45b62003a2ab19bb0fd8ddeb7f"
+ },
+ {
+ "ImportPath": "github.com/openwhisk/openwhisk-client-go/wski18n",
+ "Rev": "c91a7494986d5f45b62003a2ab19bb0fd8ddeb7f"
+ }
]
}
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..4b5f304
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,52 @@
+SOURCEDIR=.
+
+SOURCES := $(shell find $(SOURCEDIR) -name '*.go')
+BINARY=wsk
+
+VERSION=1.0.0
+
+BUILD=`git rev-parse HEAD`
+
+deps:
+ @echo "Installing dependencies"
+ go get -d -t ./...
+
+LDFLAGS=-ldflags "-X main.Version=`date -u '+%Y-%m-%dT%H:%M:%S'` -X main.Build=`git rev-parse HEAD` "
+
+updatedeps:
+ @echo "Updating all dependencies"
+ @go get -d -u -f -fix -t ./...
+
+# Build the project
+build: deps
+ go build ${LDFLAGS} -o ${BINARY}
+
+test:
+ @echo "Launch the unit tests."
+ go test ./... -tags=unit
+
+native_test:
+ @echo "Launch the native tests for the commands."
+ go test -v ./... -tags=native
+
+# Run the integration test against OpenWhisk
+integration_test:
+ @echo "Launch the integration tests."
+ go test -v ./... -tags=integration
+
+format:
+ @echo "Formatting"
+ go fmt ./...
+
+lint: format
+ @echo "Linting"
+ golint .
+
+install:
+ go install
+
+# Cleans our project: deletes binaries
+clean:
+ if [ -f ${BINARY} ] ; then rm ${BINARY}; fi
+
+.PHONY: clean install
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ec41e8c
--- /dev/null
+++ b/README.md
@@ -0,0 +1,83 @@
+# OpenWhisk Command Line Interface `wsk`
+
+The OpenWhisk Command Line Interface (OpenWhisk CLI) is a unified tool that provides a consistent interface to
+interact with OpenWhisk services. With this tool to download and configure, you are able to manage OpenWhisk services
+from the command line and automate them through scripts.
+
+
+# Where to download the binary of OpenWhisk CLI
+
+The OpenWhisk CLI is available on the release page: [click here to download](https://github.com/openwhisk/openwhisk-cli/releases).
+We currently have binaries available for Linux, Mac OS and windows under amd64 architecture. You can download the
+binary, which fits your local environment.
+
+
+# How to build the binary locally
+
+You can also choose to build the binary locally based on the source code. First, install the prerequisites to
+download and build OpenWhisk CLI: [installing Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git).
+
+Then, download the source code via the Git command:
+
+```
+$ git clone https://github.com/openwhisk/openwhisk-cli.git
+```
+
+OpenWhisk CLI(`wsk`) is produced in a Docker container during the build process which is copied from the
+Docker container to the local file system in the following directory: bin. This binary will be platform
+specific, it will only run on the operating system, and CPU architecture that matches the build machine.
+
+## Build the binary with Go
+
+The binary can be built by Go build command. Make sure that you have Go installed: [installing Go](https://golang.org/doc/install).
+
+After that, open an terminal, go to the directory of OpenWhisk CLI home directory, and build the binary via
+the following command:
+
+```
+$ go build -o wsk
+```
+
+If you would like to build the binary for a specific operating system, you may add the arguments GOOS and
+GOARCH into the Go build command. Since it is only applicable under amd64 architecture, you have to set GOARCH
+to amd64. GOOS can be set to "linux" "darwin" or "windows".
+
+For example, run the following command to build the binary for Linux:
+
+```
+$ GOOS=linux GOARCH=amd64 go build -o wsk
+```
+
+If it is executed successfully, you can find your binary `wsk` directly under OpenWhisk CLI home directory.
+
+## Build the binary with Docker and Gradle
+
+This is the second choice for you to build the binary. Make sure that you have Docker and gradle on your machine:
+[installing Docker](https://docs.docker.com/engine/installation/) and [installing Gradle](https://gradle.org/install) for your local machine.
+
+After that, open an terminal, go to the directory of OpenWhisk CLI home directory, and
+build the binary via the following command under Linux or Mac:
+
+```
+$ ./gradlew distDocker
+```
+
+or run the following command for Windows:
+
+```
+$ ./gradlew.bat distDocker
+```
+
+Finally, you can find the binary `wsk` or `wsk.exe` in the bin folder under the OpenWhisk CLI home directory.
+
+
+# How to use the binary
+
+When you have the binary, you can copy the binary to any folder, and add folder into the system PATH in order to
+run the OpenWhisk CLI command. To get the CLI command help, execute the following command:
+
+```
+$ wsk --help
+```
+
+To get CLI command debug information, include the -d, or --debug flag when executing this command.
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..4204c1a
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,93 @@
+ext.dockerImageName = "cli"
+ext.dockerContainerName = "cli"
+ext.dockerBuildArgs = getDockerBuildArgs()
+apply from: 'gradle/docker.gradle'
+
+
+task removeBinary(type: Delete) {
+ delete "${projectDir}/bin/wsk"
+ delete "${projectDir}/bin/mac"
+ delete "${projectDir}/bin/linux"
+ delete "${projectDir}/bin/windows"
+}
+
+task distBinary(dependsOn: [removeBinary, distDocker]) {
+ doLast {
+ run(dockerBinary + ["rm", "-f", dockerContainerName], true)
+ run(dockerBinary + ["run", "--name", dockerContainerName, dockerTaggedImageName])
+
+ // Copy all Go binaries from Docker into openwhisk/bin folder
+ run(dockerBinary + ["cp", dockerContainerName +
+ ":/src/github.com/openwhisk/openwhisk-cli/build/.", "${projectDir}/bin"])
+
+ run(dockerBinary + ["rm", "-f", dockerContainerName])
+ }
+}
+
+task dumpOSInfo {
+ doLast {
+ println "os.name = "+getOsName()
+ println "os.arch = "+getOsArch()
+ println "go.name = "+mapOsNameToGoName(getOsName())
+ println "go.arch = "+mapOsArchToGoArch(getOsArch())
+ }
+}
+
+task copyCLIShortcut(type: Copy, dependsOn: [distBinary, dumpOSInfo]) {
+ String go_osname = mapOsNameToGoName(getOsName())
+ String go_osarch = mapOsArchToGoArch(getOsArch())
+ String from_path_wsk = "${projectDir}/bin/${go_osname}/${go_osarch}/wsk"
+ String to_path_dir = "${projectDir}/bin"
+
+ from from_path_wsk
+ into to_path_dir
+}
+
+pushImage.finalizedBy copyCLIShortcut
+
+// Returns the Go CLI docker build args
+def getDockerBuildArgs() {
+ String local_os = mapOsNameToGoName(getOsName())
+ String local_arch = mapOsArchToGoArch(getOsArch())
+ def res = []
+
+ if(!project.hasProperty('crossCompileCLI') || project.crossCompileCLI == "false") {
+ res = ["CLI_OS=${local_os}", "CLI_ARCH=${local_arch}"]
+ } else {
+ res = ["CLI_OS=mac linux windows", "CLI_ARCH=386 amd64"]
+ }
+
+ return res
+}
+
+def run(cmd, ignoreError = false) {
+ println("Executing '${cmd.join(" ")}'")
+ def proc = cmd.execute()
+ proc.waitFor()
+ if(!ignoreError && proc.exitValue() != 0) {
+ println("Command '${cmd.join(" ")}' failed with exitCode ${proc.exitValue()}")
+ }
+}
+
+def getOsName() {
+ return System.properties['os.name']
+}
+
+def getOsArch() {
+ return System.properties['os.arch']
+}
+
+def mapOsNameToGoName(String osname) {
+ String osname_l = osname.toLowerCase()
+ if (osname_l.contains("nux") || osname.contains("nix")) return "linux"
+ if (osname_l.contains("mac")) return "mac"
+ if (osname_l.contains("windows")) return "windows"
+ return osname_l
+}
+
+def mapOsArchToGoArch(String osarch) {
+ String osarch_l = osarch.toLowerCase()
+ if (osarch_l.contains("x86_64") || osarch_l == "amd64") return "amd64"
+ if (osarch_l.contains("i386") || osarch_l.contains("x86_32")) return "386"
+ return osarch_l
+}
diff --git a/build.sh b/build.sh
new file mode 100644
index 0000000..4a7fca8
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,105 @@
+#!/bin/bash
+
+set +x
+set -e
+
+get_bin_name () {
+ local os=$1
+ local bin="wsk"
+
+ if [ $os = "windows" ]; then
+ bin="${bin}.exe";
+ fi
+
+ echo $bin;
+};
+
+build_cli () {
+ local os=$1
+ local arch=$2
+ local bin=$3
+
+ echo "Building for OS '$os' and architecture '$arch'"
+
+ if [ $os = "mac" ]; then
+ export GOOS=darwin;
+ else
+ export GOOS=$os;
+ fi
+
+ export GOARCH=$arch
+
+ cd /src/github.com/openwhisk/openwhisk-cli
+ go build -ldflags "-X main.CLI_BUILD_TIME=`date -u '+%Y-%m-%dT%H:%M:%S%:z'`" -v -o build/$os/$arch/$bin main.go;
+};
+
+get_compressed_name() {
+ local os=$1
+ local arch=$2
+ local product_name="OpenWhisk_CLI"
+
+ if [ $arch = amd64 ]; then
+ comp_name="$product_name-$os";
+ elif [ $arch = 386 ]; then
+ comp_name="$product_name-$os-32bit";
+ else
+ comp_name="$product_name-$os-$arch";
+ fi
+
+ echo $comp_name;
+};
+
+compress_binary() {
+ local comp_name=$1
+ local bin=$2
+ local os=$3
+ local arch=$4
+
+ cd build/$os/$arch
+
+ if [ $os = "linux" ]; then
+ comp_name="$comp_name.tgz"
+ tar -cvzf $comp_name $bin >/dev/null 2>&1;
+ else
+ comp_name="$comp_name.zip"
+ zip $comp_name $bin >/dev/null 2>&1;
+ fi
+
+ cd ../../..
+ echo $os/$arch/$comp_name;
+};
+
+create_cli_packages() {
+ local dirIndex="{\"cli\":{"
+
+ for platform in $platforms; do
+ dirIndex="$dirIndex\"$platform\":{"
+
+ for arch in $archs; do
+ bin=$(get_bin_name $platform)
+ build_cli $platform $arch $bin
+ comp_name=$(get_compressed_name $platform $arch)
+ comp_path=$(compress_binary $comp_name $bin $platform $arch)
+
+ if [ $arch = $default_arch ]; then
+ dirIndex="$dirIndex\"default\":{\"path\":\"$comp_path\"},";
+ fi
+
+ dirIndex="$dirIndex\"$arch\":{\"path\":\"$comp_path\"},";
+ done
+
+ dirIndex="$(echo $dirIndex | rev | cut -c2- | rev)"
+ dirIndex="$dirIndex},";
+ done
+
+ dirIndex="$(echo $dirIndex | rev | cut -c2- | rev)"
+ dirIndex="$dirIndex}}"
+
+ echo $dirIndex > ./build/content.json
+};
+
+platforms="$CLI_OS"
+archs="$CLI_ARCH";
+default_arch="amd64"
+
+create_cli_packages
diff --git a/commands/action.go b/commands/action.go
index 0f446f2..2393a51 100644
--- a/commands/action.go
+++ b/commands/action.go
@@ -24,8 +24,8 @@
"io"
"strings"
- "../../go-whisk/whisk"
- "../wski18n"
+ "github.com/openwhisk/openwhisk-client-go/whisk"
+ "github.com/openwhisk/openwhisk-cli/wski18n"
"github.com/fatih/color"
"github.com/spf13/cobra"
diff --git a/commands/activation.go b/commands/activation.go
index 04c879a..df0b2d5 100644
--- a/commands/activation.go
+++ b/commands/activation.go
@@ -24,8 +24,8 @@
"syscall"
"time"
- "../../go-whisk/whisk"
- "../wski18n"
+ "github.com/openwhisk/openwhisk-client-go/whisk"
+ "github.com/openwhisk/openwhisk-cli/wski18n"
"github.com/fatih/color"
"github.com/spf13/cobra"
diff --git a/commands/api.go b/commands/api.go
index 1a183db..430dc38 100644
--- a/commands/api.go
+++ b/commands/api.go
@@ -23,8 +23,8 @@
"strconv"
"strings"
- "../../go-whisk/whisk"
- "../wski18n"
+ "github.com/openwhisk/openwhisk-client-go/whisk"
+ "github.com/openwhisk/openwhisk-cli/wski18n"
"github.com/fatih/color"
"github.com/spf13/cobra"
diff --git a/commands/commands.go b/commands/commands.go
index 6180c37..30947f7 100644
--- a/commands/commands.go
+++ b/commands/commands.go
@@ -22,8 +22,8 @@
"net/http"
"os"
- "../../go-whisk/whisk"
- "../wski18n"
+ "github.com/openwhisk/openwhisk-client-go/whisk"
+ "github.com/openwhisk/openwhisk-cli/wski18n"
"github.com/spf13/cobra"
)
diff --git a/commands/namespace.go b/commands/namespace.go
index cafed6f..613c7ec 100644
--- a/commands/namespace.go
+++ b/commands/namespace.go
@@ -23,8 +23,8 @@
"github.com/spf13/cobra"
"github.com/fatih/color"
- "../../go-whisk/whisk"
- "../wski18n"
+ "github.com/openwhisk/openwhisk-client-go/whisk"
+ "github.com/openwhisk/openwhisk-cli/wski18n"
)
// namespaceCmd represents the namespace command
diff --git a/commands/package.go b/commands/package.go
index 6020b72..939eff5 100644
--- a/commands/package.go
+++ b/commands/package.go
@@ -21,8 +21,8 @@
"fmt"
"net/http"
- "../../go-whisk/whisk"
- "../wski18n"
+ "github.com/openwhisk/openwhisk-client-go/whisk"
+ "github.com/openwhisk/openwhisk-cli/wski18n"
"github.com/fatih/color"
"github.com/spf13/cobra"
diff --git a/commands/property.go b/commands/property.go
index 5c24956..0ec5f52 100644
--- a/commands/property.go
+++ b/commands/property.go
@@ -25,8 +25,8 @@
"github.com/spf13/cobra"
"github.com/fatih/color"
- "../../go-whisk/whisk"
- "../wski18n"
+ "github.com/openwhisk/openwhisk-client-go/whisk"
+ "github.com/openwhisk/openwhisk-cli/wski18n"
)
var Properties struct {
diff --git a/commands/rule.go b/commands/rule.go
index e29b677..b537aff 100644
--- a/commands/rule.go
+++ b/commands/rule.go
@@ -20,8 +20,8 @@
"errors"
"fmt"
- "../../go-whisk/whisk"
- "../wski18n"
+ "github.com/openwhisk/openwhisk-client-go/whisk"
+ "github.com/openwhisk/openwhisk-cli/wski18n"
"github.com/fatih/color"
"github.com/spf13/cobra"
diff --git a/commands/sdk.go b/commands/sdk.go
index 4fc042b..aed876a 100644
--- a/commands/sdk.go
+++ b/commands/sdk.go
@@ -25,8 +25,8 @@
"github.com/spf13/cobra"
- "../../go-whisk/whisk"
- "../wski18n"
+ "github.com/openwhisk/openwhisk-client-go/whisk"
+ "github.com/openwhisk/openwhisk-cli/wski18n"
)
// sdkCmd represents the sdk command
diff --git a/commands/trigger.go b/commands/trigger.go
index b9d11ca..31cdb27 100644
--- a/commands/trigger.go
+++ b/commands/trigger.go
@@ -20,8 +20,8 @@
"errors"
"fmt"
- "../../go-whisk/whisk"
- "../wski18n"
+ "github.com/openwhisk/openwhisk-client-go/whisk"
+ "github.com/openwhisk/openwhisk-cli/wski18n"
"github.com/spf13/cobra"
"github.com/fatih/color"
diff --git a/commands/util.go b/commands/util.go
index b63efdc..ccabcd1 100644
--- a/commands/util.go
+++ b/commands/util.go
@@ -17,13 +17,14 @@
package commands
import (
+
"bufio"
"errors"
"fmt"
"strings"
- "../../go-whisk/whisk"
- "../wski18n"
+ "github.com/openwhisk/openwhisk-client-go/whisk"
+ "github.com/openwhisk/openwhisk-cli/wski18n"
"github.com/fatih/color"
//prettyjson "github.com/hokaccha/go-prettyjson" // See prettyjson comment below
diff --git a/commands/wsk.go b/commands/wsk.go
index 7290549..beca322 100644
--- a/commands/wsk.go
+++ b/commands/wsk.go
@@ -18,7 +18,7 @@
import (
"github.com/spf13/cobra"
- "../wski18n"
+ "github.com/openwhisk/openwhisk-cli/wski18n"
)
// WskCmd defines the entry point for the cli.
diff --git a/gradle/docker.gradle b/gradle/docker.gradle
new file mode 100644
index 0000000..f716c7b
--- /dev/null
+++ b/gradle/docker.gradle
@@ -0,0 +1,99 @@
+import groovy.time.*
+
+/**
+ * Utility to build docker images based in gradle projects
+ *
+ * This extends gradle's 'application' plugin logic with a 'distDocker' task which builds
+ * a docker image from the Dockerfile of the project that applies this file. The image
+ * is automatically tagged and pushed if a tag and/or a registry is given.
+ *
+ * Parameters that can be set on project level:
+ * - dockerImageName (required): The name of the image to build (e.g. controller)
+ * - dockerRegistry (optional): The registry to push to
+ * - dockerImageTag (optional, default 'latest'): The tag for the image
+ * - dockerImagePrefix (optional, default 'whisk'): The prefix for the image,
+ * 'controller' becomes 'whisk/controller' per default
+ * - dockerTimeout (optional, default 840): Timeout for docker operations in seconds
+ * - dockerRetries (optional, default 3): How many times to retry docker operations
+ * - dockerBinary (optional, default 'docker'): The binary to execute docker commands
+ * - dockerBuildArgs (options, default ''): Project specific custom docker build arguments
+ * - dockerHost (optional): The docker host to run commands on, default behaviour is
+ * docker's own DOCKER_HOST environment variable
+ */
+
+ext {
+ dockerRegistry = project.hasProperty('dockerRegistry') ? dockerRegistry + '/' : ''
+ dockerImageTag = project.hasProperty('dockerImageTag') ? dockerImageTag : 'latest'
+ dockerImagePrefix = project.hasProperty('dockerImagePrefix') ? dockerImagePrefix : 'whisk'
+ dockerTimeout = project.hasProperty('dockerTimeout') ? dockerTimeout.toInteger() : 840
+ dockerRetries = project.hasProperty('dockerRetries') ? dockerRetries.toInteger() : 3
+ dockerBinary = project.hasProperty('dockerBinary') ? [dockerBinary] : ['docker']
+ dockerBuildArg = ['build']
+}
+ext.dockerTaggedImageName = dockerRegistry + dockerImagePrefix + '/' + dockerImageName + ':' + dockerImageTag
+
+if(project.hasProperty('dockerHost')) {
+ dockerBinary += ['--host', project.dockerHost]
+}
+
+if(project.hasProperty('dockerBuildArgs')) {
+ dockerBuildArgs.each { arg ->
+ dockerBuildArg += ['--build-arg', arg]
+ }
+}
+
+task distDocker {
+ doLast {
+ def start = new Date()
+ def cmd = dockerBinary + dockerBuildArg + ['-t', dockerImageName, project.buildscript.sourceFile.getParentFile().getAbsolutePath()]
+ retry(cmd, dockerRetries, dockerTimeout)
+ println("Building '${dockerImageName}' took ${TimeCategory.minus(new Date(), start)}")
+ }
+}
+task tagImage {
+ doLast {
+ def versionString = (dockerBinary + ['-v']).execute().text
+ def matched = (versionString =~ /(\d+)\.(\d+)\.(\d+)/)
+
+ def major = matched[0][1] as int
+ def minor = matched[0][2] as int
+
+ def dockerCmd = ['tag']
+ if(major == 1 && minor < 12) {
+ dockerCmd += ['-f']
+ }
+ retry(dockerBinary + dockerCmd + [dockerImageName, dockerTaggedImageName], dockerRetries, dockerTimeout)
+ }
+}
+
+task pushImage {
+ doLast {
+ def cmd = dockerBinary + ['push', dockerTaggedImageName]
+ retry(cmd, dockerRetries, dockerTimeout)
+ }
+}
+pushImage.dependsOn tagImage
+pushImage.onlyIf { dockerRegistry != '' }
+distDocker.finalizedBy pushImage
+
+def retry(cmd, retries, timeout) {
+ println("${new Date()}: Executing '${cmd.join(" ")}'")
+ def proc = cmd.execute()
+ proc.consumeProcessOutput(System.out, System.err)
+ proc.waitForOrKill(timeout * 1000)
+ if(proc.exitValue() != 0) {
+ def message = "${new Date()}: Command '${cmd.join(" ")}' failed with exitCode ${proc.exitValue()}"
+ if(proc.exitValue() == 143) { // 143 means the process was killed (SIGTERM signal)
+ message = "${new Date()}: Command '${cmd.join(" ")}' was killed after ${timeout} seconds"
+ }
+
+ if(retries > 1) {
+ println("${message}, ${retries-1} retries left, retrying...")
+ retry(cmd, retries-1, timeout)
+ }
+ else {
+ println("${message}, no more retries left, aborting...")
+ throw new GradleException(message)
+ }
+ }
+}
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..ca78035
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..cd3a0f7
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 05 17:13:49 EDT 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-bin.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..27309d9
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100755
index 0000000..f6d5974
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/main.go b/main.go
index a26f8fc..9ee44cb 100644
--- a/main.go
+++ b/main.go
@@ -23,9 +23,9 @@
goi18n "github.com/nicksnyder/go-i18n/i18n"
"github.com/fatih/color"
- "../go-whisk/whisk"
- "./commands"
- "./wski18n"
+ "github.com/openwhisk/openwhisk-client-go/whisk"
+ "github.com/openwhisk/openwhisk-cli/commands"
+ "github.com/openwhisk/openwhisk-cli/wski18n"
"github.com/mattn/go-colorable"
)
diff --git a/tests/src/dat/empty.js b/tests/src/dat/empty.js
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/src/dat/empty.js
diff --git a/tests/src/dat/hello.js b/tests/src/dat/hello.js
new file mode 100644
index 0000000..c40254d
--- /dev/null
+++ b/tests/src/dat/hello.js
@@ -0,0 +1,8 @@
+/**
+ * Hello, world.
+ */
+function main(params) {
+ greeting = 'hello, ' + params.payload + '!'
+ console.log(greeting);
+ return {payload: greeting}
+}
\ No newline at end of file
diff --git a/tests/src/dat/invalidInput1.json b/tests/src/dat/invalidInput1.json
new file mode 100644
index 0000000..da57a79
--- /dev/null
+++ b/tests/src/dat/invalidInput1.json
@@ -0,0 +1,3 @@
+{
+ "invalidJSON":
+}
diff --git a/tests/src/dat/invalidInput2.json b/tests/src/dat/invalidInput2.json
new file mode 100644
index 0000000..275fa9f
--- /dev/null
+++ b/tests/src/dat/invalidInput2.json
@@ -0,0 +1,4 @@
+{
+ "invalid": "JS
+ ON"
+}
diff --git a/tests/src/dat/invalidInput3.json b/tests/src/dat/invalidInput3.json
new file mode 100644
index 0000000..3272df5
--- /dev/null
+++ b/tests/src/dat/invalidInput3.json
@@ -0,0 +1,2 @@
+{
+ "invalid": "JSON"
diff --git a/tests/src/dat/invalidInput4.json b/tests/src/dat/invalidInput4.json
new file mode 100644
index 0000000..63c9ade
--- /dev/null
+++ b/tests/src/dat/invalidInput4.json
@@ -0,0 +1,3 @@
+{
+ "invalid": "JS"ON"
+}
\ No newline at end of file
diff --git a/tests/src/dat/malformed.js b/tests/src/dat/malformed.js
new file mode 100644
index 0000000..587be6b
--- /dev/null
+++ b/tests/src/dat/malformed.js
@@ -0,0 +1 @@
+x
diff --git a/tests/src/integration/command_test.go b/tests/src/integration/command_test.go
new file mode 100644
index 0000000..599cb7f
--- /dev/null
+++ b/tests/src/integration/command_test.go
@@ -0,0 +1,549 @@
+// +build native
+
+package tests
+
+import (
+ "testing"
+ "os"
+ "github.com/stretchr/testify/assert"
+ "github.com/openwhisk/openwhisk-cli/tests/src/integration/common"
+)
+
+var wsk *common.Wsk = common.NewWsk()
+var tmpProp = os.Getenv("GOPATH") + "/src/github.com/openwhisk/openwhisk-cli/wskprops.tmp"
+var invalidArgs []common.InvalidArg
+var invalidParamMsg = "Arguments for '-p' must be a key/value pair"
+var invalidAnnotMsg = "Arguments for '-a' must be a key/value pair"
+var invalidParamFileMsg = "An argument must be provided for '-P'"
+var invalidAnnotFileMsg = "An argument must be provided for '-A'"
+
+var emptyFile = common.GetTestActionFilename("emtpy.js")
+var helloFile = common.GetTestActionFilename("hello.js")
+var missingFile = "notafile"
+var emptyFileMsg = "File '" + emptyFile + "' is not a valid file or it does not exist"
+var missingFileMsg = "File '" + missingFile + "' is not a valid file or it does not exist"
+
+// Test case to check if the binary exits.
+func TestWskExist(t *testing.T) {
+ assert.True(t, wsk.Exists(), "The binary should exist.")
+}
+
+func TestHelpUsageInfoCommand(t *testing.T) {
+ stdout, err := wsk.RunCommand("-h")
+ assert.Equal(t, nil, err, "The command -h failed to run.")
+ assert.Contains(t, string(stdout), "Usage:", "The output of the command -h does not contain \"Usage\".")
+ assert.Contains(t, string(stdout), "Flags:", "The output of the command -h does not contain \"Flags\".")
+ assert.Contains(t, string(stdout), "Available Commands:",
+ "The output of the command -h does not contain \"Available Commands\".")
+ assert.Contains(t, string(stdout), "--help", "The output of the command -h does not contain \"--help\".")
+}
+
+func TestHelpUsageInfoCommandLanguage(t *testing.T) {
+ os.Setenv("LANG", "de_DE")
+ assert.Equal(t, "de_DE", os.Getenv("LANG"), "The environment variable LANG has not been set to de_DE.")
+ TestHelpUsageInfoCommand(t)
+}
+
+func TestShowCLIBuildVersion(t *testing.T) {
+ stdout, err := wsk.RunCommand("property", "get", "--cliversion")
+ assert.Equal(t, nil, err, "The command property get --cliversion failed to run.")
+ assert.Contains(t, string(stdout), "whisk CLI version",
+ "The output of the command property get --cliversion does not contain \"whisk CLI version\".")
+}
+
+func TestShowAPIVersion(t *testing.T) {
+ stdout, err := wsk.RunCommand("property", "get", "--apiversion")
+ assert.Equal(t, nil, err, "The command property get --apiversion failed to run.")
+ assert.Contains(t, string(stdout), "whisk API version",
+ "The output of the command property get --apiversion does not contain \"whisk API version\".")
+}
+
+// Test case to verify the default namespace _.
+func TestDefaultNamespace(t *testing.T) {
+ common.CreateFile(tmpProp)
+ common.WriteFile(tmpProp, []string{"NAMESPACE="})
+
+ os.Setenv("WSK_CONFIG_FILE", tmpProp)
+ assert.Equal(t, os.Getenv("WSK_CONFIG_FILE"), tmpProp, "The environment variable WSK_CONFIG_FILE has not been set.")
+
+ stdout, err := wsk.RunCommand("property", "get", "-i", "--namespace")
+ assert.Equal(t, nil, err, "The command property get -i --namespace failed to run.")
+ assert.Contains(t, common.RemoveRedundentSpaces(string(stdout)), "whisk namespace _",
+ "The output of the command does not contain \"whisk namespace _\".")
+ common.DeleteFile(tmpProp)
+}
+
+// Test case to validate default property values.
+func TestValidateDefaultProperties(t *testing.T) {
+ common.CreateFile(tmpProp)
+
+ os.Setenv("WSK_CONFIG_FILE", tmpProp)
+ assert.Equal(t, os.Getenv("WSK_CONFIG_FILE"), tmpProp, "The environment variable WSK_CONFIG_FILE has not been set.")
+
+ stdout, err := wsk.RunCommand("property", "unset", "--auth", "--apihost", "--apiversion", "--namespace")
+ assert.Equal(t, nil, err, "The command property unset failed to run.")
+ outputString := string(stdout)
+ assert.Contains(t, outputString, "ok: whisk auth unset",
+ "The output of the command does not contain \"ok: whisk auth unset\".")
+ assert.Contains(t, outputString, "ok: whisk API host unset",
+ "The output of the command does not contain \"ok: whisk API host unset\".")
+ assert.Contains(t, outputString, "ok: whisk API version unset",
+ "The output of the command does not contain \"ok: whisk API version unset\".")
+ assert.Contains(t, outputString, "ok: whisk namespace unset",
+ "The output of the command does not contain \"ok: whisk namespace unset\".")
+
+ stdout, err = wsk.RunCommand("property", "get", "--auth")
+ assert.Equal(t, nil, err, "The command property get --auth failed to run.")
+ assert.Equal(t, common.RemoveRedundentSpaces(string(stdout)), "whisk auth",
+ "The output of the command does not equal to \"whisk auth\".")
+
+ stdout, err = wsk.RunCommand("property", "get", "--apihost")
+ assert.Equal(t, nil, err, "The command property get --apihost failed to run.")
+ assert.Equal(t, common.RemoveRedundentSpaces(string(stdout)), "whisk API host",
+ "The output of the command does not equal to \"whisk API host\".")
+
+ stdout, err = wsk.RunCommand("property", "get", "--namespace")
+ assert.Equal(t, nil, err, "The command property get --namespace failed to run.")
+ assert.Equal(t, common.RemoveRedundentSpaces(string(stdout)), "whisk namespace _",
+ "The output of the command does not equal to \"whisk namespace _\".")
+
+ common.DeleteFile(tmpProp)
+}
+
+// Test case to set auth in property file.
+func TestSetAuth(t *testing.T) {
+ common.CreateFile(tmpProp)
+
+ os.Setenv("WSK_CONFIG_FILE", tmpProp)
+ assert.Equal(t, os.Getenv("WSK_CONFIG_FILE"), tmpProp, "The environment variable WSK_CONFIG_FILE has not been set.")
+
+ _, err := wsk.RunCommand("property", "set", "--auth", "testKey")
+ assert.Equal(t, nil, err, "The command property set --auth testKey failed to run.")
+ output := common.ReadFile(tmpProp)
+ assert.Contains(t, output, "AUTH=testKey",
+ "The wsk property file does not contain \"AUTH=testKey\".")
+ common.DeleteFile(tmpProp)
+}
+
+// Test case to set multiple property values with single command.
+func TestSetMultipleValues(t *testing.T) {
+ common.CreateFile(tmpProp)
+
+ os.Setenv("WSK_CONFIG_FILE", tmpProp)
+ assert.Equal(t, os.Getenv("WSK_CONFIG_FILE"), tmpProp, "The environment variable WSK_CONFIG_FILE has not been set.")
+
+ _, err := wsk.RunCommand("property", "set", "--auth", "testKey", "--apihost", "openwhisk.ng.bluemix.net",
+ "--apiversion", "v1")
+ assert.Equal(t, nil, err, "The command property set --auth --apihost --apiversion failed to run.")
+ output := common.ReadFile(tmpProp)
+ assert.Contains(t, output, "AUTH=testKey", "The wsk property file does not contain \"AUTH=testKey\".")
+ assert.Contains(t, output, "APIHOST=openwhisk.ng.bluemix.net",
+ "The wsk property file does not contain \"APIHOST=openwhisk.ng.bluemix.net\".")
+ assert.Contains(t, output, "APIVERSION=v1", "The wsk property file does not contain \"APIVERSION=v1\".")
+ common.DeleteFile(tmpProp)
+}
+
+// Test case to reject bad command.
+func TestRejectBadComm(t *testing.T) {
+ common.CreateFile(tmpProp)
+
+ os.Setenv("WSK_CONFIG_FILE", tmpProp)
+ assert.Equal(t, os.Getenv("WSK_CONFIG_FILE"), tmpProp, "The environment variable WSK_CONFIG_FILE has not been set.")
+
+ stdout, err := wsk.RunCommand("bogus")
+ assert.NotEqual(t, nil, err, "The command bogus should fail to run.")
+ assert.Contains(t, string(stdout), "Run 'wsk --help' for usage",
+ "The output of the command does not contain \"Run 'wsk --help' for usage\".")
+ common.DeleteFile(tmpProp)
+}
+
+// Test case to reject a command when the API host is not set.
+func TestRejectCommAPIHostNotSet(t *testing.T) {
+ common.CreateFile(tmpProp)
+
+ os.Setenv("WSK_CONFIG_FILE", tmpProp)
+ assert.Equal(t, os.Getenv("WSK_CONFIG_FILE"), tmpProp, "The environment variable WSK_CONFIG_FILE has not been set.")
+
+ stdout, err := wsk.RunCommand("property", "get")
+ assert.NotEqual(t, nil, err, "The command property get --apihost --apiversion should fail to run.")
+ assert.Contains(t, common.RemoveRedundentSpaces(string(stdout)),
+ "The API host is not valid: An API host must be provided",
+ "The output of the command does not contain \"The API host is not valid: An API host must be provided\".")
+ common.DeleteFile(tmpProp)
+}
+
+func initInvalidArgsNotEnoughParamsArgs() {
+ invalidArgs = []common.InvalidArg{
+ common.InvalidArg{
+ Cmd: []string{"action", "create", "actionName", "-p"},
+ Err: invalidParamMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "create", "actionName", "-p", "key"},
+ Err: invalidParamMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "create", "actionName", "-P"},
+ Err: invalidParamFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "update", "actionName", "-p"},
+ Err: invalidParamMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "update", "actionName", "-p", "key"},
+ Err: invalidParamMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "update", "actionName", "-P"},
+ Err: invalidParamFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "invoke", "actionName", "-p"},
+ Err: invalidParamMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "invoke", "actionName", "-p", "key"},
+ Err: invalidParamMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "invoke", "actionName", "-P"},
+ Err: invalidParamFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "create", "actionName", "-a"},
+ Err: invalidAnnotMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "create", "actionName", "-a", "key"},
+ Err: invalidAnnotMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "create", "actionName", "-A"},
+ Err: invalidAnnotFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "update", "actionName", "-a"},
+ Err: invalidAnnotMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "update", "actionName", "-a", "key"},
+ Err: invalidAnnotMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "update", "actionName", "-A"},
+ Err: invalidAnnotFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "invoke", "actionName", "-a"},
+ Err: invalidAnnotMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "invoke", "actionName", "-a", "key"},
+ Err: invalidAnnotMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "invoke", "actionName", "-A"},
+ Err: invalidAnnotFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "create", "packageName", "-p"},
+ Err: invalidParamMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "create", "packageName", "-p", "key"},
+ Err: invalidParamMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "create", "packageName", "-P"},
+ Err: invalidParamFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "update", "packageName", "-p"},
+ Err: invalidParamMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "update", "packageName", "-p", "key"},
+ Err: invalidParamMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "update", "packageName", "-P"},
+ Err: invalidParamFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "bind", "packageName", "boundPackageName", "-p"},
+ Err: invalidParamMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "bind", "packageName", "boundPackageName", "-p", "key"},
+ Err: invalidParamMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "bind", "packageName", "boundPackageName", "-P"},
+ Err: invalidParamFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "create", "packageName", "-a"},
+ Err: invalidAnnotMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "create", "packageName", "-a", "key"},
+ Err: invalidAnnotMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "create", "packageName", "-A"},
+ Err: invalidAnnotFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "update", "packageName", "-a"},
+ Err: invalidAnnotMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "update", "packageName", "-a", "key"},
+ Err: invalidAnnotMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "update", "packageName", "-A"},
+ Err: invalidAnnotFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "bind", "packageName", "boundPackageName", "-a"},
+ Err: invalidAnnotMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "bind", "packageName", "boundPackageName", "-a", "key"},
+ Err: invalidAnnotMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "bind", "packageName", "boundPackageName", "-A"},
+ Err: invalidAnnotFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "create", "triggerName", "-p"},
+ Err: invalidParamMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "create", "triggerName", "-p", "key"},
+ Err: invalidParamMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "create", "triggerName", "-P"},
+ Err: invalidParamFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "update", "triggerName", "-p"},
+ Err: invalidParamMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "update", "triggerName", "-p", "key"},
+ Err: invalidParamMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "update", "triggerName", "-P"},
+ Err: invalidParamFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "fire", "triggerName", "-p"},
+ Err: invalidParamMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "fire", "triggerName", "-p", "key"},
+ Err: invalidParamMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "fire", "triggerName", "-P"},
+ Err: invalidParamFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "create", "triggerName", "-a"},
+ Err: invalidAnnotMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "create", "triggerName", "-a", "key"},
+ Err: invalidAnnotMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "create", "triggerName", "-A"},
+ Err: invalidAnnotFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "update", "triggerName", "-a"},
+ Err: invalidAnnotMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "update", "triggerName", "-a", "key"},
+ Err: invalidAnnotMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "update", "triggerName", "-A"},
+ Err: invalidAnnotFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "fire", "triggerName", "-a"},
+ Err: invalidAnnotMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "fire", "triggerName", "-a", "key"},
+ Err: invalidAnnotMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "fire", "triggerName", "-A"},
+ Err: invalidAnnotFileMsg,
+ },
+ }
+}
+
+func initInvalidArgsMissingInvalidParamsAnno(){
+ invalidArgs = []common.InvalidArg{
+ common.InvalidArg{
+ Cmd: []string{"action", "create", "actionName", helloFile, "-P", emptyFile},
+ Err: emptyFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "update", "actionName", helloFile, "-P", emptyFile},
+ Err: emptyFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "invoke", "actionName", "-P", emptyFile},
+ Err: emptyFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "create", "actionName", "-P", emptyFile},
+ Err: emptyFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "update", "actionName", "-P", emptyFile},
+ Err: emptyFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "invoke", "actionName", "-P", emptyFile},
+ Err: emptyFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "create", "packageName", "-P", emptyFile},
+ Err: emptyFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "update", "packageName", "-P", emptyFile},
+ Err: emptyFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "bind", "packageName", "boundPackageName", "-P", emptyFile},
+ Err: emptyFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "create", "packageName", "-P", emptyFile},
+ Err: emptyFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "update", "packageName", "-P", emptyFile},
+ Err: emptyFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "bind", "packageName", "boundPackageName", "-P", emptyFile},
+ Err: emptyFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "create", "triggerName", "-P", emptyFile},
+ Err: emptyFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "update", "triggerName", "-P", emptyFile},
+ Err: emptyFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "fire", "triggerName", "-P", emptyFile},
+ Err: emptyFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "create", "triggerName", "-P", emptyFile},
+ Err: emptyFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "update", "triggerName", "-P", emptyFile},
+ Err: emptyFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "fire", "triggerName", "-P", emptyFile},
+ Err: emptyFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "create", "actionName", helloFile, "-A", missingFile},
+ Err: missingFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "update", "actionName", helloFile, "-A", missingFile},
+ Err: missingFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "invoke", "actionName", "-A", missingFile},
+ Err: missingFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "create", "actionName", "-A", missingFile},
+ Err: missingFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "update", "actionName", "-A", missingFile},
+ Err: missingFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "invoke", "actionName", "-A", missingFile},
+ Err: missingFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "create", "packageName", "-A", missingFile},
+ Err: missingFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "update", "packageName", "-A", missingFile},
+ Err: missingFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "bind", "packageName", "boundPackageName", "-A", missingFile},
+ Err: missingFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "create", "triggerName", "-A", missingFile},
+ Err: missingFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "update", "triggerName", "-A", missingFile},
+ Err: missingFileMsg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "fire", "triggerName", "-A", missingFile},
+ Err: missingFileMsg,
+ },
+ }
+}
+
+// Test case to reject commands that are executed with not enough param or annotation arguments.
+func TestRejectCommandsNotEnoughParamsArgs(t *testing.T) {
+ initInvalidArgsNotEnoughParamsArgs()
+ for _, invalidArg := range invalidArgs {
+ stdout, err := wsk.RunCommand(invalidArg.Cmd...)
+ outputString := string(stdout)
+ assert.NotEqual(t, nil, err, "The command should fail to run.")
+ assert.Equal(t, "exit status 1", err.Error(), "The error should be exit status 1.")
+ assert.Contains(t, outputString, invalidArg.Err,
+ "The output of the command does not contain " + invalidArg.Err)
+ assert.Contains(t, outputString, "Run 'wsk --help' for usage",
+ "The output of the command does not contain \"Run 'wsk --help' for usage\".")
+ }
+}
+
+// Test case to reject commands that are executed with a missing or invalid parameter or annotation file.
+func TestRejectCommandsMissingIvalidParamsAnno(t *testing.T) {
+ initInvalidArgsMissingInvalidParamsAnno()
+ for _, invalidArg := range invalidArgs {
+ stdout, err := wsk.RunCommand(invalidArg.Cmd...)
+ outputString := string(stdout)
+ assert.NotEqual(t, nil, err, "The command should fail to run.")
+ assert.Equal(t, "exit status 2", err.Error(), "The error should be exit status 1.")
+ assert.Contains(t, outputString, invalidArg.Err,
+ "The output of the command does not contain " + invalidArg.Err)
+ assert.Contains(t, outputString, "Run 'wsk --help' for usage",
+ "The output of the command does not contain \"Run 'wsk --help' for usage\".")
+ }
+}
diff --git a/tests/src/integration/common/utils.go b/tests/src/integration/common/utils.go
new file mode 100644
index 0000000..1830490
--- /dev/null
+++ b/tests/src/integration/common/utils.go
@@ -0,0 +1,79 @@
+package common
+
+import (
+ "fmt"
+ "os"
+ "regexp"
+ "io"
+)
+
+func checkError(err error) {
+ if err != nil {
+ fmt.Println(err.Error())
+ os.Exit(0)
+ }
+}
+
+func CreateFile(filePath string) {
+ var _, err = os.Stat(filePath)
+
+ if os.IsNotExist(err) {
+ var file, err = os.Create(filePath)
+ checkError(err)
+ defer file.Close()
+ }
+ return
+}
+
+func ReadFile(filePath string) string {
+ var file, err = os.OpenFile(filePath, os.O_RDWR, 0644)
+ checkError(err)
+ defer file.Close()
+
+ var text = make([]byte, 1024)
+ for {
+ n, err := file.Read(text)
+ if err != io.EOF {
+ checkError(err)
+ }
+ if n == 0 {
+ break
+ }
+ }
+ return string(text)
+}
+
+func WriteFile(filePath string, lines []string) {
+ var file, err = os.OpenFile(filePath, os.O_RDWR, 0644)
+ checkError(err)
+ defer file.Close()
+
+ for _, each := range lines {
+ _, err = file.WriteString(each + "\n")
+ checkError(err)
+ }
+
+ err = file.Sync()
+ checkError(err)
+}
+
+func DeleteFile(filePath string) {
+ var err = os.Remove(filePath)
+ checkError(err)
+}
+
+func RemoveRedundentSpaces(str string) string {
+ re_leadclose_whtsp := regexp.MustCompile(`^[\s\p{Zs}]+|[\s\p{Zs}]+$`)
+ re_inside_whtsp := regexp.MustCompile(`[\s\p{Zs}]{2,}`)
+ final := re_leadclose_whtsp.ReplaceAllString(str, "")
+ return re_inside_whtsp.ReplaceAllString(final, " ")
+}
+
+func GetTestActionFilename(fileName string) string {
+ return os.Getenv("GOPATH") + "/src/github.com/openwhisk/openwhisk-cli/tests/src/dat/" + fileName
+}
+
+type InvalidArg struct {
+ Cmd []string
+ Err string
+}
\ No newline at end of file
diff --git a/tests/src/integration/common/wsk.go b/tests/src/integration/common/wsk.go
new file mode 100644
index 0000000..78716c9
--- /dev/null
+++ b/tests/src/integration/common/wsk.go
@@ -0,0 +1,51 @@
+package common
+
+import (
+ "os"
+ "os/exec"
+)
+
+const cmd = "wsk"
+const arg = "-i"
+
+type Wsk struct {
+ Path string
+ Arg []string
+ Dir string
+ Wskprops *Wskprops
+}
+
+func NewWsk() *Wsk {
+ return NewWskWithPath(os.Getenv("GOPATH") + "/src/github.com/openwhisk/openwhisk-cli/")
+}
+
+func NewWskWithPath(path string) *Wsk {
+ var dep Wsk
+ dep.Path = cmd
+ dep.Arg = []string{arg}
+ dep.Dir = path
+ dep.Wskprops = GetWskprops()
+ return &dep
+}
+
+func (wsk *Wsk)Exists() bool {
+ _, err := os.Stat(wsk.Dir + wsk.Path);
+ if err == nil {
+ return true
+ } else {
+ return false
+ }
+}
+
+func (wsk *Wsk)RunCommand(s ...string) ([]byte, error) {
+ cs := wsk.Arg
+ cs = append(cs, s...)
+ command := exec.Command(wsk.Path, cs...)
+ command.Dir = wsk.Dir
+ return command.CombinedOutput()
+}
+
+func (wsk *Wsk)ListNamespaces() ([]byte, error) {
+ return wsk.RunCommand("namespace", "list", "--apihost", wsk.Wskprops.APIHost,
+ "--auth", wsk.Wskprops.AuthKey)
+}
\ No newline at end of file
diff --git a/tests/src/integration/common/wskprops.go b/tests/src/integration/common/wskprops.go
new file mode 100644
index 0000000..ae493c1
--- /dev/null
+++ b/tests/src/integration/common/wskprops.go
@@ -0,0 +1,39 @@
+package common
+
+import (
+ "github.com/spf13/viper"
+ "io/ioutil"
+ "os"
+)
+
+type Wskprops struct {
+ APIHost string
+ APIVersion string
+ AuthKey string
+ ControllerHost string
+ ControllerPort string
+}
+
+func GetWskprops() *Wskprops {
+ var dep Wskprops
+ dep.APIHost = ""
+ dep.AuthKey = ""
+ dep.APIVersion = "v1"
+
+ viper.SetConfigName("whisk")
+ viper.AddConfigPath(os.Getenv("OPENWHISK_HOME"))
+
+ err := viper.ReadInConfig()
+ if err == nil {
+ authPath := viper.GetString("testing.auth")
+
+ b, err := ioutil.ReadFile(authPath)
+ if err == nil {
+ dep.AuthKey = string(b)
+ }
+ dep.APIHost = viper.GetString("router.host")
+ dep.ControllerHost = viper.GetString("router.host")
+ dep.ControllerPort = viper.GetString("controller.host.port")
+ }
+ return &dep
+}
diff --git a/tests/src/integration/integration_test.go b/tests/src/integration/integration_test.go
new file mode 100644
index 0000000..d46e108
--- /dev/null
+++ b/tests/src/integration/integration_test.go
@@ -0,0 +1,597 @@
+// +build integration
+
+package tests
+
+import (
+ "testing"
+ "github.com/stretchr/testify/assert"
+ "github.com/openwhisk/openwhisk-cli/tests/src/integration/common"
+ "os"
+ "strings"
+)
+
+var invalidArgs []common.InvalidArg
+var invalidArgsMsg = "error: Invalid argument(s)"
+var tooFewArgsMsg = invalidArgsMsg + "."
+var tooManyArgsMsg = invalidArgsMsg + ": "
+var actionNameActionReqMsg = "An action name and action are required."
+var actionNameReqMsg = "An action name is required."
+var actionOptMsg = "An action is optional."
+var packageNameReqMsg = "A package name is required."
+var packageNameBindingReqMsg = "A package name and binding name are required."
+var ruleNameReqMsg = "A rule name is required."
+var ruleTriggerActionReqMsg = "A rule, trigger and action name are required."
+var activationIdReq = "An activation ID is required."
+var triggerNameReqMsg = "A trigger name is required."
+var optNamespaceMsg = "An optional namespace is the only valid argument."
+var optPayloadMsg = "A payload is optional."
+var noArgsReqMsg = "No arguments are required."
+var invalidArg = "invalidArg"
+var apiCreateReqMsg = "Specify a swagger file or specify an API base path with an API path, an API verb, and an action name."
+var apiGetReqMsg = "An API base path or API name is required."
+var apiDeleteReqMsg = "An API base path or API name is required. An optional API relative path and operation may also be provided."
+var apiListReqMsg = "Optional parameters are: API base path (or API name), API relative path and operation."
+var invalidShared = "Cannot use value '" + invalidArg + "' for shared"
+
+func initInvalidArgs() {
+ invalidArgs = []common.InvalidArg{
+ common.InvalidArg {
+ Cmd: []string{"api-experimental", "create"},
+ Err: tooFewArgsMsg + " " + apiCreateReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"api-experimental", "create", "/basepath", "/path", "GET", "action", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ". " + apiCreateReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"api-experimental", "get"},
+ Err: tooFewArgsMsg + " " + apiGetReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"api-experimental", "get", "/basepath", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ". " + apiGetReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"api-experimental", "delete"},
+ Err: tooFewArgsMsg + " " + apiDeleteReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"api-experimental", "delete", "/basepath", "/path", "GET", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ". " + apiDeleteReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"api-experimental", "list", "/basepath", "/path", "GET", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ". " + apiListReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"action", "create"},
+ Err: tooFewArgsMsg + " " + actionNameActionReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"action", "create", "someAction"},
+ Err: tooFewArgsMsg + " " + actionNameActionReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"action", "create", "actionName", "artifactName", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"action", "update"},
+ Err: tooFewArgsMsg + " " + actionNameReqMsg + " " + actionOptMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"action", "update", "actionName", "artifactName", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ". " + actionNameReqMsg + " " + actionOptMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"action", "delete"},
+ Err: tooFewArgsMsg + " " + actionNameReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"action", "delete", "actionName", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"action", "get"},
+ Err: tooFewArgsMsg + " " + actionNameReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"action", "get", "actionName", "namespace", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"action", "list", "namespace", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ". " + optNamespaceMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"action", "invoke"},
+ Err: tooFewArgsMsg + " " + actionNameReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"action", "invoke", "actionName", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"activation", "list", "namespace", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ". " + optNamespaceMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"activation", "get"},
+ Err: tooFewArgsMsg + " " + activationIdReq,
+ },
+ common.InvalidArg {
+ Cmd: []string{"activation", "get", "activationID", "namespace", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"activation", "logs"},
+ Err: tooFewArgsMsg + " " + activationIdReq,
+ },
+ common.InvalidArg {
+ Cmd: []string{"activation", "logs", "activationID", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+
+ common.InvalidArg {
+ Cmd: []string{"activation", "result"},
+ Err: tooFewArgsMsg + " " + activationIdReq,
+ },
+ common.InvalidArg {
+ Cmd: []string{"activation", "result", "activationID", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"activation", "poll", "activationID", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ". " + optNamespaceMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"namespace", "list", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ". " + noArgsReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"namespace", "get", "namespace", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ". " + optNamespaceMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"package", "create"},
+ Err: tooFewArgsMsg + " " + packageNameReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"package", "create", "packageName", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"package", "create", "packageName", "--shared", invalidArg},
+ Err: invalidShared,
+ },
+ common.InvalidArg {
+ Cmd: []string{"package", "update"},
+ Err: tooFewArgsMsg + " " + packageNameReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"package", "update", "packageName", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"package", "update", "packageName", "--shared", invalidArg},
+ Err: invalidShared,
+ },
+ common.InvalidArg {
+ Cmd: []string{"package", "get"},
+ Err: tooFewArgsMsg + " " +packageNameReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"package", "get", "packageName", "namespace", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"package", "bind"},
+ Err: tooFewArgsMsg + " " + packageNameBindingReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"package", "bind", "packageName"},
+ Err: tooFewArgsMsg + " " +packageNameBindingReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"package", "bind", "packageName", "bindingName", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"package", "list", "namespace", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ". " + optNamespaceMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"package", "delete"},
+ Err: tooFewArgsMsg + " " + packageNameReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"package", "delete", "namespace", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"package", "refresh", "namespace", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ". " + optNamespaceMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"rule", "enable"},
+ Err: tooFewArgsMsg + " " + ruleNameReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"rule", "enable", "ruleName", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"rule", "disable"},
+ Err: tooFewArgsMsg + " " + ruleNameReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"rule", "disable", "ruleName", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"rule", "status"},
+ Err: tooFewArgsMsg + " " + ruleNameReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"rule", "status", "ruleName", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"rule", "create"},
+ Err: tooFewArgsMsg + " " + ruleTriggerActionReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"rule", "create", "ruleName"},
+ Err: tooFewArgsMsg + " " + ruleTriggerActionReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"rule", "create", "ruleName", "triggerName"},
+ Err: tooFewArgsMsg + " " + ruleTriggerActionReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"rule", "create", "ruleName", "triggerName", "actionName", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+
+ common.InvalidArg {
+ Cmd: []string{"rule", "update"},
+ Err: tooFewArgsMsg + " " + ruleTriggerActionReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"rule", "update", "ruleName"},
+ Err: tooFewArgsMsg + " " + ruleTriggerActionReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"rule", "update", "ruleName", "triggerName"},
+ Err: tooFewArgsMsg + " " + ruleTriggerActionReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"rule", "update", "ruleName", "triggerName", "actionName", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"rule", "get"},
+ Err: tooFewArgsMsg + " " + ruleNameReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"rule", "get", "ruleName", "namespace", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"rule", "delete"},
+ Err: tooFewArgsMsg + " " + ruleNameReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"rule", "delete", "ruleName", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"rule", "list", "namespace", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ". " + optNamespaceMsg,
+ },
+
+ common.InvalidArg {
+ Cmd: []string{"trigger", "fire"},
+ Err: tooFewArgsMsg + " " + triggerNameReqMsg + " " + optPayloadMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"trigger", "fire", "triggerName", "triggerPayload", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ". " + triggerNameReqMsg + " " +optPayloadMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"trigger", "create"},
+ Err: tooFewArgsMsg + " " + triggerNameReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"trigger", "create", "triggerName", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"trigger", "update"},
+ Err: tooFewArgsMsg + " " + triggerNameReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"trigger", "update", "triggerName", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+
+ common.InvalidArg {
+ Cmd: []string{"trigger", "get"},
+ Err: tooFewArgsMsg + " " + triggerNameReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"trigger", "get", "triggerName", "namespace", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"trigger", "delete"},
+ Err: tooFewArgsMsg + " " + triggerNameReqMsg,
+ },
+ common.InvalidArg {
+ Cmd: []string{"trigger", "delete", "triggerName", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ".",
+ },
+ common.InvalidArg {
+ Cmd: []string{"trigger", "list", "namespace", invalidArg},
+ Err: tooManyArgsMsg + invalidArg + ". " + optNamespaceMsg,
+ },
+ }
+}
+
+var wsk *common.Wsk = common.NewWsk()
+var tmpProp = os.Getenv("GOPATH") + "/src/github.com/openwhisk/openwhisk-cli/wskprops.tmp"
+
+// Test case to set apihost, auth, and namespace.
+func TestSetAPIHostAuthNamespace(t *testing.T) {
+ common.CreateFile(tmpProp)
+ common.WriteFile(tmpProp, []string{})
+
+ os.Setenv("WSK_CONFIG_FILE", tmpProp)
+ assert.Equal(t, os.Getenv("WSK_CONFIG_FILE"), tmpProp, "The environment variable WSK_CONFIG_FILE has not been set.")
+
+ namespace, _ := wsk.ListNamespaces()
+ namespaces := strings.Split(strings.TrimSpace(string(namespace)), "\n")
+ expectedNamespace := string(namespaces[len(namespaces) - 1])
+ if (wsk.Wskprops.APIHost != "" && wsk.Wskprops.APIHost != "") {
+ stdout, err := wsk.RunCommand("property", "set", "--apihost", wsk.Wskprops.APIHost,
+ "--auth", wsk.Wskprops.AuthKey, "--namespace", expectedNamespace)
+ ouputString := string(stdout)
+ assert.Equal(t, nil, err, "The command property set --apihost --auth --namespace failed to run.")
+ assert.Contains(t, ouputString, "ok: whisk auth set to " + wsk.Wskprops.AuthKey,
+ "The output of the command property set --apihost --auth --namespace does not contain \"whisk auth key setting\".")
+ assert.Contains(t, ouputString, "ok: whisk API host set to " + wsk.Wskprops.APIHost,
+ "The output of the command property set --apihost --auth --namespace does not contain \"whisk API host setting\".")
+ assert.Contains(t, ouputString, "ok: whisk namespace set to " + expectedNamespace,
+ "The output of the command property set --apihost --auth --namespace does not contain \"whisk namespace setting\".")
+ }
+ common.DeleteFile(tmpProp)
+}
+
+// Test case to show api build version using property file.
+func TestShowAPIBuildVersion(t *testing.T) {
+ common.CreateFile(tmpProp)
+
+ os.Setenv("WSK_CONFIG_FILE", tmpProp)
+ assert.Equal(t, os.Getenv("WSK_CONFIG_FILE"), tmpProp, "The environment variable WSK_CONFIG_FILE has not been set.")
+
+ stdout, err := wsk.RunCommand("property", "set", "--apihost", wsk.Wskprops.APIHost,
+ "--apiversion", wsk.Wskprops.APIVersion)
+ assert.Equal(t, nil, err, "The command property set --apihost --apiversion failed to run.")
+ stdout, err = wsk.RunCommand("property", "get", "-i", "--apibuild")
+ assert.Equal(t, nil, err, "The command property get -i --apibuild failed to run.")
+ assert.NotContains(t, common.RemoveRedundentSpaces(string(stdout)), "whisk API build Unknown",
+ "The output of the command property get --apibuild does not contain \"whisk API build Unknown\".")
+ assert.NotContains(t, common.RemoveRedundentSpaces(string(stdout)), "Unable to obtain API build information",
+ "The output of the command property get --apibuild does not contain \"Unable to obtain API build information\".")
+ assert.Contains(t, common.RemoveRedundentSpaces(string(stdout)), "whisk API build 20",
+ "The output of the command property get --apibuild does not contain \"whisk API build 20\".")
+ common.DeleteFile(tmpProp)
+}
+
+// Test case to fail to show api build when setting apihost to bogus value.
+func TestFailShowAPIBuildVersion(t *testing.T) {
+ common.CreateFile(tmpProp)
+
+ os.Setenv("WSK_CONFIG_FILE", tmpProp)
+ assert.Equal(t, os.Getenv("WSK_CONFIG_FILE"), tmpProp, "The environment variable WSK_CONFIG_FILE has not been set.")
+
+ _, err := wsk.RunCommand("property", "set", "--apihost", "xxxx.yyyy")
+ assert.Equal(t, nil, err, "The command property set --apihost failed to run.")
+ stdout, err := wsk.RunCommand("property", "get", "-i", "--apibuild")
+ assert.NotEqual(t, nil, err, "The command property get -i --apibuild does not raise any error.")
+ assert.Contains(t, common.RemoveRedundentSpaces(string(stdout)), "whisk API build Unknown",
+ "The output of the command property get --apibuild does not contain \"whisk API build Unknown\".")
+ assert.Contains(t, common.RemoveRedundentSpaces(string(stdout)), "Unable to obtain API build information",
+ "The output of the command property get --apibuild does not contain \"Unable to obtain API build information\".")
+}
+
+// Test case to show api build using http apihost.
+func TestShowAPIBuildVersionHTTP(t *testing.T) {
+ common.CreateFile(tmpProp)
+
+ os.Setenv("WSK_CONFIG_FILE", tmpProp)
+ assert.Equal(t, os.Getenv("WSK_CONFIG_FILE"), tmpProp, "The environment variable WSK_CONFIG_FILE has not been set.")
+
+ apihost := "http://" + wsk.Wskprops.ControllerHost + ":" + wsk.Wskprops.ControllerPort
+ stdout, err := wsk.RunCommand("property", "set", "--apihost", apihost)
+ assert.Equal(t, nil, err, "The command property set --apihost failed to run.")
+ stdout, err = wsk.RunCommand("property", "get", "-i", "--apibuild")
+ assert.Equal(t, nil, err, "The command property get -i --apibuild failed to run.")
+ assert.NotContains(t, common.RemoveRedundentSpaces(string(stdout)), "whisk API build Unknown",
+ "The output of the command property get --apibuild does not contain \"whisk API build Unknown\".")
+ assert.NotContains(t, common.RemoveRedundentSpaces(string(stdout)), "Unable to obtain API build information",
+ "The output of the command property get --apibuild does not contain \"Unable to obtain API build information\".")
+ assert.Contains(t, common.RemoveRedundentSpaces(string(stdout)), "whisk API build 20",
+ "The output of the command property get --apibuild does not contain \"whisk API build 20\".")
+ common.DeleteFile(tmpProp)
+}
+
+// Test case to reject bad command.
+func TestRejectAuthCommNoKey(t *testing.T) {
+ common.CreateFile(tmpProp)
+
+ os.Setenv("WSK_CONFIG_FILE", tmpProp)
+ assert.Equal(t, os.Getenv("WSK_CONFIG_FILE"), tmpProp, "The environment variable WSK_CONFIG_FILE has not been set.")
+
+ stdout, err := wsk.RunCommand("list", "--apihost", wsk.Wskprops.APIHost,
+ "--apiversion", wsk.Wskprops.APIVersion)
+ assert.NotEqual(t, nil, err, "The command list should fail to run.")
+ assert.Contains(t, common.RemoveRedundentSpaces(string(stdout)), "usage.",
+ "The output of the command does not contain \"usage.\".")
+ assert.Contains(t, common.RemoveRedundentSpaces(string(stdout)), "--auth is required",
+ "The output of the command does not contain \"--auth is required\".")
+ common.DeleteFile(tmpProp)
+}
+
+// Test case to reject commands that are executed with invalid arguments.
+func TestRejectCommInvalidArgs(t *testing.T) {
+ initInvalidArgs()
+ for _, invalidArg := range invalidArgs {
+ cs := invalidArg.Cmd
+ cs = append(cs, "--apihost", wsk.Wskprops.APIHost)
+ stdout, err := wsk.RunCommand(cs...)
+ outputString := string(stdout)
+ assert.NotEqual(t, nil, err, "The command should fail to run.")
+ assert.Equal(t, "exit status 1", err.Error(), "The error should be exit status 1.")
+ assert.Contains(t, outputString, invalidArg.Err,
+ "The output of the command does not contain " + invalidArg.Err)
+ assert.Contains(t, outputString, "Run 'wsk --help' for usage",
+ "The output of the command does not contain \"Run 'wsk --help' for usage\".")
+ }
+}
+
+// Test case to reject commands that are executed with invalid JSON for annotations and parameters.
+func TestRejectCommInvalidJSON(t *testing.T) {
+ helloFile := common.GetTestActionFilename("hello.js")
+ var invalidJSONInputs = []string{
+ "{\"invalid1\": }",
+ "{\"invalid2\": bogus}",
+ "{\"invalid1\": \"aKey\"",
+ "invalid \"string\"",
+ "{\"invalid1\": [1, 2, \"invalid\"\"arr\"]}",
+ }
+ var invalidJSONFiles = []string{
+ common.GetTestActionFilename("malformed.js"),
+ common.GetTestActionFilename("invalidInput1.json"),
+ common.GetTestActionFilename("invalidInput2.json"),
+ common.GetTestActionFilename("invalidInput3.json"),
+ common.GetTestActionFilename("invalidInput4.json"),
+ }
+ var invalidParamArg = "Invalid parameter argument"
+ var invalidAnnoArg = "Invalid annotation argument"
+ var paramCmds = []common.InvalidArg{
+ common.InvalidArg{
+ Cmd: []string{"action", "create", "actionName", helloFile},
+ Err: invalidParamArg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "update", "actionName", helloFile},
+ Err: invalidParamArg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "invoke", "actionName"},
+ Err: invalidParamArg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "create", "packageName"},
+ Err: invalidParamArg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "update", "packageName"},
+ Err: invalidParamArg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "bind", "packageName", "boundPackageName"},
+ Err: invalidParamArg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "create", "triggerName"},
+ Err: invalidParamArg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "update", "triggerName"},
+ Err: invalidParamArg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "fire", "triggerName"},
+ Err: invalidParamArg,
+ },
+ }
+
+ var annotCmds = []common.InvalidArg{
+ common.InvalidArg{
+ Cmd: []string{"action", "create", "actionName", helloFile},
+ Err: invalidAnnoArg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"action", "update", "actionName", helloFile},
+ Err: invalidAnnoArg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "create", "packageName"},
+ Err: invalidAnnoArg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "update", "packageName"},
+ Err: invalidAnnoArg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"package", "bind", "packageName", "boundPackageName"},
+ Err: invalidAnnoArg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "create", "triggerName"},
+ Err: invalidAnnoArg,
+ },
+ common.InvalidArg{
+ Cmd: []string{"trigger", "update", "triggerName"},
+ Err: invalidAnnoArg,
+ },
+ }
+
+ for _, cmd := range paramCmds {
+ for _, invalid := range invalidJSONInputs {
+ cs := cmd.Cmd
+ cs = append(cs, "-p", "key", invalid, "--apihost", wsk.Wskprops.APIHost)
+ stdout, err := wsk.RunCommand(cs...)
+ outputString := string(stdout)
+ assert.NotEqual(t, nil, err, "The command should fail to run.")
+ assert.Equal(t, "exit status 1", err.Error(), "The error should be exit status 1.")
+ assert.Contains(t, outputString, cmd.Err,
+ "The output of the command does not contain " + cmd.Err + " .")
+ }
+ for _, invalid := range invalidJSONFiles {
+ cs := cmd.Cmd
+ cs = append(cs, "-P", invalid, "--apihost", wsk.Wskprops.APIHost)
+ stdout, err := wsk.RunCommand(cs...)
+ outputString := string(stdout)
+ assert.NotEqual(t, nil, err, "The command should fail to run.")
+ assert.Equal(t, "exit status 1", err.Error(), "The error should be exit status 1.")
+ assert.Contains(t, outputString, cmd.Err,
+ "The output of the command does not contain " + cmd.Err + " .")
+ }
+ }
+
+ for _, cmd := range annotCmds {
+ for _, invalid := range invalidJSONInputs {
+ cs := cmd.Cmd
+ cs = append(cs, "-a", "key", invalid, "--apihost", wsk.Wskprops.APIHost)
+ stdout, err := wsk.RunCommand(cs...)
+ outputString := string(stdout)
+ assert.NotEqual(t, nil, err, "The command should fail to run.")
+ assert.Equal(t, "exit status 1", err.Error(), "The error should be exit status 1.")
+ assert.Contains(t, outputString, cmd.Err,
+ "The output of the command does not contain " + cmd.Err + " .")
+ }
+ for _, invalid := range invalidJSONFiles {
+ cs := cmd.Cmd
+ cs = append(cs, "-A", invalid, "--apihost", wsk.Wskprops.APIHost)
+ stdout, err := wsk.RunCommand(cs...)
+ outputString := string(stdout)
+ assert.NotEqual(t, nil, err, "The command should fail to run.")
+ assert.Equal(t, "exit status 1", err.Error(), "The error should be exit status 1.")
+ assert.Contains(t, outputString, cmd.Err,
+ "The output of the command does not contain " + cmd.Err + " .")
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/travis/build_tag_releases.sh b/tools/travis/build_tag_releases.sh
new file mode 100755
index 0000000..71298f0
--- /dev/null
+++ b/tools/travis/build_tag_releases.sh
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+
+declare -a os_list=()
+declare -a arc_list=("amd64" "386")
+build_file_name=${1:-"wsk"}
+zip_file_name=${2:-"OpenWhisk_CLI"}
+os=$TRAVIS_OS_NAME
+
+if [[ $TRAVIS_OS_NAME == 'linux' ]]; then
+ # Currently we have not set up the CI designated to build windows binaries, so we tentatively
+ # add the windows build into the linux CI environment.
+ os_list=("linux" "windows")
+elif [[ $TRAVIS_OS_NAME == 'osx' ]]; then
+ os_list=("darwin")
+fi
+
+for os in "${os_list[@]}"
+do
+ for arc in "${arc_list[@]}"
+ do
+ wsk=$build_file_name
+ os_name=$os
+ if [ "$os" == "windows" ]; then
+ wsk="$wsk.exe"
+ fi
+ if [ "$os" == "darwin" ]; then
+ os_name="mac"
+ fi
+ cd $TRAVIS_BUILD_DIR
+ GOOS=$os GOARCH=$arc go build -o build/$os/$arc/$wsk
+ cd build/$os/$arc
+ zip -r "$TRAVIS_BUILD_DIR/$zip_file_name-$TRAVIS_TAG-$os_name-$arc.zip" $wsk
+ done
+done
diff --git a/tools/travis/install_openwhisk.sh b/tools/travis/install_openwhisk.sh
new file mode 100755
index 0000000..56d3799
--- /dev/null
+++ b/tools/travis/install_openwhisk.sh
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+
+HOMEDIR="$(dirname "$TRAVIS_BUILD_DIR")"
+cd $HOMEDIR
+
+sudo gpasswd -a travis docker
+sudo -E bash -c 'echo '\''DOCKER_OPTS="-H tcp://0.0.0.0:4243 -H unix:///var/run/docker.sock --api-enable-cors --storage-driver=aufs"'\'' > /etc/default/docker'
+
+# Docker
+sudo apt-get -y update -qq
+sudo apt-get -o Dpkg::Options::="--force-confold" --force-yes -y install docker-engine=1.12.0-0~trusty
+sudo service docker restart
+echo "Docker Version:"
+docker version
+echo "Docker Info:"
+docker info
+
+# Ansible
+pip install --user ansible==2.1.2.0
+
+# Clone the OpenWhisk code
+git clone --depth 3 https://github.com/openwhisk/openwhisk.git
+
+# Build script for Travis-CI.
+WHISKDIR="$HOMEDIR/openwhisk"
+
+ANSIBLE_CMD="ansible-playbook -i environments/local"
+
+cd $WHISKDIR/ansible
+$ANSIBLE_CMD setup.yml
+$ANSIBLE_CMD prereq.yml
+$ANSIBLE_CMD couchdb.yml
+$ANSIBLE_CMD initdb.yml
+
+cd $WHISKDIR
+./gradlew distDocker
+
+cd $WHISKDIR/ansible
+$ANSIBLE_CMD wipe.yml
+$ANSIBLE_CMD openwhisk.yml
diff --git a/wski18n/i18n_resources.go b/wski18n/i18n_resources.go
new file mode 100644
index 0000000..723b874
--- /dev/null
+++ b/wski18n/i18n_resources.go
@@ -0,0 +1,469 @@
+// Code generated by go-bindata.
+// sources:
+// wski18n/resources/.DS_Store
+// wski18n/resources/de_DE.all.json
+// wski18n/resources/en_US.all.json
+// wski18n/resources/es_ES.all.json
+// wski18n/resources/fr_FR.all.json
+// wski18n/resources/it_IT.all.json
+// wski18n/resources/ja_JA.all.json
+// wski18n/resources/ko_KR.all.json
+// wski18n/resources/pt_BR.all.json
+// wski18n/resources/zh_Hans.all.json
+// wski18n/resources/zh_Hant.all.json
+// DO NOT EDIT!
+
+package wski18n
+
+import (
+ "bytes"
+ "compress/gzip"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "time"
+)
+
+func bindataRead(data []byte, name string) ([]byte, error) {
+ gz, err := gzip.NewReader(bytes.NewBuffer(data))
+ if err != nil {
+ return nil, fmt.Errorf("Read %q: %v", name, err)
+ }
+
+ var buf bytes.Buffer
+ _, err = io.Copy(&buf, gz)
+ clErr := gz.Close()
+
+ if err != nil {
+ return nil, fmt.Errorf("Read %q: %v", name, err)
+ }
+ if clErr != nil {
+ return nil, err
+ }
+
+ return buf.Bytes(), nil
+}
+
+type asset struct {
+ bytes []byte
+ info os.FileInfo
+}
+
+type bindataFileInfo struct {
+ name string
+ size int64
+ mode os.FileMode
+ modTime time.Time
+}
+
+func (fi bindataFileInfo) Name() string {
+ return fi.name
+}
+func (fi bindataFileInfo) Size() int64 {
+ return fi.size
+}
+func (fi bindataFileInfo) Mode() os.FileMode {
+ return fi.mode
+}
+func (fi bindataFileInfo) ModTime() time.Time {
+ return fi.modTime
+}
+func (fi bindataFileInfo) IsDir() bool {
+ return false
+}
+func (fi bindataFileInfo) Sys() interface{} {
+ return nil
+}
+
+var _wski18nResourcesDs_store = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xec\x97\x41\x4e\xea\x40\x18\x80\xbf\x29\x5d\x34\xef\x25\xa4\xcb\xb7\xec\xf2\xad\x48\xbc\x41\x25\x40\x44\x77\xa0\x5b\x47\x10\x8c\x62\x43\x8d\xa0\x0b\x57\x1c\xc3\x43\x79\x00\x0f\xe0\x01\xbc\x81\xa6\xed\x6f\x40\x0b\x0e\x1b\x63\x35\xff\x97\x90\x6f\xc1\x7c\x65\x42\xda\xe9\x0c\x60\x9a\x37\xa3\x1d\x08\x81\x80\xc2\xfe\x1f\xd6\x12\xc8\xa7\x84\x27\xce\x3a\x93\x5f\xa3\x4d\x83\x01\x09\x09\x8d\xf5\xd7\x52\x14\xa5\x42\x64\xcf\x6e\x9d\x11\x63\x2c\xad\x77\xcf\xef\x84\x19\x29\xd3\x6e\x92\x9e\x0e\x93\x74\x28\x8b\x44\x07\xf8\xff\x92\x93\xf7\x75\xc6\x4c\xb1\x1c\xd1\x77\xb6\x0f\x6b\xda\x19\x96\xb6\xbb\x35\xbd\x52\x7b\xc6\x35\x96\x0e\x3d\x67\xfb\x58\x6a\x2f\x98\x63\xe9\x72\xe8\x6a\xbd\xe3\x52\x3b\x61\x80\x65\x9f\x5d\x67\xfb\x54\x6a\x2f\x49\xb1\x1c\xb8\xe7\x5c\x9b\x94\xda\xab\x7c\xce\xcd\x2d\xda\xe7\x0f\x6d\xc8\x1d\xe7\x58\xf6\x18\x30\x65\xe6\xea\xfd\xdb\x4f\xfb\xf9\x56\xf7\xc8\xfd\x4a\xaf\x54\x13\x53\x28\xf8\xfb\xdd\x13\x51\x14\xa5\x72\x64\xeb\x43\x24\x8e\xc5\x8b\xc2\x46\xbe\xf7\xc4\xfe\x4a\x13\x8a\x23\x71\x2c\x5e\x14\x36\x32\xce\x13\xfb\xe2\x40\x1c\x8a\x23\x71\x2c\x5e\x14\x96\x45\xcb\xc8\xe1\xc3\xc8\x2f\x1b\x39\xa1\x98\x50\x1c\x89\xe3\xaf\xf9\x6f\x14\xe5\xa7\x53\x2b\x14\x66\xef\xff\xf6\xe6\xf3\xbf\xa2\x28\xbf\x18\xe3\xb7\xfa\xad\xe6\xf2\x40\x50\xc2\x93\x8d\xc0\xc9\x5b\xb0\x61\x23\x20\x63\xb3\x57\xf1\x3f\x96\x63\x75\x23\xa0\x28\x15\xe3\x35\x00\x00\xff\xff\x7e\xe4\x42\x91\x04\x18\x00\x00")
+
+func wski18nResourcesDs_storeBytes() ([]byte, error) {
+ return bindataRead(
+ _wski18nResourcesDs_store,
+ "wski18n/resources/.DS_Store",
+ )
+}
+
+func wski18nResourcesDs_store() (*asset, error) {
+ bytes, err := wski18nResourcesDs_storeBytes()
+ if err != nil {
+ return nil, err
+ }
+
+ info := bindataFileInfo{name: "wski18n/resources/.DS_Store", size: 6148, mode: os.FileMode(420), modTime: time.Unix(1491519881, 0)}
+ a := &asset{bytes: bytes, info: info}
+ return a, nil
+}
+
+var _wski18nResourcesDe_deAllJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00")
+
+func wski18nResourcesDe_deAllJsonBytes() ([]byte, error) {
+ return bindataRead(
+ _wski18nResourcesDe_deAllJson,
+ "wski18n/resources/de_DE.all.json",
+ )
+}
+
+func wski18nResourcesDe_deAllJson() (*asset, error) {
+ bytes, err := wski18nResourcesDe_deAllJsonBytes()
+ if err != nil {
+ return nil, err
+ }
+
+ info := bindataFileInfo{name: "wski18n/resources/de_DE.all.json", size: 0, mode: os.FileMode(420), modTime: time.Unix(1491513831, 0)}
+ a := &asset{bytes: bytes, info: info}
+ return a, nil
+}
+
+var _wski18nResourcesEn_usAllJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd4\x5d\x5f\x73\xdb\x38\x92\x7f\x9f\x4f\xd1\x95\x17\x3b\x55\xb6\xb3\xfb\x74\x75\x99\x9a\x07\x4d\xec\xd9\x78\x93\xd8\x2e\xcb\x99\xdd\xa9\x9b\xab\x11\x44\x42\x12\xd6\x14\xc0\x01\x40\x3b\x8a\xd7\xdf\xfd\x0a\x00\x49\x91\x12\xfe\x92\x72\xb2\xf7\x14\x47\xec\xfe\x75\xb3\xf1\xaf\xd1\xe8\x06\xff\xe7\x07\x80\xa7\x1f\x00\x00\x5e\x91\xfc\xd5\x5b\x78\x35\x29\xcb\x82\x64\x48\x12\x46\x01\x7f\x21\x12\xe7\x50\x51\xfc\xa5\xc4\x99\xc4\x79\xb1\x79\x75\x62\x88\x25\x47\x54\x14\x9a\x2c\x86\xeb\x07\x80\xe7\x93\x5d\x51\xb7\x15\x85\xa3\xa7\xa7\xb3\x2b\xb4\xc6\xcf\xcf\x70\x7a\xba\xc2\x45\x79\x04\x0b\xc6\xa1\x12\x68\x89\xcf\x7e\xa7\x0e\x71\x31\x9c\x56\x91\x98\x73\xc6\xdf\x82\x03\xb6\x79\x6a\x65\xa5\x4c\x82\xc0\xd2\xc1\xda\x3c\xb5\xb2\x5e\x97\x98\xfe\x63\x45\xc4\x3d\x64\x05\xab\x72\xc8\xd8\xba\xac\x24\xa1\x4b\xf5\xd7\x1a\xd1\x1c\x0a\x42\x31\x10\x2a\x31\x5f\xa0\x0c\x9f\x39\x84\xa4\xe3\x58\xd5\x79\xc0\x7c\xce\x04\x06\x56\xc9\xb2\x72\xbd\xd0\x0e\x91\x15\x28\xc7\xf3\x6a\x09\x05\x7e\xc0\x85\x1f\xcc\x42\x68\x05\x44\x95\x5c\x31\x4e\xbe\x9a\x8e\x34\xfb\x70\xf1\xdb\xcc\x81\x68\xa3\xb4\x42\x3e\x6a\x7b\x4d\x6e\x2e\x61\xf6\xfe\x7a\x7a\xe7\xc2\xdb\x23\x0b\x81\xfd\x7a\x71\x3b\xbd\xbc\xbe\x8a\xc0\x6b\x29\xad\x90\xf3\x4d\x89\x84\x80\x0c\x73\x49\x16\x6a\x08\x61\xc8\x56\x38\xbb\x27\x74\xe9\x80\xf6\x71\x58\x45\x7c\xa6\x68\x5e\x60\x90\x0c\x08\x25\x92\xa0\x82\x7c\xc5\x20\x30\x7f\xc0\x1c\x32\x46\x29\xce\x14\xf4\x5b\x78\x7a\x3a\xc3\x9c\x3f\x3f\x3b\xe4\x26\xc3\x58\x95\xb9\x41\x1c\xad\xb1\xc4\x1c\x10\x5f\x56\x6b\x4c\xa5\x80\x75\x25\x24\xcc\x31\x20\xb8\xc7\x1b\x78\x40\x45\x85\xa1\x44\x84\x6b\x2c\xc4\x97\xc2\xa9\xd3\x50\x34\xab\x6a\x13\x4a\x99\x34\x1d\xea\x10\xba\x0d\x86\xb3\x2a\xf7\x0b\x22\x05\xce\x95\xf5\x4b\xc4\x05\xde\x42\x06\xdb\x2d\x86\xd3\xde\xdb\x19\xbf\x87\x47\x22\x57\x40\xd1\x1a\x8b\x12\x65\x58\xb8\xba\xbb\x8d\xd4\x0a\x5a\x10\x21\x01\x53\x49\x24\xc1\x02\x08\x05\xb9\xc2\x90\x55\x9c\x63\x2a\xb7\xcc\x0e\x31\x91\xcc\x81\x51\xc0\xe6\x12\xd5\xbc\x1a\x90\x2d\x00\x3d\x20\x52\xe8\xe7\x5b\xfd\x13\x06\x44\x3a\xa2\x55\xc5\x25\x96\x20\x39\x59\x2e\x31\x17\x27\x80\xf4\x78\x52\x7f\xd0\x1c\x78\x55\x6c\xdf\x98\xe3\x25\x11\x92\x6f\xf4\x82\x87\x82\x56\x1b\x0d\x6b\x55\x56\xad\xbf\x54\xaf\xbf\x47\x40\x04\xa8\x05\x10\xa9\x0e\x4d\x72\xf8\xb3\x42\x05\x59\x10\x9c\x6b\x8c\xa0\x1d\x87\x20\xa5\x37\x71\xdb\x6d\xd4\xdb\xb5\xef\x06\x8d\x70\xfd\xbf\xe7\xe7\xa3\x71\xad\x9e\x2e\xc4\xfa\x22\x17\x9d\x2e\xde\xf2\x69\xa6\x0e\x8a\xd3\x3b\x8a\xe5\x76\x8f\x4f\x5b\xe7\xf5\x0d\x49\x2b\x7d\x60\x4e\x29\x51\x76\x8f\x96\x11\x33\x4a\x4b\x68\x5f\x3f\x09\xcd\xd5\xc4\x66\x96\x01\xa1\xda\x05\x35\x2c\xae\x05\xd4\xc7\x62\x15\x72\x49\x4d\x77\x2c\xf7\x96\x1b\xdd\xb4\xfa\xe7\x98\xbe\x93\x8e\xe3\x55\x07\xed\xaf\x30\x1a\x67\xfb\x7b\x8a\x52\xa9\x68\x56\xd5\x7e\x26\x34\xd7\x0e\x29\xc7\x06\x6a\xa1\x57\x9f\xa0\x12\x61\x3e\xab\xb8\xa7\xa7\x33\x76\xff\xfc\x6c\xd8\x70\x0e\xf3\x1a\xa6\x9d\x51\x9c\x83\x24\x86\xd3\x2a\xd2\x30\xa8\xf9\x11\x3f\x06\x3a\x9a\x95\xd4\xe1\x15\xe9\x87\xc9\x66\x0b\xf3\x45\x99\xad\xd6\x6d\x80\xd9\x6c\x9c\x56\x91\x55\x99\x6b\x5b\xe8\xfd\xa1\xd0\x9b\x96\x9a\xf7\x04\x18\x87\xd6\x54\x0d\x20\x59\x00\x91\x90\x33\x6c\x16\x04\xcd\xe4\xd0\xe9\x20\xd0\xde\x66\xa9\x25\x24\x36\x8a\x8b\xcb\xdb\x24\x86\x69\x48\x93\xf8\x38\x9d\xee\x86\xbf\x03\x77\x29\xbc\x06\x52\x84\x89\xd6\xb1\xb2\x78\x4d\xb3\x64\x72\x80\x59\x5c\x5c\x8e\x1d\x6d\x81\x25\x0e\x58\x65\x87\xc8\x6b\x98\x9a\x36\xd1\x36\x2e\x2e\xaf\x79\x0c\xd3\x90\x9e\xe3\xe3\xf4\x38\x09\x45\x11\x5a\xc4\xf7\xe9\xac\x70\x57\xac\x76\xf7\xb6\x1e\x53\x8e\xa5\x8e\x1d\x9d\x81\x8e\xf5\x3c\x8a\x7b\x28\x39\x2b\x31\x97\x1b\x10\x58\xc2\xe9\x69\x4b\x7b\xa4\x06\x39\xa6\xa2\xe2\x58\xfb\x62\xea\xc1\x76\x05\x23\x02\x4a\x8e\x33\x9c\xab\x39\x7e\x03\x08\x7e\x7f\xf5\xe6\xf7\x57\x0e\x7d\xbf\x83\x22\xe9\x8e\x6c\x63\x4b\x87\x8f\x39\xda\x87\x4d\xc2\xb7\xaa\xcf\xf1\x82\x63\xd1\x7a\x6e\xcd\xda\xea\xea\x25\x4e\x72\xef\xc8\x6a\xb8\x9c\x5a\xa6\x0e\xba\x01\x80\xae\xf1\x68\x18\x1a\x44\x9c\x83\xa8\xb2\x0c\x0b\xb1\xa8\x8a\x62\xe3\x1b\x8e\x21\x46\x8f\x47\xd2\xba\x30\xe2\xad\xd7\x1d\xe9\xd2\x79\x56\xea\x30\xdc\x3e\x9d\x67\x4a\x0d\xc3\xed\xd3\x59\xe1\xee\x56\xed\xcc\xbb\x6d\x31\x8c\xa4\x1a\x74\xf5\xe6\x91\xac\xcb\x02\xab\x31\x87\xf3\x66\x6b\x2b\x11\x57\xeb\x4d\x8e\xcb\x82\x6d\xd4\x23\x87\x12\x87\x42\x3f\x48\xcf\x85\xbc\xd2\x63\x74\x1b\x49\x87\xf7\x77\x77\x37\x20\x24\x92\x95\x80\x8c\xe5\x66\x57\xa7\xfe\x38\x58\xef\x4e\x14\x6a\x0f\xe3\x6e\x77\x12\x3a\xc2\xa5\x77\xa2\xb3\x0f\x17\xbf\xc1\xaf\x93\x8f\x9f\x2f\x66\x4a\x89\x35\x72\xb5\x41\x2c\xb7\x55\xf4\xec\x97\xcb\x8f\x17\x33\xc8\x18\x55\xf3\x9a\x72\x05\xad\x70\x7f\x9f\x5e\x5f\xf9\xb5\x18\x00\xb4\xa3\x10\x65\x12\x9f\x4a\x76\xda\x00\x33\x2e\x14\xf0\xf9\x35\x5c\x5d\xdf\xc1\xdd\xed\xe4\x6a\xfa\x71\x72\x77\x01\x77\xef\x2f\xe0\x68\x83\xc5\x11\x4c\xae\xce\xe1\x88\xb2\xa3\x33\x80\xbb\xf7\xd7\xd3\x0b\x98\xdc\x5e\xc0\x2f\x97\xff\xbc\x38\x87\x77\x1f\x2f\x61\x72\xfb\xb7\xcf\x9f\x2e\xae\xee\x8c\x1d\xa6\x8d\xe2\xe6\xc5\x9b\x5e\xfb\x40\x04\x99\x93\x82\xc8\x0d\xcc\xa6\xef\xae\x6f\x2e\x66\x3f\xc2\x06\x0b\xf8\x09\xc4\x0a\x71\x9c\x9f\x00\x65\xf0\x13\x94\x9c\x3c\x20\xe9\xf2\x70\x06\x82\x59\x5b\x44\x54\xeb\x35\xe2\xe4\xeb\x76\x60\xe5\x58\x22\x52\xb8\x56\x03\x37\xbd\x15\x9e\xd0\xac\xa8\x72\x0c\x65\x35\x2f\x48\x56\x6c\x6a\xcd\xf6\xa2\x84\x1c\x8b\xaa\x70\x35\x76\x22\x88\xfd\x68\xe9\x8b\xc1\x50\x74\x0b\xc2\x85\x84\xd9\xf4\xc3\xe5\xcd\x0c\x68\xb5\x9e\x63\xde\x5f\x59\x39\x5b\x87\xb5\x1a\x83\x68\x55\x91\xd1\x62\x03\x1c\xcb\x8a\x53\x98\x7d\xbc\xfc\x74\x79\xe7\xc7\xca\x58\x51\x98\xa8\xbe\x43\xc3\x11\x80\x56\x05\x1b\xcf\xca\xd5\x2d\x9b\xc7\x81\xd0\x92\x39\x7c\xa9\xa9\x49\x44\x88\x69\x8f\xc1\xde\x93\xd5\x6e\xc8\xaf\x61\x8f\x24\xe0\xda\x29\x5a\x65\x94\xd6\x9d\xd4\xb3\x4a\x82\xef\x16\x04\xf0\x6e\x17\xcc\x3b\xa3\x4a\xae\x0c\x10\xd3\x27\x10\x95\x5c\x45\x6c\x18\xfc\xbc\x11\xef\x3d\xb9\xb9\x84\x15\x13\xd2\xa8\xfc\xa3\x7e\x8d\xfe\x6f\x26\xf4\x54\x12\xf5\x4b\x1d\x18\x26\x26\x50\x95\x68\xa1\x03\x89\x8a\xb0\x65\x8b\xba\xb5\x89\x81\x8c\xb4\xa7\x8f\x3f\x52\xfc\x03\xe6\x42\x2d\x50\x5b\x84\xfa\x97\x24\x25\xfc\x28\xf6\xb3\xb3\x4a\xae\xd4\x6c\x99\x69\x97\xb0\x12\x98\x6f\x03\x2c\x2b\xf4\x80\xed\x3e\xc7\x8f\x5a\x44\x73\x78\x1d\xe9\xaf\xbf\x88\x28\xfb\xbe\xd4\xea\x27\x35\x7e\xa0\x25\xf2\x5f\xe0\x3c\x1c\x31\x1f\x8b\x1a\xd1\x15\xb6\x16\xd8\x36\x61\x64\x2c\x20\x02\x60\xd0\xbc\x76\x2c\x5e\x8f\x9e\xda\xfa\x18\xf6\xcd\x0b\x8d\x98\xa5\x77\x88\x02\xef\x63\xa8\x47\xcd\xd4\x11\x10\xb1\x73\xb5\x86\x8a\x9f\x9e\x0d\x79\x52\x97\x89\x97\xb0\xcb\x93\x32\x47\xc6\x4b\xd9\x61\x49\x9c\x09\xd3\xe4\xf4\xb9\xac\xa2\xcc\x0a\x92\xe3\x05\xaa\x8a\x66\x01\x61\x0b\xd5\x90\xf5\x6f\x0a\x90\x14\x05\xcc\xb1\x9a\x9c\x72\x77\x16\xd3\x10\x24\xb7\x4a\xcd\x0e\x75\x07\x50\xae\x90\x84\x0c\xd1\x48\x75\x12\x50\xdc\x31\x65\xff\xe8\x5b\x06\xc7\xde\xb6\xff\x7a\xb3\x6b\x34\x41\x20\x4f\x47\x75\x9c\x60\x8a\x8e\x26\x0a\x00\xd5\x3d\x23\x88\xd5\xd0\x79\xe0\x42\xe7\xf5\xbb\x54\x1e\x28\xb5\x2d\x8c\xd1\xac\x4b\xe7\x98\xee\xee\x29\x7b\x74\x81\x34\x4f\x03\x36\x9a\x57\xa4\xc8\x83\x16\x32\x54\x31\x50\xf5\x5e\x22\x0e\xb1\x21\x8e\x0b\xa8\x6e\xd9\x08\x35\xdb\xf7\xb4\x14\xa8\x48\x98\x88\x2c\xb7\x7b\xec\x1a\x28\xfb\x74\x51\x46\x8b\xed\xa9\x7d\x6a\xbb\xa6\x45\x11\xde\x46\xed\x10\x79\x74\x9c\x5d\x4d\x3e\x5d\x4c\x6f\x26\xef\x2e\xfc\x69\x73\x5d\xba\x40\x73\x16\x4c\x27\xc0\x6d\xe5\xc3\x82\x14\xc6\xb3\x52\x7f\xa4\x07\xc4\x93\x01\x03\x0a\x72\x8c\xf2\xee\xca\x7f\x00\x15\x07\x40\x7a\xd3\x08\xf4\xe2\x8a\xf2\x9c\x63\x21\x34\x46\xbd\x11\x8a\xce\x1c\x88\x00\xb0\x2a\xf0\x8f\x9d\x6d\xb7\x79\x8b\x47\x4e\xea\xb3\xaf\x8a\x87\x9d\xac\x34\x8c\x40\xe0\x40\xa7\x43\x05\xa3\x05\x86\xca\x1e\x04\x32\x8d\xa4\x28\x5c\x41\x9d\x0e\x45\xa0\xeb\x74\x48\x07\x9e\xee\x84\x11\xbc\xde\x94\x61\x37\x59\x62\x09\x5b\x08\x27\x9b\xfd\x7c\x80\x88\x90\xcd\x7a\x24\x01\xa3\x75\x69\x07\x5a\x2d\x02\xc2\x7f\x10\x6b\xf8\x93\xed\xe6\xe6\x73\xba\x5a\x9a\xd2\x84\xe6\x3d\xde\x56\x97\x2a\x38\x5d\x49\x4e\xf0\xc3\x28\xfb\xc5\x60\x78\x0d\xd8\x7f\x7f\xe5\x90\x3e\x3d\x9d\x19\xfd\x23\xcc\x18\xe2\xf6\x65\xf5\x50\xfc\xe8\xeb\x88\xbb\x54\x01\x63\xd6\xe4\x23\x4c\x19\x46\x88\xca\xef\x49\xec\x88\x4e\xb6\xd8\xcc\x1e\xc5\xd8\xcf\xbd\xd1\x50\xe3\x72\x7a\x52\x40\x43\x5b\x7a\x83\x3e\xa2\x61\xc2\x08\x51\x59\x3e\x89\x0d\xe3\x64\xf3\x4e\x10\x81\x99\xe1\xff\xc5\x94\xb0\x64\x32\xd5\x58\x56\x16\x5f\xd6\x8f\x6f\x05\xea\x50\x84\x16\xa0\x2d\xe9\xd0\xf5\x27\x88\x10\x95\x07\x94\xba\xfa\xb8\xd8\xfc\x19\x40\x3e\x97\x69\x87\x28\x2d\xc6\xba\x17\x82\x77\xbe\x41\x12\x44\x7a\xbe\x8d\xc9\x91\x7f\xa1\x64\x9b\x78\xf0\xef\x7a\xba\xac\x3b\xc5\x41\x8e\x96\x87\x20\xb9\x76\xd1\x4c\xed\xb7\x33\x54\x14\x9b\x9e\xa7\x8b\x16\x12\xd7\xab\x84\x5a\x37\x88\x33\xd1\x20\x01\x21\x42\x85\x9e\xdb\x38\xc7\x0b\xc6\xeb\x44\xbe\x04\x25\x42\x18\x81\x13\x76\xcd\x16\x7b\xbc\xde\x23\x0e\x6c\x8c\x54\x97\x15\xf9\x7d\x70\x6b\xd4\xd0\x39\x8e\xea\x85\x54\xb3\xc1\xf4\xfc\x03\x20\x2e\xc9\x02\x65\xd2\xa5\xa6\x9d\x36\x1e\xf6\x04\x1e\x75\x30\xd3\x6c\x50\xdf\x5d\x7f\xba\xb9\xbe\x52\x9d\xbb\xce\xdc\x40\xca\xae\x2c\xbb\xc7\xfc\x04\x08\xab\x8b\x61\xe6\x48\xac\x54\x73\xa4\xa8\x94\x22\xe7\x7a\xba\x23\xc7\x99\xe0\xa4\x44\x64\x6c\x5d\x32\x8a\xa9\xec\xa5\x10\xae\x89\x10\x84\x2e\xcf\xe0\x9a\xe2\x0e\xc9\x71\xef\x65\x18\x6f\x65\xbc\x6e\x2b\xce\x44\x89\x33\x5d\x4a\xe3\x49\x7d\x7a\x59\xb9\x81\xa9\x77\x89\x29\xe6\xca\xa9\x1a\x36\xb9\x06\xd8\xed\xc5\x23\x48\xac\xfe\x50\x6f\xa3\x46\x18\xa3\x7f\xac\x85\xab\xe8\x52\x59\x47\x51\x83\x7a\xb9\xd3\x2d\x0b\x88\x8c\x93\x52\xc2\x71\x2b\xf4\xb5\x59\x79\x74\x5f\xd9\xa6\x88\x35\x45\x6a\x39\xe1\x38\x93\x8c\x6f\xce\x7e\xa7\x77\xed\x06\xbd\x57\xbe\xdb\x01\x67\x0b\x78\x14\xf7\xcd\x63\x71\x02\x82\x55\x3c\x33\x19\x21\x4a\x11\xd8\x57\x84\x50\xc9\x60\xc3\x2a\xd3\x14\x80\xe9\x03\xe1\x8c\xaa\x66\x74\x2d\x7e\x9e\x86\x3f\xd2\x89\x5e\xf5\xcf\xfd\x45\xf5\x0c\x7e\xd5\x5d\xbe\x7d\xbc\x37\xa8\x62\xc6\xd4\xb7\x91\xed\x7c\x6d\x1d\x2b\xda\x6e\x15\x51\xc1\x31\xca\x37\x66\x0f\x21\xce\x00\xce\x8d\x27\x46\xa4\x29\x96\xc3\x92\x6f\x5c\xb5\xd9\x83\xe1\x9c\xca\xf5\xdf\x5f\x9b\xa9\xee\x56\x49\x25\x22\x83\xa0\x9c\x4a\x19\x1b\x83\xb8\x57\xaf\xc2\xa8\x39\x2c\x7a\xec\xf4\x77\x24\x1d\xfd\xdd\xa3\xde\x08\x50\xab\xa2\xe7\xec\x91\x16\x0c\xe5\x38\x87\x6d\xc9\x3c\xb9\x9e\x82\x90\x88\xeb\xaa\xab\xb2\x3c\x83\xcf\xf4\x2b\x29\xbb\xcd\x45\x73\x60\x25\xa6\x4d\x68\xf5\x5f\x38\xd3\xe7\xf1\xff\xcc\x58\xee\xb9\x8b\xe0\x85\x84\xc5\x6e\xca\xd4\x30\xa9\x78\x51\x22\xb9\x52\x83\x64\x7a\xfe\x61\xc8\xb6\xcc\x8b\x62\x55\x65\x6a\x0a\xbf\x17\x6d\x71\xb1\xc0\xd4\x44\xa5\xf7\x06\x6e\x8c\x4e\x83\xe1\xec\xf5\x93\x9c\xb3\x8e\xff\xa6\xfa\x7b\x7f\x6c\x06\xf5\x49\x41\xf0\xa9\xc0\xca\x8d\xe2\xaf\xcb\xe4\x39\x16\x25\xa3\x02\x9b\x59\x5a\x01\xc6\x2a\x92\x80\xe3\x1e\xbb\xcd\xb0\x39\xe0\x94\x37\x1c\xd3\x63\xb5\x8a\xfe\xed\x2b\x29\x4b\xf5\xc2\x83\x9a\x2d\x86\xdf\x2b\x5e\x22\xce\x47\x48\x0f\xb2\x87\xbc\xed\xba\x58\x3b\xec\x6e\x37\x84\x56\xc0\x05\xe1\xb8\x21\x01\xfc\xe0\xce\x7a\xb7\x10\x06\xa6\x9f\x1e\xc7\x30\x7f\x2d\x02\xc2\x1b\xe6\xa8\x59\x71\x0e\x6f\xfa\x95\xcd\x6f\xb6\x3d\x51\x1b\x89\xe4\x0a\x91\xe4\x11\x31\x90\x61\x98\xa1\x80\x72\x8d\x1a\x8e\x29\x37\x84\xdf\x75\xab\xdf\x1c\xea\x9d\x9e\xd6\x49\xd0\xad\x47\xd6\x49\x96\xe4\xcb\x07\x54\xe8\xac\x3a\x43\xdc\xd9\xee\x18\x0d\x18\xd7\x0a\x04\x0e\x0e\x0f\x23\x23\x2e\x0a\x3f\xae\xb7\x46\x81\x44\xc5\xe2\x1b\x88\xf4\x70\xbc\x8d\x33\x36\x22\x5f\xf3\xf6\xe3\xe7\x0d\xe0\xb8\xb8\x3c\xa2\x03\xd0\xe3\x02\xf4\xe3\xda\x2c\x0a\x24\x2a\x4c\x9f\xde\x66\x3e\x4e\x67\xb0\xde\x3f\x51\x74\x29\x82\x5b\x69\x39\xd2\x76\x61\x84\x60\xb8\x3e\xdd\x68\x2e\x2e\x5f\xd0\xde\x6f\xb3\x1d\xa2\xb8\xd0\xfd\x38\xcb\x45\x81\x44\x05\xf0\xd3\x0d\xe8\xe3\xf4\x87\xf1\x03\x2e\xc7\x3e\x5d\x7a\x1c\xbd\x61\x7d\xa9\x50\x7a\x12\x7e\x40\x7d\x42\x1f\xd8\x7d\xbf\x11\xd5\xdf\x6d\xa9\x1c\x56\x6b\x96\x2e\x64\x31\x49\x37\x18\xe7\x6d\x9e\xb9\x7e\x58\xa7\x73\x67\x8c\x2e\xc8\xb2\xe2\x11\xfb\xf4\x6f\x24\xfc\xbb\xfa\x16\xcd\x2b\x1d\xe4\x24\x61\x20\x98\xb5\xe5\x1b\x2c\x6d\xbe\xd9\xe4\xdd\xdd\xe5\xf5\xd5\x1f\x57\x93\x4f\xce\x44\x35\x0f\x43\x20\x46\xdf\x70\xc6\x86\xe9\x77\xe9\xed\x85\x53\xed\x85\x32\x03\x0a\x2e\x23\x99\x23\xeb\x2d\x6d\x68\x43\xca\x2d\x43\x38\xf6\x32\x94\xbd\x8b\xd6\x74\xec\x14\x04\x56\x70\x52\x75\x06\xb5\x05\x6d\x1e\xfe\x59\x31\x5d\xc6\xbb\x50\x13\xc9\xa6\x91\x0e\xa6\xd0\xc2\xb5\xed\x3d\xac\x0c\x6f\x66\x5e\xfc\x25\x6f\x3e\x0e\x67\x40\xa0\xf5\xba\x67\xc6\xdf\x7e\x7e\x9e\xa5\xd4\x59\x25\x41\x0c\x54\x42\x37\xf9\x01\x34\xd9\xc5\x71\x94\x1f\x7a\x8b\x62\xbd\x55\xae\x7a\xaa\x71\x0d\x66\xf3\xd0\x1f\x86\xee\x9e\x5d\xf7\x9d\xe5\x60\xc8\xd9\xc7\x1a\x13\x1f\x33\xf1\x92\x3d\xac\xd4\x20\x59\x18\xc6\xa3\x0c\x2b\xb1\x1e\xf6\x63\x34\x89\xc5\x08\x16\xfc\x0c\xd2\x21\x01\x20\xe4\x3c\x95\x98\x8a\x7e\x6d\xbe\x8e\x2b\xd5\x41\xad\x14\x37\x29\x1a\x29\x6e\x13\xbd\x0d\xf7\x29\xcc\x9c\x70\x05\xf9\xb8\x52\x6f\xda\x82\x8e\xdb\x61\x8f\x90\x10\x61\x55\x50\x08\x38\xef\xa7\x4b\x1f\xee\x1d\x0e\x20\x22\xae\x1d\x5e\x48\xff\x71\xe8\x11\xa3\x5b\x22\x3e\x76\x70\x07\x20\x3c\x4a\x70\x8c\xf2\x91\x4a\x44\x42\x1c\x60\x30\x35\x31\xe4\x97\x1b\x4c\x7e\x09\x23\xfb\xe1\x41\xd4\x1f\x88\x1e\x9c\xde\x07\x75\x80\x04\x80\x28\x05\x7a\x8b\xf6\xde\x9d\x2b\xad\x04\xb9\x29\xb1\x73\xdf\x3f\x0e\x33\x70\x0a\x51\x5f\x14\x1b\x3c\x84\x68\xe8\x7c\x61\x70\x73\x05\x22\xf2\xdd\x25\x61\xa3\x0c\x18\xb2\xbe\xcf\xd8\x6c\x85\x9b\x1c\x8a\x78\x9f\x79\x00\x50\xdc\xa8\x40\xa9\x17\x6a\xbb\xf8\xa2\x02\xca\xb5\xda\xe9\xf1\x64\x0b\x63\x6c\x38\xd9\xb0\xf6\xe2\xbd\xb4\xc1\x1b\x19\x4d\x4e\x45\x8e\x8b\x24\x27\x37\x89\x8b\x2f\x2a\x5e\x9c\xdc\x24\x1e\x46\x47\xce\x9b\x0e\x11\x79\x07\x54\x9f\x26\x2e\xe0\xd5\x09\x2b\xa5\xcf\x8b\x31\x18\x5e\xf3\x19\x80\x43\x1e\xe8\x0d\x41\x74\x86\xe7\xbd\xd6\xee\x10\x44\x04\xe7\x93\xbb\xa3\x95\xc9\x9e\x94\xea\xd3\xd2\xa7\x61\x37\x0a\x9f\xdc\x81\x1d\x4c\xbe\xc8\xbd\x57\xd1\x3e\x4d\x5c\xdc\x3e\xd9\xa8\x2e\xbe\xa8\xe8\x7c\xb2\x89\x3c\x8c\xfe\xd8\xbc\x7f\x21\xde\x23\x4b\x8f\xcc\xd7\x9c\x2f\x15\x98\x4f\x81\xb7\x2a\xff\x8e\x55\x45\xae\xa7\xfc\x05\xa1\x39\x1c\xad\x11\xa1\x47\xb0\xc6\x72\xc5\x74\x82\x65\x07\xca\xa1\x5f\x0a\x42\x9c\xfd\xf6\x67\x39\xbd\x90\xb3\x72\x93\x6e\xac\x28\x2c\xab\x5a\xbf\xec\x86\x7f\x76\xee\xcf\xd7\xee\x1e\xe3\xfb\xcb\x66\x50\xc9\x43\x20\xc7\x94\x46\x0f\xad\x27\x72\xb2\x5a\x85\x5a\x5f\x44\x54\x65\xc9\x78\x67\x48\xf2\x8a\x4a\xb2\x76\xc5\xff\xd2\x30\xdc\xde\x70\x7d\x66\x5e\xd3\xeb\xbb\xd1\x10\x9c\x7d\x25\x65\x9b\x55\x0e\x1c\xff\x59\x11\x8e\x45\x9d\x3c\xad\x53\xbf\x74\xce\xaf\xe1\xb9\x57\x7d\x18\x7f\x29\x0b\x92\x11\xe9\xfc\x26\xd3\x0b\x09\xb3\xbe\xd8\xdf\xd1\x03\x6a\xc7\x79\x0d\x08\xa7\xa7\x6b\x3d\x15\xb0\x06\xd9\xdc\x64\x57\x15\xc5\xe6\xb4\xff\x61\x07\x7d\x7c\xb7\xc2\xa0\xe9\xb3\x02\x09\xd7\x64\x77\x78\x39\x8e\xe3\x20\x8c\x24\x98\x53\x1d\x40\xa2\x49\x50\x25\x6b\xb4\xc4\x50\x22\xb9\x02\x46\xeb\x1f\x57\xd5\xdc\x79\x44\x94\x04\x12\xa5\x48\x7b\xad\xb0\x9a\x56\xf7\xbc\xe6\x48\x45\x02\x20\x51\x8a\xec\x1c\x79\x80\xc0\x7f\x56\x98\x66\xb8\x3b\xdf\xb7\x2e\x61\xa4\x5e\x69\x98\x76\x35\x57\x18\x66\x1f\x2e\xaf\xce\x67\x4d\x53\xf7\x87\x25\x1c\xe3\x2f\x68\x5d\x16\xf8\x2d\x88\x47\xb2\x90\x6f\xeb\x6b\x6e\x4e\x80\xb2\x1c\xff\x4b\x34\xff\x7f\xed\x52\xf9\x60\xf8\x4e\xf5\xbb\xfd\xb4\x06\xc7\x54\xf2\x0d\x94\x8c\x50\x09\xc7\x8b\x8a\x9a\x5f\x19\xdf\xeb\xe3\xf5\x82\xa6\x21\x1e\x57\x98\x02\x32\xdf\x65\x9b\x17\xd8\xf7\x46\x2f\x26\xd2\xe3\xa0\x1e\xe6\x0c\x78\x18\x96\xd3\xf6\xaa\x09\x59\x25\xdb\xbb\x2c\x09\x85\x35\x29\x0a\x22\x70\xc6\x68\x2e\xea\xba\xb0\xc7\x15\xc9\x56\x5d\x63\x11\x01\x12\xf3\x35\xa1\xaa\xdb\x7a\xec\x7c\x10\x78\xa7\xf2\x6b\xf4\x85\xac\xab\x35\xac\xf1\x9a\xf1\x4d\x57\xc8\xa7\x9f\xb5\xef\xb5\x85\xf4\xe8\x98\x82\x12\x54\xa5\x60\x4b\x10\xe4\x2b\x1e\xab\x4c\x1c\x8e\xbd\xc4\xa7\x60\xfa\xd3\x68\xfe\xa9\x68\x97\xca\x71\xc6\xc7\x1e\x41\x5f\x79\xaa\x04\x3e\x98\xd2\x09\x73\xdf\x2a\x90\x05\x20\x68\x51\x3a\xcf\x8f\x2b\x5a\x60\x21\xb6\x77\x6b\xa1\xe6\x5a\x12\xd7\x88\x3c\xb8\x18\xeb\xcb\x44\x5c\x2e\xdb\xfa\xed\x87\xba\xad\xd6\x05\x68\x55\xd0\x7f\xb7\xec\x1e\xd4\xc8\xbb\x6a\x7d\x78\x81\x84\x8e\x7a\x94\xc6\xe6\x73\xec\x90\x47\x04\x61\x4d\x13\xc7\x05\x62\x1b\x5a\xcf\x8e\x32\x88\xb8\x47\x36\xa8\x70\x7b\x60\xb1\xf6\xc0\xed\x6b\xad\xea\x4b\x6e\x61\x53\x44\x78\x63\x48\x06\x28\x10\x47\xaa\x89\xe2\x37\xa3\xed\x44\x91\x31\x9e\x6b\x25\x8f\xea\x98\xd6\x80\x77\x8f\x07\x74\x2e\xf1\xfd\x89\x4b\xb1\xbb\x83\x70\xc7\x9d\x1b\x5a\x00\x49\x85\xae\xd6\xca\xe7\xe7\xd7\xce\xa8\xca\x41\x45\x44\xc5\xc3\x6a\x69\xb1\xa1\x47\x27\x9b\x3b\xfd\x57\x75\x38\xb6\x14\xb5\x5f\x1e\xd5\x55\xdc\x3c\x71\x3d\x47\xf3\xea\xef\xfd\x6d\x55\x1d\xd1\x6f\x62\xe1\xe2\x94\x6b\x16\xbd\xed\xd5\x7a\x07\x54\x75\x18\xb8\x3d\x6f\x88\x15\x85\x4e\xec\x22\xb4\x62\x95\x28\xcc\x17\x14\x95\xd3\xb2\xc6\x42\x6c\xaf\x40\xaf\xeb\x17\xd5\x3a\x54\x51\xba\xdd\x72\xb9\x26\xe2\xf1\xb8\x56\x75\x6f\x14\x6c\xd0\x61\xdd\xa5\xb2\x1f\xac\x53\xe5\xb1\xbe\x93\xbc\x38\xcd\xf4\x85\x63\x5f\x88\x33\x5d\xc9\x4e\xeb\xd4\x50\x17\x50\xf5\x1b\x44\xf5\x2e\xe7\xb8\xf3\xf3\x58\xc5\xfc\x4e\x27\x2d\xe1\x5b\xe8\xcf\x15\xaa\xe1\xdd\xb3\x4f\x0c\xe7\x18\xb7\xab\x5d\x6b\x0e\xe9\x7a\xb9\x40\x07\xba\x5f\x7b\x70\x07\x70\xc1\x7c\x98\x8e\x83\x30\xf3\xea\x6a\x5b\xda\x6d\xf6\x1c\x9b\x7a\x78\xdf\xf1\x58\x98\xd3\x2a\xb2\x7e\x81\xae\xba\xa6\x18\x8f\xac\xb1\x90\x68\x5d\x0a\xc0\x88\x17\x04\xab\xcd\x09\xa2\x30\xfb\x7c\x73\x77\x3d\xfb\x11\xd6\x18\x89\x8a\x9b\x2b\x01\x7a\xdb\x3e\x41\x68\x86\xe1\x6e\x75\x02\x7f\xf9\xeb\x09\xfc\x1d\x51\xf8\xeb\x7f\xff\xd7\x5f\x1c\x6a\x7f\x2b\xe9\x43\x5f\xbd\x40\xb2\x15\x3d\xbd\xbc\x7a\x77\xf1\x2d\xdf\xfc\x10\xc2\x23\xbc\xfd\xb6\xa7\xc4\x7b\xfc\x3b\x2c\x76\x21\x92\x95\x50\xd6\xd3\x98\x89\x05\xcc\xa6\x17\xef\xae\xaf\xce\xa7\x33\xa8\xb5\x76\x09\x8b\x61\x75\x08\x45\x5c\xb6\xac\xfd\xc9\x53\xec\x83\x00\x5a\xba\xee\x72\x18\x82\x34\x44\xa5\x4f\x97\x57\x9f\xef\x2e\xa6\x33\x58\x13\x5a\x49\x3c\x42\x25\x2b\xd2\x10\x95\xde\x5f\x7f\xbe\x9d\xce\x60\xc5\x2a\x3e\x42\x9d\x3d\x94\x21\xaa\x9c\x4f\x7e\x9b\xce\x20\x47\x9b\x11\x8a\xec\x60\x04\x32\xed\x9b\x9d\x81\xce\xb9\x3e\xea\x7f\xe1\xfc\xcd\xf6\x0b\xe7\xc1\x6c\xfa\x58\x1c\x67\x1a\xf5\xfe\x57\x75\x4d\xb5\x68\x4a\x3e\x7b\x3c\x86\x3b\xa1\xdd\xf6\xdd\xf7\x54\x3d\x12\x40\xec\xcd\xd3\x9e\xf9\xe8\xc8\xac\xce\x67\xca\x6a\x38\xdc\x9c\x9c\xe4\xce\x12\x87\x48\xee\x18\xd1\x44\xa4\x8a\xeb\x71\xa4\x8a\x80\xed\x33\x22\x80\xe9\x75\x1c\x15\x03\x24\xbb\x80\x7c\x0a\xd5\xb3\xfc\xe5\x79\xc2\x5b\x3b\x78\xec\x62\xda\x6f\x62\xb5\xed\xd2\x7c\xb7\xd8\xfc\x10\xd3\xb6\x49\x18\x31\x6a\xc4\xbc\xab\x87\xc3\x21\x42\x5f\x4f\x16\x8f\xef\x20\xf7\x80\x9f\xb4\xa5\x55\x9d\xfe\x9d\x60\xc6\x14\x0c\x87\x1a\x0d\x73\xfc\x6b\xba\x39\x52\x45\x80\x6a\x93\x4d\xc1\x50\x1e\x35\x50\xd2\x81\x5c\x03\xa5\xa1\xe8\x04\xd1\x88\x39\x94\xd4\xfb\x83\x7e\x1d\x93\x67\xf0\x24\xe2\x44\x15\x5a\x1d\x8b\xd7\x67\x6a\x66\x6d\x5e\x2e\xba\xde\xca\xc2\x18\x2b\x50\x4f\xe5\x88\x2f\xc5\xf3\xf3\x60\xd9\x1e\x0c\xc7\x36\x14\x65\xee\xcc\x81\xe6\xa9\x3d\xb6\x27\xa1\xc0\xc8\x99\xd2\xd9\x3e\xb6\x32\x53\x06\x6b\xa6\x3f\x8f\x8b\x5c\xfb\xb2\x1e\x89\x3d\xf2\xcc\x76\xd6\xc4\xd0\xb0\xf1\x30\xb8\x5c\x2c\xf7\x85\xda\xbe\x7b\xb4\x5b\xef\xc1\x19\xd1\xd9\x12\x38\x52\xe3\xf4\xf1\xb0\x33\x29\xce\x3c\x75\x2e\x41\x4d\x09\x5c\xe3\x3b\x95\x9c\x3d\x90\x1c\xe7\x3b\xbe\x95\x67\x41\x8a\x45\x70\x7a\x3f\xed\xd7\x7a\xea\x14\x99\x78\x97\x27\xc4\xe9\x7a\xeb\x96\x6f\x57\x67\xcf\xdc\xe1\xe1\xf1\x0e\xdb\x05\xc1\x85\xce\x7d\x92\xb8\x6b\x0e\x97\xa4\x20\xdb\x80\xc0\xf3\x09\xe4\x44\x94\x05\xda\x98\x1b\x86\x14\xb0\xae\x90\xc0\xc5\xf0\xa8\x74\x08\x33\x39\x5f\xf4\x30\x4a\xa6\x20\x06\x55\xdc\xfb\xb8\xf9\x78\x1d\x93\x21\x83\x4a\xf6\x2f\x5f\x1e\xaf\x61\x1a\x5e\x50\xbd\xbd\x7b\x25\xc6\x6b\x98\x0c\xe9\x9b\x06\x4a\x24\x57\x27\x80\x68\xf3\x25\xa6\xb9\x29\xc5\x46\x34\xdd\xbd\x1b\x0e\x18\x52\xb0\x41\x68\x20\x53\xf5\x09\xf0\x5b\xc4\xdb\x53\x17\x6b\x27\x62\xfb\x1e\xc1\x59\x3a\x19\x66\xb8\x2e\x2e\x3b\xc4\x72\xc7\x15\xea\x4c\x6e\x2e\xd3\xab\x74\xfa\x4c\x51\x25\x3a\xaa\xa9\x9e\x9e\xce\xcc\xf5\x8a\x60\xbe\xa7\x39\x7f\x7e\x6e\x23\x2f\xfd\x74\x70\xd5\xdd\xab\xa2\xbe\x8c\x31\xba\x9a\x67\x9c\x8c\xb8\x4a\xab\x92\x1c\xa2\xcc\x2a\x80\x12\xae\x23\x9b\xdc\x5c\xc6\x14\x91\x29\xb2\xd8\x82\x26\x37\xa4\x83\x38\xae\xec\x28\xad\x83\x59\x99\xa2\x0a\x8e\x5e\xb2\x83\x1d\x46\x86\xf3\xd4\xdb\x6d\xf9\xe6\x69\x44\x3d\x4d\x9a\x9d\xf7\x39\xbc\x75\x2a\xd4\xa3\x63\x9f\xe6\x3f\xb1\x4e\xa5\x6e\xb6\x39\x12\x38\xb2\xc1\x7d\x9c\x51\x22\xb7\xbd\x44\x9f\xe3\x0d\x93\x1e\x00\x49\x54\xa4\xd3\x5d\x0f\xa1\x52\x00\xce\x9d\x7d\x35\xb9\xb9\xf4\xa6\x5d\xe9\xe7\xf1\x29\x50\xaa\x89\x8a\x98\x72\x8e\x58\x6e\x67\xf5\x84\x79\xdd\xbd\x45\xb7\x71\x42\xce\xa0\xbe\x93\x7b\x7b\x4b\xe3\xdb\xc6\x4a\xc2\xeb\x50\x8c\x04\xb6\x2a\x5c\x5f\xc8\x34\xeb\x54\x25\xea\xdc\x64\x05\x4a\x04\x64\xfa\x56\x69\x87\x4e\x71\xbc\x8e\xe3\x51\x05\xf3\x80\x61\x36\xb9\xb9\xfc\xe3\x66\x72\xf7\x5e\xa7\x89\xbb\x67\x0f\x0f\x83\xdd\x8b\xbc\xb9\x34\xa4\xbf\x5e\xdc\xfe\xec\xba\x9a\x6a\x87\xc8\x09\xb4\x3d\x5a\x37\x1f\x5c\x9c\xc1\x71\xf3\xd1\xd7\xf6\xfb\x8b\xae\x1c\xd5\x78\x7e\x6f\xa0\x60\xf6\xf3\x64\x7a\x51\xbf\xb7\x64\x9d\xcc\xe7\xc6\x1c\x26\xd0\x68\x8c\x14\x08\x1d\xa4\x61\x79\xa7\x0f\x35\x08\x83\x93\x42\x4d\xe4\x02\xaa\x78\x61\xe6\x09\x56\x62\xae\x99\xcd\x7f\xcd\x24\xee\x9f\x74\x22\x78\x7d\x9b\x0c\x35\x15\x99\x9d\x42\x2f\x4c\x0b\xdd\x98\xa9\xa2\x6b\x7b\x5f\xbb\xab\x68\xe5\xc1\x1a\x6d\x00\x15\x82\x25\xc4\x53\x5e\x4e\xac\x73\x52\x6a\xee\x2b\xaf\xa3\x38\x4b\x42\x4d\x4a\xc0\xd1\x1b\x67\x48\x26\xc8\x16\xe5\x09\x8b\x47\x64\xae\x7f\x8b\xb9\xc2\x3b\x92\xd9\x9e\x60\xd5\xbb\x76\xa4\xcb\x39\xe2\xf6\x92\x08\x18\xc7\x69\x42\x73\xa5\x60\x9d\x2f\xa7\x3f\xb5\x89\xcc\xc4\xdd\x7e\xe7\xc3\x7d\x84\x10\xc7\xed\xb1\x83\xb2\xdf\x01\xec\x90\x00\x63\x55\x66\xda\xe5\xeb\xdc\x31\x76\x5c\x7f\x25\x45\x0f\x85\x1b\x1d\x3a\x20\x74\xc1\x4e\x74\x4f\x37\xdf\x47\xd9\x8a\xc4\x45\x2e\x5c\xd3\xeb\x01\x05\x84\x5f\xa0\xc1\x32\xc3\xc1\xa4\x06\x98\x7c\x72\x28\x9a\x0e\x53\x20\xb1\x82\xe3\x37\x51\xfa\x26\xe1\x39\x57\xa7\x7e\x51\xa9\xde\x64\x34\x2e\x56\xdf\xdd\xf2\xac\x50\xf1\x18\x29\x6a\xa8\x9f\x75\xd8\x47\xcd\xc8\x25\x31\x7d\x26\x4d\x0b\x3b\x84\x6f\x4e\xd7\xd4\x19\xa2\xe6\x6c\xab\xfb\x59\x1d\x60\xd4\xf9\x25\xf1\x48\x66\x7b\x1f\xa9\x6b\x29\x51\x7f\x98\xa8\x2e\xd6\x3c\xd9\x9d\xf7\x4d\x2b\x27\x44\xce\x5c\x53\xc5\xb7\x91\x1d\x7f\x22\xf8\x3d\x6d\xf1\x1d\x15\xb2\x17\xe3\xeb\xd4\x14\xa8\x04\xee\x5f\xd6\xde\x5c\x2e\x67\x2a\xee\x5c\x6f\x13\xcb\x6d\x1f\x0b\xbe\xe4\xd2\x89\x27\x4b\xf4\x57\xcc\x5d\x55\xba\xfa\x91\x73\xf8\x5f\x21\x67\x39\x78\xfb\xd8\xee\x30\xdc\x7e\x74\x79\x03\xb7\x1f\xed\x2c\x3f\x37\x4d\xe5\x60\xdc\x3e\xb7\xe7\x4c\xbb\x39\x6f\x9c\x4c\x75\xa8\xdd\x24\xc1\x76\x32\x5f\xd5\x2e\x04\xa3\x6c\xe5\x8b\x7c\xc4\xb0\x7e\xcf\x33\xbb\x5b\xfc\x67\x85\x75\x25\x53\x86\x4b\x7d\x2d\xeb\xbc\x92\x8a\x29\xc3\x66\x11\x35\x77\x24\xeb\x0f\x68\xe1\x1c\x36\xd8\x99\x6c\x30\x04\xc9\xbb\xc1\x08\x5e\x4b\x63\x24\x98\x1c\x6d\x23\x7b\x55\x3b\x4a\x1b\xac\xef\xb0\x20\x62\xa5\xf4\x38\xf8\x7d\x38\xc3\x05\x47\x95\x8d\x23\x78\xc4\xf3\x7a\x92\x39\x01\x04\x1c\x3d\xc2\xfb\xbb\xbb\x9b\xde\xcf\x8c\x1b\x52\x21\x11\xcd\x11\x6f\x22\xfb\xa6\xa4\xf7\xdf\x20\x79\x85\xe1\xa7\x1e\x87\x82\xf9\xc9\x0e\x46\x19\xfc\x1b\x16\xa8\x10\x8a\x67\x07\x31\xb2\x2e\xfd\x3f\x4c\xe9\xa8\x65\x6b\x77\x5e\x3d\x3d\x55\xf8\x8b\x02\x2d\x9b\x8f\xa7\x11\x5a\x56\xfa\x8e\x70\x51\xd7\xb8\xe9\xeb\xbd\x4f\xe0\x48\xa9\xaa\xfe\xe5\xe8\x51\xfd\xa3\xd5\x38\x3a\x69\xbe\xb6\x11\xbb\x4c\xbd\xa0\x02\xca\x00\x3f\xfc\xef\x0f\xff\x17\x00\x00\xff\xff\x19\x9a\x50\xcb\xbb\xac\x00\x00")
+
+func wski18nResourcesEn_usAllJsonBytes() ([]byte, error) {
+ return bindataRead(
+ _wski18nResourcesEn_usAllJson,
+ "wski18n/resources/en_US.all.json",
+ )
+}
+
+func wski18nResourcesEn_usAllJson() (*asset, error) {
+ bytes, err := wski18nResourcesEn_usAllJsonBytes()
+ if err != nil {
+ return nil, err
+ }
+
+ info := bindataFileInfo{name: "wski18n/resources/en_US.all.json", size: 44219, mode: os.FileMode(420), modTime: time.Unix(1491513831, 0)}
+ a := &asset{bytes: bytes, info: info}
+ return a, nil
+}
+
+var _wski18nResourcesEs_esAllJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00")
+
+func wski18nResourcesEs_esAllJsonBytes() ([]byte, error) {
+ return bindataRead(
+ _wski18nResourcesEs_esAllJson,
+ "wski18n/resources/es_ES.all.json",
+ )
+}
+
+func wski18nResourcesEs_esAllJson() (*asset, error) {
+ bytes, err := wski18nResourcesEs_esAllJsonBytes()
+ if err != nil {
+ return nil, err
+ }
+
+ info := bindataFileInfo{name: "wski18n/resources/es_ES.all.json", size: 0, mode: os.FileMode(420), modTime: time.Unix(1491513831, 0)}
+ a := &asset{bytes: bytes, info: info}
+ return a, nil
+}
+
+var _wski18nResourcesFr_frAllJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8a\xe6\x52\x50\xa8\xe6\x52\x50\x50\x50\x50\xca\x4c\x51\xb2\x52\x50\x4a\xaa\x2c\x48\x2c\x2e\x56\x48\x4e\x2d\x2a\xc9\x4c\xcb\x4c\x4e\x2c\x49\x55\x48\xce\x48\x4d\xce\xce\xcc\x4b\x57\xd2\x81\x28\x2c\x29\x4a\xcc\x2b\xce\x49\x2c\xc9\xcc\xcf\x03\xe9\x08\xce\xcf\x4d\x55\x40\x12\x53\xc8\xcc\x53\x70\x2b\x4a\xcd\x4b\xce\x50\xe2\x52\x50\xa8\xe5\x8a\xe5\x02\x04\x00\x00\xff\xff\x45\xa4\xe9\x62\x65\x00\x00\x00")
+
+func wski18nResourcesFr_frAllJsonBytes() ([]byte, error) {
+ return bindataRead(
+ _wski18nResourcesFr_frAllJson,
+ "wski18n/resources/fr_FR.all.json",
+ )
+}
+
+func wski18nResourcesFr_frAllJson() (*asset, error) {
+ bytes, err := wski18nResourcesFr_frAllJsonBytes()
+ if err != nil {
+ return nil, err
+ }
+
+ info := bindataFileInfo{name: "wski18n/resources/fr_FR.all.json", size: 101, mode: os.FileMode(420), modTime: time.Unix(1491513831, 0)}
+ a := &asset{bytes: bytes, info: info}
+ return a, nil
+}
+
+var _wski18nResourcesIt_itAllJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00")
+
+func wski18nResourcesIt_itAllJsonBytes() ([]byte, error) {
+ return bindataRead(
+ _wski18nResourcesIt_itAllJson,
+ "wski18n/resources/it_IT.all.json",
+ )
+}
+
+func wski18nResourcesIt_itAllJson() (*asset, error) {
+ bytes, err := wski18nResourcesIt_itAllJsonBytes()
+ if err != nil {
+ return nil, err
+ }
+
+ info := bindataFileInfo{name: "wski18n/resources/it_IT.all.json", size: 0, mode: os.FileMode(420), modTime: time.Unix(1491513831, 0)}
+ a := &asset{bytes: bytes, info: info}
+ return a, nil
+}
+
+var _wski18nResourcesJa_jaAllJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00")
+
+func wski18nResourcesJa_jaAllJsonBytes() ([]byte, error) {
+ return bindataRead(
+ _wski18nResourcesJa_jaAllJson,
+ "wski18n/resources/ja_JA.all.json",
+ )
+}
+
+func wski18nResourcesJa_jaAllJson() (*asset, error) {
+ bytes, err := wski18nResourcesJa_jaAllJsonBytes()
+ if err != nil {
+ return nil, err
+ }
+
+ info := bindataFileInfo{name: "wski18n/resources/ja_JA.all.json", size: 0, mode: os.FileMode(420), modTime: time.Unix(1491513831, 0)}
+ a := &asset{bytes: bytes, info: info}
+ return a, nil
+}
+
+var _wski18nResourcesKo_krAllJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00")
+
+func wski18nResourcesKo_krAllJsonBytes() ([]byte, error) {
+ return bindataRead(
+ _wski18nResourcesKo_krAllJson,
+ "wski18n/resources/ko_KR.all.json",
+ )
+}
+
+func wski18nResourcesKo_krAllJson() (*asset, error) {
+ bytes, err := wski18nResourcesKo_krAllJsonBytes()
+ if err != nil {
+ return nil, err
+ }
+
+ info := bindataFileInfo{name: "wski18n/resources/ko_KR.all.json", size: 0, mode: os.FileMode(420), modTime: time.Unix(1491513831, 0)}
+ a := &asset{bytes: bytes, info: info}
+ return a, nil
+}
+
+var _wski18nResourcesPt_brAllJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00")
+
+func wski18nResourcesPt_brAllJsonBytes() ([]byte, error) {
+ return bindataRead(
+ _wski18nResourcesPt_brAllJson,
+ "wski18n/resources/pt_BR.all.json",
+ )
+}
+
+func wski18nResourcesPt_brAllJson() (*asset, error) {
+ bytes, err := wski18nResourcesPt_brAllJsonBytes()
+ if err != nil {
+ return nil, err
+ }
+
+ info := bindataFileInfo{name: "wski18n/resources/pt_BR.all.json", size: 0, mode: os.FileMode(420), modTime: time.Unix(1491513831, 0)}
+ a := &asset{bytes: bytes, info: info}
+ return a, nil
+}
+
+var _wski18nResourcesZh_hansAllJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00")
+
+func wski18nResourcesZh_hansAllJsonBytes() ([]byte, error) {
+ return bindataRead(
+ _wski18nResourcesZh_hansAllJson,
+ "wski18n/resources/zh_Hans.all.json",
+ )
+}
+
+func wski18nResourcesZh_hansAllJson() (*asset, error) {
+ bytes, err := wski18nResourcesZh_hansAllJsonBytes()
+ if err != nil {
+ return nil, err
+ }
+
+ info := bindataFileInfo{name: "wski18n/resources/zh_Hans.all.json", size: 0, mode: os.FileMode(420), modTime: time.Unix(1491513831, 0)}
+ a := &asset{bytes: bytes, info: info}
+ return a, nil
+}
+
+var _wski18nResourcesZh_hantAllJson = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x01\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00")
+
+func wski18nResourcesZh_hantAllJsonBytes() ([]byte, error) {
+ return bindataRead(
+ _wski18nResourcesZh_hantAllJson,
+ "wski18n/resources/zh_Hant.all.json",
+ )
+}
+
+func wski18nResourcesZh_hantAllJson() (*asset, error) {
+ bytes, err := wski18nResourcesZh_hantAllJsonBytes()
+ if err != nil {
+ return nil, err
+ }
+
+ info := bindataFileInfo{name: "wski18n/resources/zh_Hant.all.json", size: 0, mode: os.FileMode(420), modTime: time.Unix(1491513831, 0)}
+ a := &asset{bytes: bytes, info: info}
+ return a, nil
+}
+
+// Asset loads and returns the asset for the given name.
+// It returns an error if the asset could not be found or
+// could not be loaded.
+func Asset(name string) ([]byte, error) {
+ cannonicalName := strings.Replace(name, "\\", "/", -1)
+ if f, ok := _bindata[cannonicalName]; ok {
+ a, err := f()
+ if err != nil {
+ return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
+ }
+ return a.bytes, nil
+ }
+ return nil, fmt.Errorf("Asset %s not found", name)
+}
+
+// MustAsset is like Asset but panics when Asset would return an error.
+// It simplifies safe initialization of global variables.
+func MustAsset(name string) []byte {
+ a, err := Asset(name)
+ if err != nil {
+ panic("asset: Asset(" + name + "): " + err.Error())
+ }
+
+ return a
+}
+
+// AssetInfo loads and returns the asset info for the given name.
+// It returns an error if the asset could not be found or
+// could not be loaded.
+func AssetInfo(name string) (os.FileInfo, error) {
+ cannonicalName := strings.Replace(name, "\\", "/", -1)
+ if f, ok := _bindata[cannonicalName]; ok {
+ a, err := f()
+ if err != nil {
+ return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
+ }
+ return a.info, nil
+ }
+ return nil, fmt.Errorf("AssetInfo %s not found", name)
+}
+
+// AssetNames returns the names of the assets.
+func AssetNames() []string {
+ names := make([]string, 0, len(_bindata))
+ for name := range _bindata {
+ names = append(names, name)
+ }
+ return names
+}
+
+// _bindata is a table, holding each asset generator, mapped to its name.
+var _bindata = map[string]func() (*asset, error){
+ "wski18n/resources/.DS_Store": wski18nResourcesDs_store,
+ "wski18n/resources/de_DE.all.json": wski18nResourcesDe_deAllJson,
+ "wski18n/resources/en_US.all.json": wski18nResourcesEn_usAllJson,
+ "wski18n/resources/es_ES.all.json": wski18nResourcesEs_esAllJson,
+ "wski18n/resources/fr_FR.all.json": wski18nResourcesFr_frAllJson,
+ "wski18n/resources/it_IT.all.json": wski18nResourcesIt_itAllJson,
+ "wski18n/resources/ja_JA.all.json": wski18nResourcesJa_jaAllJson,
+ "wski18n/resources/ko_KR.all.json": wski18nResourcesKo_krAllJson,
+ "wski18n/resources/pt_BR.all.json": wski18nResourcesPt_brAllJson,
+ "wski18n/resources/zh_Hans.all.json": wski18nResourcesZh_hansAllJson,
+ "wski18n/resources/zh_Hant.all.json": wski18nResourcesZh_hantAllJson,
+}
+
+// AssetDir returns the file names below a certain
+// directory embedded in the file by go-bindata.
+// For example if you run go-bindata on data/... and data contains the
+// following hierarchy:
+// data/
+// foo.txt
+// img/
+// a.png
+// b.png
+// then AssetDir("data") would return []string{"foo.txt", "img"}
+// AssetDir("data/img") would return []string{"a.png", "b.png"}
+// AssetDir("foo.txt") and AssetDir("notexist") would return an error
+// AssetDir("") will return []string{"data"}.
+func AssetDir(name string) ([]string, error) {
+ node := _bintree
+ if len(name) != 0 {
+ cannonicalName := strings.Replace(name, "\\", "/", -1)
+ pathList := strings.Split(cannonicalName, "/")
+ for _, p := range pathList {
+ node = node.Children[p]
+ if node == nil {
+ return nil, fmt.Errorf("Asset %s not found", name)
+ }
+ }
+ }
+ if node.Func != nil {
+ return nil, fmt.Errorf("Asset %s not found", name)
+ }
+ rv := make([]string, 0, len(node.Children))
+ for childName := range node.Children {
+ rv = append(rv, childName)
+ }
+ return rv, nil
+}
+
+type bintree struct {
+ Func func() (*asset, error)
+ Children map[string]*bintree
+}
+var _bintree = &bintree{nil, map[string]*bintree{
+ "wski18n": &bintree{nil, map[string]*bintree{
+ "resources": &bintree{nil, map[string]*bintree{
+ ".DS_Store": &bintree{wski18nResourcesDs_store, map[string]*bintree{}},
+ "de_DE.all.json": &bintree{wski18nResourcesDe_deAllJson, map[string]*bintree{}},
+ "en_US.all.json": &bintree{wski18nResourcesEn_usAllJson, map[string]*bintree{}},
+ "es_ES.all.json": &bintree{wski18nResourcesEs_esAllJson, map[string]*bintree{}},
+ "fr_FR.all.json": &bintree{wski18nResourcesFr_frAllJson, map[string]*bintree{}},
+ "it_IT.all.json": &bintree{wski18nResourcesIt_itAllJson, map[string]*bintree{}},
+ "ja_JA.all.json": &bintree{wski18nResourcesJa_jaAllJson, map[string]*bintree{}},
+ "ko_KR.all.json": &bintree{wski18nResourcesKo_krAllJson, map[string]*bintree{}},
+ "pt_BR.all.json": &bintree{wski18nResourcesPt_brAllJson, map[string]*bintree{}},
+ "zh_Hans.all.json": &bintree{wski18nResourcesZh_hansAllJson, map[string]*bintree{}},
+ "zh_Hant.all.json": &bintree{wski18nResourcesZh_hantAllJson, map[string]*bintree{}},
+ }},
+ }},
+}}
+
+// RestoreAsset restores an asset under the given directory
+func RestoreAsset(dir, name string) error {
+ data, err := Asset(name)
+ if err != nil {
+ return err
+ }
+ info, err := AssetInfo(name)
+ if err != nil {
+ return err
+ }
+ err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
+ if err != nil {
+ return err
+ }
+ err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
+ if err != nil {
+ return err
+ }
+ err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// RestoreAssets restores an asset under the given directory recursively
+func RestoreAssets(dir, name string) error {
+ children, err := AssetDir(name)
+ // File
+ if err != nil {
+ return RestoreAsset(dir, name)
+ }
+ // Dir
+ for _, child := range children {
+ err = RestoreAssets(dir, filepath.Join(name, child))
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func _filePath(dir, name string) string {
+ cannonicalName := strings.Replace(name, "\\", "/", -1)
+ return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
+}
+