This is an automated email from the ASF dual-hosted git repository. csantanapr pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk-runtime-swift.git
commit f8709160cd07e9ca03acfa7a85ce3a9d2ca2528f Author: Carlos Santana <[email protected]> AuthorDate: Fri Jan 4 00:58:41 2019 -0500 update compiler and added tests --- .gitignore | 3 +- .travis.yml | 2 +- README.md | 71 +++++++---- ansible/environments/local/group_vars/all | 7 +- .../swiftbuild.py.launcher.swift | 67 ----------- .../Dockerfile | 15 ++- .../swift42Action/Package.swift | 28 +++-- .../build.gradle | 14 ++- core/swift42Action/buildandrecord.py | 82 +++++++++++++ .../build.gradle => swift42Action/main.swift} | 9 +- .../swiftbuild.py | 102 +++++++--------- core/swift42Action/swiftbuild.py.launcher.swift | 133 +++++++++++++++++++++ examples/swift-main-single/Makefile | 8 +- examples/swift-main-single/main.swift | 31 ++++- .../swift-main-zip/HelloSwift4/Package.swift | 28 +++-- .../swift-main-zip/HelloSwift4/Sources/main.swift | 10 ++ examples/swift-main-zip/Makefile | 39 ++++++ settings.gradle | 2 +- tests/dat/actions/Makefile | 30 +++++ tests/dat/build.sh | 3 + tests/dat/build/swift4.2/HelloSwift4.zip | Bin 0 -> 2186350 bytes tests/dat/build/swift4.2/HelloSwift4Codable.zip | Bin 0 -> 2190710 bytes tests/dat/build/swift4.2/SwiftyRequest.zip | Bin 0 -> 154371 bytes tests/dat/build/swift4.2/SwiftyRequestCodable.zip | Bin 0 -> 159216 bytes .../Swift311ActionContainerTests.scala | 54 +++++++++ .../Swift41ActionContainerTests.scala | 54 +++++++++ .../Swift41CodableActionContainerTests.scala | 37 ++++++ .../Swift42ActionContainerTests.scala | 118 ++++++++++++++++++ ...la => Swift42CodableActionContainerTests.scala} | 6 +- .../SwiftActionContainerTests.scala | 48 -------- .../SwiftCodableActionContainerTests.scala | 33 ----- .../test/scala/runtime/sdk/Swift42SDKTests.scala | 14 ++- tools/travis/build.sh | 6 + tools/travis/publish.sh | 13 ++ 34 files changed, 793 insertions(+), 274 deletions(-) diff --git a/.gitignore b/.gitignore index 6dc577b..100215f 100644 --- a/.gitignore +++ b/.gitignore @@ -76,4 +76,5 @@ ansible/roles/nginx/files/*cert.pem # Makefile artifacts test.json *.done -examples/swift-main-single/main.zip +examples/*/*.zip +ActionLoop/ diff --git a/.travis.yml b/.travis.yml index 1d530a6..b92ee9d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,7 +29,7 @@ deploy: all_branches: true repo: apache/incubator-openwhisk-runtime-swift - provider: script - script: "./tools/travis/publish.sh openwhisk 4.1 latest" + script: "./tools/travis/publish.sh openwhisk 4.1 latest && ./tools/travis/publish.sh openwhisk 4.2 latest" on: branch: master repo: apache/incubator-openwhisk-runtime-swift diff --git a/README.md b/README.md index a14b034..4f3cefd 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ ## Changelogs - [Swift 3.1.1 CHANGELOG.md](core/swift3.1.1Action/CHANGELOG.md) - [Swift 4.1 CHANGELOG.md](core/swift41Action/CHANGELOG.md) +- [Swift 4.2 CHANGELOG.md](core/swift42Action/CHANGELOG.md) ## Quick Swift Action ### Simple swift action hello.swift @@ -57,7 +58,7 @@ func main(input: Employee, respondWith: (Employee?, Error?) -> Void) -> Void { } ``` ``` -wsk action update helloCodableAsync helloCodableAsync.swift swift:4.1 +wsk action update helloCodableAsync helloCodableAsync.swift swift:4.2 ``` ``` ok: updated action helloCodableAsync @@ -94,7 +95,7 @@ func main(input: Employee, respondWith: (Employee?, Error?) -> Void) -> Void { } ``` ``` -wsk action update helloCodableError helloCodableError.swift swift:4.1 +wsk action update helloCodableError helloCodableError.swift swift:4.2 ``` ``` ok: updated action helloCodableError @@ -114,15 +115,37 @@ wsk action invoke helloCodableError -b -p id 42 -p name Carlos } ``` -### Packaging an action as a Swift executable using Swift 4 +## Packaging an action as a Swift executable using Swift 4.x When you create an OpenWhisk Swift action with a Swift source file, it has to be compiled into a binary before the action is run. Once done, subsequent calls to the action are much faster until the container holding your action is purged. This delay is known as the cold-start delay. -To avoid the cold-start delay, you can compile your Swift file into a binary and then upload to OpenWhisk in a zip file. As you need the OpenWhisk scaffolding, the easiest way to create the binary is to build it within the same environment as it will be run in. These are the steps: +To avoid the cold-start delay, you can compile your Swift file into a binary and then upload to OpenWhisk in a zip file. As you need the OpenWhisk scaffolding, the easiest way to create the binary is to build it within the same environment as it will be run in. + +### Compiling Swift 4.2 + +### Compiling Swift 4.2 single file + +Use the docker container and pass the single source file as stdin. +Pass the name of the method to the flag `-compile` +``` +docker run -i openwhisk/action-swift-v4.2 -compile main <main.swift >../action.zip +``` + +### Compiling Swift 4.2 multiple files with dependencies +Use the docker container and pass a zip archive containing a `Package.swift` and source files a main source file in the location `Sources/main.swift`. +``` +zip - -r * | docker run -i openwhisk/action-swift-v4.2 -compile main >../action.zip +``` + +For more build examples see [here](./examples/) + + +### Compiling Swift 4.1 +These are the steps: - Run an interactive Swift action container. ``` - docker run --rm -it -v "$(pwd):/owexec" openwhisk/action-swift-v4.1 bash + docker run --rm -it -v "$(pwd):/owexec" openwhisk/action-swift-v4.2 bash ``` This puts you in a bash shell within the Docker container. @@ -139,7 +162,7 @@ To avoid the cold-start delay, you can compile your Swift file into a binary and Copy any additional source files to `/swift4Action/spm-build/Sources/Action/` -- (Optional) Create the `Package.swift` file to add dependencies. +- Create the `Package.swift` file to add dependencies. ```swift // swift-tools-version:4.0 // The swift-tools-version declares the minimum version of Swift required to build this package. @@ -167,7 +190,7 @@ let package = Package( ``` As you can see this example adds `SwiftyRequest` dependencies. - Notice that now with swift:4.1 is no longer required to include `CCurl`, `Kitura-net` and `SwiftyJSON` in your own `Package.swift`. + Notice that now with swift:4.2 is no longer required to include `CCurl`, `Kitura-net` and `SwiftyJSON` in your own `Package.swift`. You are free now to use no dependencies, or add the combination that you want with the versions you want. - Copy Package.swift to spm-build directory @@ -199,7 +222,7 @@ let package = Package( - Upload it to OpenWhisk with the action name helloSwifty: ``` - wsk action update helloSwiftly hello.zip openwhisk/action-swift-v4.1 + wsk action update helloSwiftly hello.zip openwhisk/action-swift-v4.2 ``` - To check how much faster it is, run @@ -224,15 +247,15 @@ Having a project directory `Hello` under a directory `actions` like the followin actions/Hello/Package.swift actions/Hello/Sources/main.swift ``` -Change to the parent directory then run the compile script specify the project directory, the kind `swift:3.1.1` or `swift:4.1` and any swiftc build flags like the following: +Change to the parent directory then run the compile script specify the project directory, the kind `swift:3.1.1` or `swift:4.2` and any swiftc build flags like the following: ``` cd actions/ -incubator-runtime-swift/tools/build/compile.sh Hello swift:4.1 -v +incubator-runtime-swift/tools/build/compile.sh Hello swift:4.2 -v ``` This will produce a zip `build/swift4/Hello.zip` ### SwiftyJSON using single source action file -If you have a swift:3.1.1 action not compile, just as source using the `SwiftyJSON` package, you need to precompile your action and specify the version of SwiftyJSON you wan to use for swift:4.1 kind action. +If you have a swift:3.1.1 action not compile, just as source using the `SwiftyJSON` package, you need to precompile your action and specify the version of SwiftyJSON you wan to use for swift:4.2 kind action. Take into account that starting with Swift 4 there is better support to manage JSON data natively. Note: This is only applicable to the base image provided for the Swift 4 runtime, other downstream such as IBM Cloud Functions extending this image might provide additional SDK and packages including `SwiftyJSON` and IBM Watson SDK, check the vendor documentation for more specific information about packages and versions. @@ -241,7 +264,7 @@ Note: This is only applicable to the base image provided for the Swift 4 runtime ``` ./gradlew core:swift40Action:distDocker ``` -This will produce the image `whisk/action-swift-v4.1` +This will produce the image `whisk/action-swift-v4.2` Build and Push image ``` @@ -249,10 +272,6 @@ docker login ./gradlew core:swift40Action:distDocker -PdockerImagePrefix=$prefix-user -PdockerRegistry=docker.io ``` -### Swift 4.1 Experimental -We have a runtime for swift 4.1, is experimental as we are trying beta builds released by Swift org. -Follow same insructions for Swift 4.1 above and replace the kind wih `swift:4.1` and image with `openwhisk/action-swift-v4.1` - ## Codable Suppor with Swift 4.x @@ -273,7 +292,7 @@ func main(input: Employee, respondWith: (Employee?, Error?) -> Void) -> Void { } ``` ``` -wsk action update helloCodableAsync helloCodableAsync.swift swift:4.1 +wsk action update helloCodableAsync helloCodableAsync.swift swift:4.2 ``` ``` ok: updated action helloCodableAsync @@ -310,7 +329,7 @@ func main(input: Employee, respondWith: (Employee?, Error?) -> Void) -> Void { } ``` ``` -wsk action update helloCodableError helloCodableError.swift swift:4.1 +wsk action update helloCodableError helloCodableError.swift swift:4.2 ``` ``` ok: updated action helloCodableError @@ -331,24 +350,24 @@ wsk action invoke helloCodableError -b -p id 42 -p name Carlos } ``` -### Using Swift 4.1 +### Using Swift 4.2 To use as a docker action ``` -wsk action update myAction myAction.swift --docker openwhisk/action-swift-v4.1:1.0.7 +wsk action update myAction myAction.swift --docker openwhisk/action-swift-v4.2:1.0.7 ``` This works on any deployment of Apache OpenWhisk ### To use on deployment that contains the rutime as a kind To use as a kind action ``` -wsk action update myAction myAction.swift --kind swift:4.1 +wsk action update myAction myAction.swift --kind swift:4.2 ``` ## Local development ``` ./gradlew core:swift41Action:distDocker ``` -This will produce the image `whisk/action-swift-v4.1` +This will produce the image `whisk/action-swift-v4.2` Build and Push image ``` @@ -356,7 +375,7 @@ docker login ./gradlew core:swift41Action:distDocker -PdockerImagePrefix=$prefix-user -PdockerRegistry=docker.io ``` -Deploy OpenWhisk using ansible environment that contains the kind `swift:4.1` +Deploy OpenWhisk using ansible environment that contains the kind `swift:4.2` Assuming you have OpenWhisk already deploy localy and `OPENWHISK_HOME` pointing to root directory of OpenWhisk core repository. Set `ROOTDIR` to the root directory of this repository. @@ -400,12 +419,12 @@ Using IntelliJ: #### Using container image to test To use as docker action push to your own dockerhub account ``` -docker tag whisk/action-swift-v4.1 $user_prefix/action-swift-v4.1 -docker push $user_prefix/action-swift-v4.1 +docker tag whisk/action-swift-v4.2 $user_prefix/action-swift-v4.2 +docker push $user_prefix/action-swift-v4.2 ``` Then create the action using your the image from dockerhub ``` -wsk action update myAction myAction.swift --docker $user_prefix/action-swift-v4.1 +wsk action update myAction myAction.swift --docker $user_prefix/action-swift-v4.2 ``` The `$user_prefix` is usually your dockerhub user id. diff --git a/ansible/environments/local/group_vars/all b/ansible/environments/local/group_vars/all index 4caf10e..64dd3f0 100755 --- a/ansible/environments/local/group_vars/all +++ b/ansible/environments/local/group_vars/all @@ -50,10 +50,15 @@ runtimes_manifest: name: "action-swift-v3.1.1" deprecated: false - kind: "swift:4.1" - default: true + default: false image: name: "action-swift-v4.1" deprecated: false + - kind: "swift:4.2" + default: true + image: + name: "action-swift-v4.2" + deprecated: false blackboxes: - name: "dockerskeleton" diff --git a/core/swift421ActionLoop/swiftbuild.py.launcher.swift b/core/swift421ActionLoop/swiftbuild.py.launcher.swift deleted file mode 100644 index de649ab..0000000 --- a/core/swift421ActionLoop/swiftbuild.py.launcher.swift +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Imports -import Foundation -#if os(Linux) - import Glibc -#else - import Darwin -#endif - -func _whisk_print_error(message: String, error: Error?){ - var errStr = "{\"error\":\"\(message)\"}\n" - if let error = error { - errStr = "{\"error\":\"\(message) \(error.localizedDescription)\"\n}" - } - let buf : [UInt8] = Array(errStr.utf8) - write(3, buf, buf.count) -} - -// snippet of code "injected" (wrapper code for invoking traditional main) -func _run_main(mainFunction: ([String: Any]) -> [String: Any]) -> Void { - while let inputStr: String = readLine() { - do { - let json = inputStr.data(using: .utf8, allowLossyConversion: true)! - let parsed = try JSONSerialization.jsonObject(with: json, options: []) as! [String: Any] - // TODO put the values in the env - let value = parsed["value"] as! [String: Any] - let result = mainFunction(value) - if JSONSerialization.isValidJSONObject(result) { - do { - let jsonData = try JSONSerialization.data(withJSONObject: result, options: []) - var json = [UInt8](jsonData) - json.append(10) - write(3, json, json.count) - fflush(stdout) - fflush(stderr) - } catch { - _whisk_print_error(message: "Failed to encode Dictionary type to JSON string:", error: error) - } - } else { - _whisk_print_error(message: "Error serializing JSON, data does not appear to be valid JSON", error: nil) - } - } catch { - _whisk_print_error(message: "Failed to execute action handler with error:", error: error) - return - } - } -} - -// snippets of code "injected", dependending on the type of function the developer -// wants to use traditional vs codable - diff --git a/core/swift421ActionLoop/Dockerfile b/core/swift42Action/Dockerfile similarity index 75% rename from core/swift421ActionLoop/Dockerfile rename to core/swift42Action/Dockerfile index 506da76..e3e018b 100644 --- a/core/swift421ActionLoop/Dockerfile +++ b/core/swift42Action/Dockerfile @@ -16,16 +16,16 @@ # FROM openwhisk/actionloop as builder -FROM swift:4.2.1 +FROM swift:4.2 RUN rm -rf /var/lib/apt/lists/* && apt-get clean && apt-get update \ - && apt-get install -y --no-install-recommends locales python3 \ + && apt-get install -y --no-install-recommends locales python3 vim \ && rm -rf /var/lib/apt/lists/* \ && locale-gen en_US.UTF-8 ENV LANG="en_US.UTF-8" \ LANGUAGE="en_US:en" \ - LC_ALL="en_US.UTF-8" + LC_ALL="en_US.UTF-8" RUN mkdir -p /swiftAction WORKDIR /swiftAction @@ -34,6 +34,15 @@ WORKDIR /swiftAction COPY --from=builder /bin/proxy /bin/proxy ADD swiftbuild.py /bin/compile ADD swiftbuild.py.launcher.swift /bin/compile.launcher.swift +COPY _Whisk.swift /swiftAction/Sources/ +COPY Package.swift /swiftAction/ +COPY buildandrecord.py /swiftAction/ +COPY main.swift /swiftAction/Sources/ +RUN swift build -c release; \ +touch /swiftAction/Sources/main.swift; \ +rm /swiftAction/.build/release/Action; \ +/swiftAction/buildandrecord.py + ENV OW_COMPILER=/bin/compile ENTRYPOINT [ "/bin/proxy" ] diff --git a/tests/src/test/scala/runtime/actionContainers/Swift41CodableActionContainerTests.scala b/core/swift42Action/Package.swift similarity index 67% copy from tests/src/test/scala/runtime/actionContainers/Swift41CodableActionContainerTests.scala copy to core/swift42Action/Package.swift index cd0a8ba..181e352 100644 --- a/tests/src/test/scala/runtime/actionContainers/Swift41CodableActionContainerTests.scala +++ b/core/swift42Action/Package.swift @@ -1,3 +1,6 @@ +// swift-tools-version:4.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. + /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -15,13 +18,20 @@ * limitations under the License. */ -package runtime.actionContainers - -import org.junit.runner.RunWith -import org.scalatest.junit.JUnitRunner +import PackageDescription -@RunWith(classOf[JUnitRunner]) -class Swift41CodableActionContainerTests extends SwiftCodableActionContainerTests { - override lazy val swiftContainerImageName = "action-swift-v4.1" - override lazy val swiftBinaryName = "tests/dat/build/swift4.1/HelloSwift4Codable.zip" -} +let package = Package( + name: "Action", + products: [ + .executable( + name: "Action", + targets: ["Action"] + ) + ], + targets: [ + .target( + name: "Action", + path: "." + ) + ] +) diff --git a/core/swift421ActionLoop/build.gradle b/core/swift42Action/build.gradle similarity index 75% copy from core/swift421ActionLoop/build.gradle copy to core/swift42Action/build.gradle index d9bb28f..117edc2 100755 --- a/core/swift421ActionLoop/build.gradle +++ b/core/swift42Action/build.gradle @@ -15,5 +15,17 @@ * limitations under the License. */ -ext.dockerImageName = 'actionloop-swift-v4.2.1' +ext.dockerImageName = 'action-swift-v4.2' apply from: '../../gradle/docker.gradle' + +distDocker.dependsOn 'copyWhisk' +distDocker.finalizedBy('cleanup') + +task copyWhisk(type: Copy) { + from '../swift41Action/spm-build/_Whisk.swift' + into '_Whisk.swift' +} + +task cleanup(type: Delete) { + delete '_Whisk.swift' +} diff --git a/core/swift42Action/buildandrecord.py b/core/swift42Action/buildandrecord.py new file mode 100755 index 0000000..bc15f06 --- /dev/null +++ b/core/swift42Action/buildandrecord.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +"""Python to generate build script. + +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +""" +from __future__ import print_function +import os +import sys +from subprocess import check_output + +# Settings +COMPILE_PREFIX = "/usr/bin/swiftc -module-name Action " +LINKER_PREFIX = "/usr/bin/swiftc -target x86_64-unknown-linux -sdk / -L /swiftAction/.build/x86_64-unknown-linux/release -o /swiftAction/.build/x86_64-unknown-linux/release/Action -module-name Action -emit-executable -Xlinker '-rpath=$ORIGIN'" +GENERATED_BUILD_SCRIPT = "/swiftAction/swiftbuildandlink.sh" +SPM_DIRECTORY = "/swiftAction" +BUILD_COMMAND = ["swift", "build", "-v", "-c", "release"] + +# Build Swift package and capture step trace +print("Building action") +out = check_output(BUILD_COMMAND, cwd=SPM_DIRECTORY) +print("action built. Decoding compile and link commands") + +# Look for compile and link commands in step trace +compileCommand = None +linkCommand = None + +buildInstructions = out.decode("utf-8").splitlines() + +for instruction in buildInstructions: + print(instruction) + if instruction.startswith(COMPILE_PREFIX): + compileCommand = instruction + + # add flag to quiet warnings + compileCommand += " -suppress-warnings" + + elif instruction.startswith(LINKER_PREFIX): + linkCommand = instruction + +# if found, create build script, otherwise exit with error +if compileCommand is not None and linkCommand is not None: + print("Generated OpenWhisk Compile command: %s" % compileCommand) + print("=========") + print("Generated OpenWhisk Link command: %s" % linkCommand) + + with open(GENERATED_BUILD_SCRIPT, "w") as buildScript: + buildScript.write("#!/bin/bash\n") + buildScript.write("#date\n") + buildScript.write("#echo \"Compiling\"\n") + buildScript.write("%s\n" % compileCommand) + buildScript.write("swiftStatus=$?\n") + buildScript.write("#echo swiftc status is $swiftStatus\n") + buildScript.write("if [[ \"$swiftStatus\" -eq \"0\" ]]; then\n") + buildScript.write("#date\n") + buildScript.write("#echo \"Linking\"\n") + buildScript.write("%s\n" % linkCommand) + buildScript.write("#date\n") + buildScript.write("else\n") + buildScript.write(">2& echo \"Action did not compile\"\n") + buildScript.write("exit 1\n") + buildScript.write("fi") + + os.chmod(GENERATED_BUILD_SCRIPT, 0o777) + sys.exit(0) +else: + print("Cannot generate build script: compile or link command not found") + sys.exit(1) diff --git a/core/swift421ActionLoop/build.gradle b/core/swift42Action/main.swift old mode 100755 new mode 100644 similarity index 79% rename from core/swift421ActionLoop/build.gradle rename to core/swift42Action/main.swift index d9bb28f..75071a3 --- a/core/swift421ActionLoop/build.gradle +++ b/core/swift42Action/main.swift @@ -15,5 +15,10 @@ * limitations under the License. */ -ext.dockerImageName = 'actionloop-swift-v4.2.1' -apply from: '../../gradle/docker.gradle' +func main(args: [String:Any]) -> [String:Any] { + if let name = args["name"] as? String { + return [ "greeting" : "Hello \(name)!" ] + } else { + return [ "greeting" : "Hello stranger!" ] + } +} diff --git a/core/swift421ActionLoop/swiftbuild.py b/core/swift42Action/swiftbuild.py similarity index 54% rename from core/swift421ActionLoop/swiftbuild.py rename to core/swift42Action/swiftbuild.py index 49ec29d..9b40903 100755 --- a/core/swift421ActionLoop/swiftbuild.py +++ b/core/swift42Action/swiftbuild.py @@ -26,61 +26,35 @@ import codecs import subprocess from io import StringIO -package_swift = """// swift-tools-version:4.2 -import PackageDescription - -let package = Package( - name: "exec", - dependencies: [], - targets: [ - .target( - name: "exec", - dependencies: []) - ] -) -""" -output = StringIO() - def eprint(*args, **kwargs): - print(*args, file=output, **kwargs) + print(*args, file=sys.stderr, **kwargs) def sources(launcher, source_dir, main): - # create Packages.swift - packagefile = "%s/Package.swift" % source_dir - if not os.path.isfile(packagefile): - with codecs.open(packagefile, 'w', 'utf-8') as s: - s.write(package_swift) - - # create Sources/Action dir - actiondir = "%s/Sources/exec" % source_dir - if not os.path.isdir(actiondir): - os.makedirs(actiondir, mode=0o755) - - # copy the exec to exec.go - # also check if it has a main in it - src = "%s/exec" % source_dir - dst = "%s/exec.swift" % actiondir - if os.path.isfile(src): - with codecs.open(src, 'r', 'utf-8') as s: - with codecs.open(dst, 'w', 'utf-8') as d: - body = s.read() - d.write(body) - + actiondir = "%s/Sources" % source_dir # copy the launcher fixing the main dst = "%s/main.swift" % actiondir - with codecs.open(dst, 'w', 'utf-8') as d: + with codecs.open(dst, 'a', 'utf-8') as d: with codecs.open(launcher, 'r', 'utf-8') as e: code = e.read() - code += "_run_main(mainFunction: %s)\n" % main + code += "while let inputStr: String = readLine() {\n" + code += " let json = inputStr.data(using: .utf8, allowLossyConversion: true)!\n" + code += " let parsed = try JSONSerialization.jsonObject(with: json, options: []) as! [String: Any]\n" + code += " for (key, value) in parsed {\n" + code += " if key != \"value\" {\n" + code += " setenv(\"__OW_\\(key.uppercased())\",value as! String,1)\n" + code += " }\n" + code += " }\n" + code += " let jsonData = try JSONSerialization.data(withJSONObject: parsed[\"value\"] as Any, options: [])\n" + code += " _run_main(mainFunction: %s, json: jsonData)\n" % main + code += "} \n" d.write(code) -def swift_build(dir, extra_args=[]): - base_args = ["swift", "build", "--package-path", dir, "-c", "release"] +def swift_build(dir, buildcmd): # compile... env = { "PATH": os.environ["PATH"] } - p = subprocess.Popen(base_args+extra_args, + p = subprocess.Popen(buildcmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=dir, @@ -95,25 +69,20 @@ def swift_build(dir, extra_args=[]): e = e.decode('utf-8') return p.returncode, o, e -def build(source_dir, target_file): - r, o, e = swift_build(source_dir) - if e: eprint(e) - if o: eprint(o) +def build(source_dir, target_file, buildcmd): + r, o, e = swift_build(source_dir, buildcmd) + #if e: print(e) + #if o: print(o) if r != 0: - print(output.getvalue()) + print(e) + print(o) + print(r) return - r, o, e = swift_build(source_dir, ["--show-bin-path"]) - if e: eprint(e) - if r != 0: - print(output.getvalue()) - return - - bin_file = "%s/exec" % o.strip() + bin_file = "%s/.build/release/Action" % source_dir os.rename(bin_file, target_file) if not os.path.isfile(target_file): - eprint("failed %s -> %s" % (bin_file, target_file)) - print(output.getvalue()) + print("failed %s -> %s" % (bin_file, target_file)) return @@ -126,8 +95,25 @@ def main(argv): source_dir = os.path.abspath(argv[2]) target = os.path.abspath("%s/exec" % argv[3]) launch = os.path.abspath(argv[0]+".launcher.swift") - sources(launch, source_dir, main) - build(source_dir, target) + + src = "%s/exec" % source_dir + + #check if single source + if os.path.isfile(src): + actiondir = os.path.abspath("Sources") + if not os.path.isdir(actiondir): + os.makedirs(actiondir, mode=0o755) + dst = "%s/main.swift" % actiondir + os.rename(src, dst) + sources(launch, os.path.abspath("."), main) + build(os.path.abspath("."), target, ["./swiftbuildandlink.sh"]) + else: + actiondir = "%s/Sources" % source_dir + if not os.path.isdir(actiondir): + os.makedirs(actiondir, mode=0o755) + os.rename(os.path.abspath("Sources/_Whisk.swift"),"%s/Sources/_Whisk.swift" % source_dir) + sources(launch, source_dir, main) + build(source_dir, target, ["swift", "build", "-c", "release"]) if __name__ == '__main__': main(sys.argv) diff --git a/core/swift42Action/swiftbuild.py.launcher.swift b/core/swift42Action/swiftbuild.py.launcher.swift new file mode 100644 index 0000000..bf6b65d --- /dev/null +++ b/core/swift42Action/swiftbuild.py.launcher.swift @@ -0,0 +1,133 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Imports +import Foundation +#if os(Linux) + import Glibc +#else + import Darwin +#endif + +func _whisk_print_error(message: String, error: Error?){ + var errStr = "{\"error\":\"\(message)\"}\n" + if let error = error { + errStr = "{\"error\":\"\(message) \(error.localizedDescription)\"\n}" + } + _whisk_print_buffer(jsonString: errStr) +} +func _whisk_print_result(jsonData: Data){ + let jsonString = String(data: jsonData, encoding: .utf8)! + _whisk_print_buffer(jsonString: jsonString) +} +func _whisk_print_buffer(jsonString: String){ + var buf : [UInt8] = Array(jsonString.utf8) + buf.append(10) + write(3, buf, buf.count) + fflush(stdout) + fflush(stderr) +} + +// snippet of code "injected" (wrapper code for invoking traditional main) +func _run_main(mainFunction: ([String: Any]) -> [String: Any], json: Data) -> Void { + do { + let parsed = try JSONSerialization.jsonObject(with: json, options: []) as! [String: Any] + let result = mainFunction(parsed) + if JSONSerialization.isValidJSONObject(result) { + do { + let jsonData = try JSONSerialization.data(withJSONObject: result, options: []) + _whisk_print_result(jsonData: jsonData) + } catch { + _whisk_print_error(message: "Failed to encode Dictionary type to JSON string:", error: error) + } + } else { + _whisk_print_error(message: "Error serializing JSON, data does not appear to be valid JSON", error: nil) + } + } catch { + _whisk_print_error(message: "Failed to execute action handler with error:", error: error) + return + } +} + +// Codable main signature input Codable +func _run_main<In: Decodable, Out: Encodable>(mainFunction: (In, @escaping (Out?, Error?) -> Void) -> Void, json: Data) { + do { + let input = try Whisk.jsonDecoder.decode(In.self, from: json) + let resultHandler = { (out: Out?, error: Error?) in + if let error = error { + _whisk_print_error(message: "Action handler callback returned an error:", error: error) + return + } + guard let out = out else { + _whisk_print_error(message: "Action handler callback did not return response or error.", error: nil) + return + } + do { + let jsonData = try Whisk.jsonEncoder.encode(out) + _whisk_print_result(jsonData: jsonData) + } catch let error as EncodingError { + _whisk_print_error(message: "JSONEncoder failed to encode Codable type to JSON string:", error: error) + return + } catch { + _whisk_print_error(message: "Failed to execute action handler with error:", error: error) + return + } + } + let _ = mainFunction(input, resultHandler) + } catch let error as DecodingError { + _whisk_print_error(message: "JSONDecoder failed to decode JSON string \(String(data: json, encoding: .utf8)!.replacingOccurrences(of: "\"", with: "\\\"")) to Codable type:", error: error) + return + } catch { + _whisk_print_error(message: "Failed to execute action handler with error:", error: error) + return + } +} + +// Codable main signature no input +func _run_main<Out: Encodable>(mainFunction: ( @escaping (Out?, Error?) -> Void) -> Void, json: Data) { + let resultHandler = { (out: Out?, error: Error?) in + if let error = error { + _whisk_print_error(message: "Action handler callback returned an error:", error: error) + return + } + guard let out = out else { + _whisk_print_error(message: "Action handler callback did not return response or error.", error: nil) + return + } + do { + let jsonData = try Whisk.jsonEncoder.encode(out) + _whisk_print_result(jsonData: jsonData) + } catch let error as EncodingError { + _whisk_print_error(message: "JSONEncoder failed to encode Codable type to JSON string:", error: error) + return + } catch { + _whisk_print_error(message: "Failed to execute action handler with error:", error: error) + return + } + } + let _ = mainFunction(resultHandler) +} + +// snippets of code "injected", dependending on the type of function the developer +// wants to use traditional vs codable + + + + + + + diff --git a/examples/swift-main-single/Makefile b/examples/swift-main-single/Makefile index 0545f1b..83950ca 100644 --- a/examples/swift-main-single/Makefile +++ b/examples/swift-main-single/Makefile @@ -1,7 +1,7 @@ -OW_USER?=openwhisk -OW_RUNTIME?=$(OW_USER)/actionloop-swift-v4.2.1 -OW_COMPILER?=$(OW_USER)/actionloop-swift-v4.2.1 -WSK?=wsk +OW_USER?=whisk +OW_RUNTIME?=$(OW_USER)/action-swift-v4.2 +OW_COMPILER?=$(OW_USER)/action-swift-v4.2 +WSK?=wsk -i MAIN=main PACKAGE=test SRC=$(MAIN).swift diff --git a/examples/swift-main-single/main.swift b/examples/swift-main-single/main.swift index 7d8521e..9a7aaf8 100644 --- a/examples/swift-main-single/main.swift +++ b/examples/swift-main-single/main.swift @@ -19,6 +19,35 @@ func main(args: [String:Any]) -> [String:Any] { if let name = args["name"] as? String { return [ "greeting" : "Hello \(name)!" ] } else { - return [ "greeting" : "Hello swif4!" ] + return [ "greeting" : "Hello swif4.2!" ] } } + +func mainenv(args: [String: Any]) -> [String: Any] { + let env = ProcessInfo.processInfo.environment + var a = "???" + var b = "???" + var c = "???" + var d = "???" + var e = "???" + var f = "???" + if let v : String = env["__OW_API_HOST"] { + a = "\(v)" + } + if let v : String = env["__OW_API_KEY"] { + b = "\(v)" + } + if let v : String = env["__OW_NAMESPACE"] { + c = "\(v)" + } + if let v : String = env["__OW_ACTION_NAME"] { + d = "\(v)" + } + if let v : String = env["__OW_ACTIVATION_ID"] { + e = "\(v)" + } + if let v : String = env["__OW_DEADLINE"] { + f = "\(v)" + } + return ["api_host": a, "api_key": b, "namespace": c, "action_name": d, "activation_id": e, "deadline": f] +} diff --git a/tests/src/test/scala/runtime/actionContainers/Swift41CodableActionContainerTests.scala b/examples/swift-main-zip/HelloSwift4/Package.swift similarity index 67% copy from tests/src/test/scala/runtime/actionContainers/Swift41CodableActionContainerTests.scala copy to examples/swift-main-zip/HelloSwift4/Package.swift index cd0a8ba..181e352 100644 --- a/tests/src/test/scala/runtime/actionContainers/Swift41CodableActionContainerTests.scala +++ b/examples/swift-main-zip/HelloSwift4/Package.swift @@ -1,3 +1,6 @@ +// swift-tools-version:4.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. + /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with @@ -15,13 +18,20 @@ * limitations under the License. */ -package runtime.actionContainers - -import org.junit.runner.RunWith -import org.scalatest.junit.JUnitRunner +import PackageDescription -@RunWith(classOf[JUnitRunner]) -class Swift41CodableActionContainerTests extends SwiftCodableActionContainerTests { - override lazy val swiftContainerImageName = "action-swift-v4.1" - override lazy val swiftBinaryName = "tests/dat/build/swift4.1/HelloSwift4Codable.zip" -} +let package = Package( + name: "Action", + products: [ + .executable( + name: "Action", + targets: ["Action"] + ) + ], + targets: [ + .target( + name: "Action", + path: "." + ) + ] +) diff --git a/examples/swift-main-zip/HelloSwift4/Sources/main.swift b/examples/swift-main-zip/HelloSwift4/Sources/main.swift new file mode 100644 index 0000000..2bd6836 --- /dev/null +++ b/examples/swift-main-zip/HelloSwift4/Sources/main.swift @@ -0,0 +1,10 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more contributor +// license agreements; and to You under the Apache License, Version 2.0. + +func main(args: [String:Any]) -> [String:Any] { + if let name = args["name"] as? String { + return [ "greeting" : "Hello \(name)!" ] + } else { + return [ "greeting" : "Hello stranger!" ] + } +} diff --git a/examples/swift-main-zip/Makefile b/examples/swift-main-zip/Makefile new file mode 100644 index 0000000..2f05476 --- /dev/null +++ b/examples/swift-main-zip/Makefile @@ -0,0 +1,39 @@ +OW_USER?=whisk +OW_RUNTIME?=$(OW_USER)/action-swift-v4.2 +OW_COMPILER?=$(OW_USER)/action-swift-v4.2 +WSK?=wsk -i +MAIN=main +PACKAGE=test +SRCS=HelloSwift4/Sources/$(MAIN).swift HelloSwift4/Package.swift +NAME=swift-$(MAIN)-zip +BINZIP=$(MAIN)-bin.zip +SRCZIP=$(MAIN)-src.zip + +deploy: package.done $(BINZIP) + $(WSK) action update $(PACKAGE)/$(NAME) $(BINZIP) --main $(MAIN) --docker $(OW_RUNTIME) + +devel: package.done $(SRCZIP) + $(WSK) action update $(PACKAGE)/$(NAME) $(SRCZIP) --main $(MAIN) --docker $(OW_COMPILER) + +$(BINZIP): $(SRCS) + cd HelloSwift4 ; zip - -r * | docker run -i $(OW_COMPILER) -compile $(MAIN) >../$(BINZIP) + +$(SRCZIP): $(SRCS) + cd HelloSwift4 ; zip ../$(SRCZIP) -r * + +clean: + -$(WSK) action delete $(PACKAGE)/$(NAME) + -rm $(BINZIP) $(SRCZIP) package.done test.json + +test: test.json + $(WSK) action invoke test/$(NAME) -r + $(WSK) action invoke test/$(NAME) -P test.json -r + +test.json: + echo '{ "name": "Mike" }' >test.json + +package.done: + $(WSK) package update $(PACKAGE) + touch package.done + +.PHONY: deploy devel test clean \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 4627e15..7b18e3c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -21,7 +21,7 @@ include 'core:swift3.1.1Action' include 'core:swift41Action' -include 'core:swift421ActionLoop' +include 'core:swift42Action' rootProject.name = 'runtime-swift' diff --git a/tests/dat/actions/Makefile b/tests/dat/actions/Makefile new file mode 100644 index 0000000..2469e53 --- /dev/null +++ b/tests/dat/actions/Makefile @@ -0,0 +1,30 @@ +OW_USER?=whisk +OW_COMPILER?=$(OW_USER)/action-swift-v4.2 +OUT?=../../build/swift4.2 +define Build + cd $(1); \ + docker run -i $(OW_COMPILER) -compile main <./Sources/main.swift >$(OUT)/$(1).zip +endef + +define BuildWithLib + cd $(1); \ + sed -i.bak 's/4.0/4.2/' Package.swift; \ + zip - -r Package.swift Sources/main.swift | docker run -i $(OW_COMPILER) -compile main >$(OUT)/$(1).zip; \ + mv Package.swift.bak Package.swift +endef + +Hello: + $(call Build,HelloSwift4) + +HelloCodable: + $(call Build,HelloSwift4Codable) + +Swifty: + $(call BuildWithLib,SwiftyRequest) + +SwiftyCodable: + $(call BuildWithLib,SwiftyRequestCodable) + +all: Hello HelloCodable Swifty SwiftyCodable + +.PHONY: Hello HelloCodable Swifty SwiftyCodable \ No newline at end of file diff --git a/tests/dat/build.sh b/tests/dat/build.sh index 3df8598..9511cbc 100755 --- a/tests/dat/build.sh +++ b/tests/dat/build.sh @@ -10,3 +10,6 @@ set -e ../../tools/build/compile.sh SwiftyRequest swift:4.1 "-v" ../../tools/build/compile.sh SwiftyRequestCodable swift:4.1 "-v" ../../tools/build/compile.sh HelloSwift4Codable swift:4.1 "-v" + +cd actions +make all diff --git a/tests/dat/build/swift4.2/HelloSwift4.zip b/tests/dat/build/swift4.2/HelloSwift4.zip new file mode 100644 index 0000000..9d4b85d Binary files /dev/null and b/tests/dat/build/swift4.2/HelloSwift4.zip differ diff --git a/tests/dat/build/swift4.2/HelloSwift4Codable.zip b/tests/dat/build/swift4.2/HelloSwift4Codable.zip new file mode 100644 index 0000000..2a26962 Binary files /dev/null and b/tests/dat/build/swift4.2/HelloSwift4Codable.zip differ diff --git a/tests/dat/build/swift4.2/SwiftyRequest.zip b/tests/dat/build/swift4.2/SwiftyRequest.zip new file mode 100644 index 0000000..c57adc3 Binary files /dev/null and b/tests/dat/build/swift4.2/SwiftyRequest.zip differ diff --git a/tests/dat/build/swift4.2/SwiftyRequestCodable.zip b/tests/dat/build/swift4.2/SwiftyRequestCodable.zip new file mode 100644 index 0000000..ca0568b Binary files /dev/null and b/tests/dat/build/swift4.2/SwiftyRequestCodable.zip differ diff --git a/tests/src/test/scala/runtime/actionContainers/Swift311ActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/Swift311ActionContainerTests.scala index cf8ede6..d23d383 100644 --- a/tests/src/test/scala/runtime/actionContainers/Swift311ActionContainerTests.scala +++ b/tests/src/test/scala/runtime/actionContainers/Swift311ActionContainerTests.scala @@ -179,4 +179,58 @@ class Swift311ActionContainerTests extends SwiftActionContainerTests { e shouldBe empty }) } + + // TODO + // skip for swift 4.2, it responds with 502 on init, stderr doesn't contain compile errors + // compile errors are contained in result + it should "log compilation errors" in { + val (out, err) = withActionContainer() { c => + val code = """ + | 10 PRINT "Hello!" + | 20 GOTO 10 + """.stripMargin + + val (initCode, _) = c.init(initPayload(code)) + initCode should not be (200) + } + + checkStreams(out, err, { + case (o, e) => + if (enforceEmptyOutputStream) o shouldBe empty + e.toLowerCase should include("error") + }) + } + + // TODO + // swift 4.2 exceptions executable exiting doesn't return error from web proxy or ends container + // the action times out + it should "return some error on action error" in { + val (out, err) = withActionContainer() { c => + val code = """ + | // You need an indirection, or swiftc detects the div/0 + | // at compile-time. Smart. + | func div(x: Int, y: Int) -> Int { + | return x/y + | } + | func main(args: [String: Any]) -> [String: Any] { + | return [ "divBy0": div(x:5, y:0) ] + | } + """.stripMargin + + val (initCode, _) = c.init(initPayload(code)) + initCode should be(200) + + val (runCode, runRes) = c.run(runPayload(JsObject())) + runCode should be(502) + + runRes shouldBe defined + runRes.get.fields.get("error") shouldBe defined + } + + checkStreams(out, err, { + case (o, e) => + if (enforceEmptyOutputStream) o shouldBe empty + if (enforceEmptyOutputStream) e shouldBe empty + }) + } } diff --git a/tests/src/test/scala/runtime/actionContainers/Swift41ActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/Swift41ActionContainerTests.scala index d96db53..ee4f6bd 100644 --- a/tests/src/test/scala/runtime/actionContainers/Swift41ActionContainerTests.scala +++ b/tests/src/test/scala/runtime/actionContainers/Swift41ActionContainerTests.scala @@ -115,4 +115,58 @@ class Swift41ActionContainerTests extends SwiftActionContainerTests { e shouldBe empty }) } + + // TODO + // skip for swift 4.2, it responds with 502 on init, stderr doesn't contain compile errors + // compile errors are contained in result + it should "log compilation errors" in { + val (out, err) = withActionContainer() { c => + val code = """ + | 10 PRINT "Hello!" + | 20 GOTO 10 + """.stripMargin + + val (initCode, _) = c.init(initPayload(code)) + initCode should not be (200) + } + + checkStreams(out, err, { + case (o, e) => + if (enforceEmptyOutputStream) o shouldBe empty + e.toLowerCase should include("error") + }) + } + + // TODO + // swift 4.2 exceptions executable exiting doesn't return error from web proxy or ends container + // the action times out + it should "return some error on action error" in { + val (out, err) = withActionContainer() { c => + val code = """ + | // You need an indirection, or swiftc detects the div/0 + | // at compile-time. Smart. + | func div(x: Int, y: Int) -> Int { + | return x/y + | } + | func main(args: [String: Any]) -> [String: Any] { + | return [ "divBy0": div(x:5, y:0) ] + | } + """.stripMargin + + val (initCode, _) = c.init(initPayload(code)) + initCode should be(200) + + val (runCode, runRes) = c.run(runPayload(JsObject())) + runCode should be(502) + + runRes shouldBe defined + runRes.get.fields.get("error") shouldBe defined + } + + checkStreams(out, err, { + case (o, e) => + if (enforceEmptyOutputStream) o shouldBe empty + if (enforceEmptyOutputStream) e shouldBe empty + }) + } } diff --git a/tests/src/test/scala/runtime/actionContainers/Swift41CodableActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/Swift41CodableActionContainerTests.scala index cd0a8ba..618f704 100644 --- a/tests/src/test/scala/runtime/actionContainers/Swift41CodableActionContainerTests.scala +++ b/tests/src/test/scala/runtime/actionContainers/Swift41CodableActionContainerTests.scala @@ -19,9 +19,46 @@ package runtime.actionContainers import org.junit.runner.RunWith import org.scalatest.junit.JUnitRunner +import spray.json.JsObject @RunWith(classOf[JUnitRunner]) class Swift41CodableActionContainerTests extends SwiftCodableActionContainerTests { override lazy val swiftContainerImageName = "action-swift-v4.1" override lazy val swiftBinaryName = "tests/dat/build/swift4.1/HelloSwift4Codable.zip" + + // TODO + // swift 4.2 exceptions executable exiting doesn't return error from web proxy or ends container + // the action times out + it should "return some error on action error" in { + val (out, err) = withActionContainer() { c => + val code = """ + | // You need an indirection, or swiftc detects the div/0 + | // at compile-time. Smart. + | func div(x: Int, y: Int) -> Int { + | return x/y + | } + | struct Result: Codable{ + | let divBy0: Int? + | } + | func main(respondWith: (Result?, Error?) -> Void) -> Void { + | respondWith(Result(divBy0: div(x:5, y:0)), nil) + | } + """.stripMargin + + val (initCode, _) = c.init(initPayload(code)) + initCode should be(200) + + val (runCode, runRes) = c.run(runPayload(JsObject())) + runCode should be(502) + + runRes shouldBe defined + runRes.get.fields.get("error") shouldBe defined + } + + checkStreams(out, err, { + case (o, e) => + if (enforceEmptyOutputStream) o shouldBe empty + e should not be empty + }) + } } diff --git a/tests/src/test/scala/runtime/actionContainers/Swift42ActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/Swift42ActionContainerTests.scala new file mode 100644 index 0000000..5a3c395 --- /dev/null +++ b/tests/src/test/scala/runtime/actionContainers/Swift42ActionContainerTests.scala @@ -0,0 +1,118 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package runtime.actionContainers + +import java.io.File + +import actionContainers.ResourceHelpers +import org.junit.runner.RunWith +import org.scalatest.junit.JUnitRunner +import spray.json.{JsObject, JsString} + +@RunWith(classOf[JUnitRunner]) +class Swift42ActionContainerTests extends SwiftActionContainerTests { + override lazy val swiftContainerImageName = "action-swift-v4.2" + override lazy val swiftBinaryName = "tests/dat/build/swift4.2/HelloSwift4.zip" + lazy val partyCompile = "tests/dat/build/swift4.2/SwiftyRequest.zip" + lazy val partyCompileCodable = "tests/dat/build/swift4.2/SwiftyRequestCodable.zip" + + val httpCode = """ + | import Dispatch + | func main(args:[String: Any]) -> [String:Any] { + | var resp :[String:Any] = ["error":"getUrl failed"] + | guard let urlStr = args["getUrl"] as? String else { + | return ["error":"getUrl not found in action input"] + | } + | guard let url = URL(string: urlStr) else { + | return ["error":"invalid url string \(urlStr)"] + | } + | let request = URLRequest(url: url) + | let session = URLSession(configuration: .default) + | let semaphore = DispatchSemaphore(value: 0) + | let task = session.dataTask(with: request, completionHandler: {data, response, error -> Void in + | print("done with http request") + | if let error = error { + | print("There was an error \(error)") + | } else if let data = data, + | let response = response as? HTTPURLResponse, + | response.statusCode == 200 { + | do { + | let respJson = try JSONSerialization.jsonObject(with: data) + | if respJson is [String:Any] { + | resp = respJson as! [String:Any] + | } else { + | resp = ["error":"Response from server is not a dictionary"] + | } + | } catch { + | resp = ["error":"Error creating json from response: \(error)"] + | } + | } + | semaphore.signal() + | }) + | task.resume() + | _ = semaphore.wait(timeout: .distantFuture) + | return resp + | } + """.stripMargin + + it should "support ability to use 3rd party packages like SwiftyRequest" in { + val zip = new File(partyCompile).toPath + val code = ResourceHelpers.readAsBase64(zip) + + val (out, err) = withActionContainer() { c => + val (initCode, initRes) = c.init(initPayload(code)) + initCode should be(200) + + val args = JsObject("message" -> (JsString("serverless"))) + val (runCode, runRes) = c.run(runPayload(args)) + + runCode should be(200) + val json = runRes.get.fields.get("json") + json shouldBe Some(args) + } + + checkStreams(out, err, { + case (o, e) => + if (enforceEmptyOutputStream) o shouldBe empty + e shouldBe empty + }) + } + + it should "support ability to use escaping completion in Codable" in { + val zip = new File(partyCompileCodable).toPath + val code = ResourceHelpers.readAsBase64(zip) + + val (out, err) = withActionContainer() { c => + val (initCode, initRes) = c.init(initPayload(code, main = "mainCodable")) + initCode should be(200) + + val (runCode, runRes) = c.run(runPayload(JsObject())) + + runCode should be(200) + runRes.get.fields.get("greeting") shouldBe Some(JsString("success")) + + } + + checkStreams(out, err, { + case (o, e) => + if (enforceEmptyOutputStream) o shouldBe empty + e shouldBe empty + }) + } + +} diff --git a/tests/src/test/scala/runtime/actionContainers/Swift41CodableActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/Swift42CodableActionContainerTests.scala similarity index 83% copy from tests/src/test/scala/runtime/actionContainers/Swift41CodableActionContainerTests.scala copy to tests/src/test/scala/runtime/actionContainers/Swift42CodableActionContainerTests.scala index cd0a8ba..0e721fc 100644 --- a/tests/src/test/scala/runtime/actionContainers/Swift41CodableActionContainerTests.scala +++ b/tests/src/test/scala/runtime/actionContainers/Swift42CodableActionContainerTests.scala @@ -21,7 +21,7 @@ import org.junit.runner.RunWith import org.scalatest.junit.JUnitRunner @RunWith(classOf[JUnitRunner]) -class Swift41CodableActionContainerTests extends SwiftCodableActionContainerTests { - override lazy val swiftContainerImageName = "action-swift-v4.1" - override lazy val swiftBinaryName = "tests/dat/build/swift4.1/HelloSwift4Codable.zip" +class Swift42CodableActionContainerTests extends SwiftCodableActionContainerTests { + override lazy val swiftContainerImageName = "action-swift-v4.2" + override lazy val swiftBinaryName = "tests/dat/build/swift4.2/HelloSwift4Codable.zip" } diff --git a/tests/src/test/scala/runtime/actionContainers/SwiftActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/SwiftActionContainerTests.scala index ecc5f41..1041af0 100644 --- a/tests/src/test/scala/runtime/actionContainers/SwiftActionContainerTests.scala +++ b/tests/src/test/scala/runtime/actionContainers/SwiftActionContainerTests.scala @@ -144,54 +144,6 @@ abstract class SwiftActionContainerTests extends BasicActionRunnerTests with Wsk """.stripMargin) } - it should "return some error on action error" in { - val (out, err) = withActionContainer() { c => - val code = """ - | // You need an indirection, or swiftc detects the div/0 - | // at compile-time. Smart. - | func div(x: Int, y: Int) -> Int { - | return x/y - | } - | func main(args: [String: Any]) -> [String: Any] { - | return [ "divBy0": div(x:5, y:0) ] - | } - """.stripMargin - - val (initCode, _) = c.init(initPayload(code)) - initCode should be(200) - - val (runCode, runRes) = c.run(runPayload(JsObject())) - runCode should be(502) - - runRes shouldBe defined - runRes.get.fields.get("error") shouldBe defined - } - - checkStreams(out, err, { - case (o, e) => - if (enforceEmptyOutputStream) o shouldBe empty - if (enforceEmptyOutputStream) e shouldBe empty - }) - } - - it should "log compilation errors" in { - val (out, err) = withActionContainer() { c => - val code = """ - | 10 PRINT "Hello!" - | 20 GOTO 10 - """.stripMargin - - val (initCode, _) = c.init(initPayload(code)) - initCode should not be (200) - } - - checkStreams(out, err, { - case (o, e) => - if (enforceEmptyOutputStream) o shouldBe empty - e.toLowerCase should include("error") - }) - } - it should "support application errors" in { val (out, err) = withActionContainer() { c => val code = """ diff --git a/tests/src/test/scala/runtime/actionContainers/SwiftCodableActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/SwiftCodableActionContainerTests.scala index a2cb482..d015949 100644 --- a/tests/src/test/scala/runtime/actionContainers/SwiftCodableActionContainerTests.scala +++ b/tests/src/test/scala/runtime/actionContainers/SwiftCodableActionContainerTests.scala @@ -200,39 +200,6 @@ abstract class SwiftCodableActionContainerTests extends BasicActionRunnerTests w }) } - it should "return some error on action error" in { - val (out, err) = withActionContainer() { c => - val code = """ - | // You need an indirection, or swiftc detects the div/0 - | // at compile-time. Smart. - | func div(x: Int, y: Int) -> Int { - | return x/y - | } - | struct Result: Codable{ - | let divBy0: Int? - | } - | func main(respondWith: (Result?, Error?) -> Void) -> Void { - | respondWith(Result(divBy0: div(x:5, y:0)), nil) - | } - """.stripMargin - - val (initCode, _) = c.init(initPayload(code)) - initCode should be(200) - - val (runCode, runRes) = c.run(runPayload(JsObject())) - runCode should be(502) - - runRes shouldBe defined - runRes.get.fields.get("error") shouldBe defined - } - - checkStreams(out, err, { - case (o, e) => - if (enforceEmptyOutputStream) o shouldBe empty - e should not be empty - }) - } - it should "support application errors" in { val (out, err) = withActionContainer() { c => val code = """ diff --git a/examples/swift-main-single/main.swift b/tests/src/test/scala/runtime/sdk/Swift42SDKTests.scala similarity index 78% copy from examples/swift-main-single/main.swift copy to tests/src/test/scala/runtime/sdk/Swift42SDKTests.scala index 7d8521e..06402ee 100644 --- a/examples/swift-main-single/main.swift +++ b/tests/src/test/scala/runtime/sdk/Swift42SDKTests.scala @@ -15,10 +15,12 @@ * limitations under the License. */ -func main(args: [String:Any]) -> [String:Any] { - if let name = args["name"] as? String { - return [ "greeting" : "Hello \(name)!" ] - } else { - return [ "greeting" : "Hello swif4!" ] - } +package runtime.sdk + +import org.junit.runner.RunWith +import org.scalatest.junit.JUnitRunner + +@RunWith(classOf[JUnitRunner]) +class Swift42SDKTests extends SwiftSDKTests { + override lazy val actionKind = "swift:4.2" } diff --git a/tools/travis/build.sh b/tools/travis/build.sh index b8ac9ad..b8a18c4 100755 --- a/tools/travis/build.sh +++ b/tools/travis/build.sh @@ -46,8 +46,14 @@ docker tag openwhisk/nodejs6action nodejs6action TERM=dumb ./gradlew install +# install new version docker +curl -fsSL https://get.docker.com -o get-docker.sh +sudo sh get-docker.sh +docker version + # Build runtime cd $ROOTDIR TERM=dumb ./gradlew \ :core:swift41Action:distDocker \ +:core:swift42Action:distDocker \ -PdockerImagePrefix=${IMAGE_PREFIX} diff --git a/tools/travis/publish.sh b/tools/travis/publish.sh index 42c2c34..dc25183 100755 --- a/tools/travis/publish.sh +++ b/tools/travis/publish.sh @@ -34,6 +34,8 @@ if [ ${RUNTIME_VERSION} == "3.1.1" ]; then RUNTIME="swift3.1.1Action" elif [ ${RUNTIME_VERSION} == "4.1" ]; then RUNTIME="swift41Action" +elif [ ${RUNTIME_VERSION} == "4.2" ]; then + RUNTIME="swift42Action" fi if [[ ! -z ${DOCKER_USER} ]] && [[ ! -z ${DOCKER_PASSWORD} ]]; then @@ -46,4 +48,15 @@ TERM=dumb ./gradlew \ -PdockerRegistry=docker.io \ -PdockerImagePrefix=${IMAGE_PREFIX} \ -PdockerImageTag=${IMAGE_TAG} + + # if doing latest also push a tag with the hash commit + if [ ${IMAGE_TAG} == "latest" ]; then + SHORT_COMMIT=`git rev-parse --short HEAD` + TERM=dumb ./gradlew \ + :core:${RUNTIME}:distDocker \ + -PdockerRegistry=docker.io \ + -PdockerImagePrefix=${IMAGE_PREFIX} \ + -PdockerImageTag=${SHORT_COMMIT} + fi + fi
