You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by ra...@apache.org on 2020/10/30 20:35:05 UTC
[openwhisk-runtime-swift] branch master updated: add support for
Swift 5.3 (#124)
This is an automated email from the ASF dual-hosted git repository.
rabbah pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openwhisk-runtime-swift.git
The following commit(s) were added to refs/heads/master by this push:
new 5025af1 add support for Swift 5.3 (#124)
5025af1 is described below
commit 5025af15b89139e5c0a12b47897e1db6a9406117
Author: David Grove <dg...@users.noreply.github.com>
AuthorDate: Fri Oct 30 16:34:55 2020 -0400
add support for Swift 5.3 (#124)
1. add support for Swift 5.3 by replicating Swift 5.1
2. Cleanup vestigial Swift 3 testing artifacts
---
.travis.yml | 2 +-
ansible/environments/local/group_vars/all | 5 +
.../build.sh => core/swift53Action/CHANGELOG.md | 22 +--
core/swift53Action/Dockerfile | 62 ++++++++
.../swift53Action}/Package.swift | 19 ++-
core/swift53Action/_Whisk.swift | 167 +++++++++++++++++++++
.../swift53Action/build.gradle | 9 +-
.../Sources => core/swift53Action}/main.swift | 0
core/swift53Action/swiftbuild.py | 119 +++++++++++++++
core/swift53Action/swiftbuild.py.launcher.swift | 133 ++++++++++++++++
.../swift53Action/swiftbuildandlink.sh | 21 +--
settings.gradle | 2 +
tests/dat/actions/sdk/swift3/createRule.swift | 30 ----
tests/dat/actions/sdk/swift3/invoke.swift | 33 ----
.../dat/actions/sdk/swift3/invokeNonBlocking.swift | 33 ----
tests/dat/build.sh | 7 +-
tests/dat/build/swift5.3/HelloSwift5.zip | Bin 0 -> 23934 bytes
tests/dat/build/swift5.3/HelloSwift5Codable.zip | Bin 0 -> 36717 bytes
tests/dat/build/swift5.3/SwiftyRequest5.zip | Bin 0 -> 6607555 bytes
tests/dat/build/swift5.3/SwiftyRequestCodable5.zip | Bin 0 -> 6617585 bytes
.../Swift53ActionContainerTests.scala | 121 +++++++++++++++
.../Swift53CodableActionContainerTests.scala} | 16 +-
.../test/scala/runtime/sdk/Swift53SDKTests.scala} | 17 +--
tools/travis/build.sh | 1 +
tools/travis/publish.sh | 2 +
25 files changed, 657 insertions(+), 164 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 4f46b6b..b63fb38 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -47,7 +47,7 @@ deploy:
branch: master
repo: apache/openwhisk-runtime-swift
- provider: script
- script: "./tools/travis/publish.sh openwhisk 5.1 nightly"
+ script: "./tools/travis/publish.sh openwhisk 5.1 nightly && ./tools/travis/publish.sh openwhisk 5.3 nightly"
on:
branch: master
repo: apache/openwhisk-runtime-swift
diff --git a/ansible/environments/local/group_vars/all b/ansible/environments/local/group_vars/all
index e071694..fa613d9 100644
--- a/ansible/environments/local/group_vars/all
+++ b/ansible/environments/local/group_vars/all
@@ -69,6 +69,11 @@ runtimes_manifest:
image:
name: "action-swift-v5.1"
deprecated: false
+ - kind: "swift:5.3"
+ default: false
+ image:
+ name: "action-swift-v5.3"
+ deprecated: false
blackboxes:
- name: "dockerskeleton"
diff --git a/tests/dat/build.sh b/core/swift53Action/CHANGELOG.md
old mode 100755
new mode 100644
similarity index 54%
copy from tests/dat/build.sh
copy to core/swift53Action/CHANGELOG.md
index f016d00..932bd52
--- a/tests/dat/build.sh
+++ b/core/swift53Action/CHANGELOG.md
@@ -1,4 +1,4 @@
-#!/bin/bash
+<!--
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
@@ -15,21 +15,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+-->
-set -e
+# Apache OpenWhisk Swift 5.3 Runtime Container
-../../tools/build/compile.sh HelloSwift3 swift:3.1.1 "-v"
-
-../../tools/build/compile.sh HelloSwift4 swift:4.1 "-v"
-../../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"
-
-../../tools/build/compile5.sh action-swift-v5.1 HelloSwift5 swift5.1 "-v"
-../../tools/build/compile5.sh action-swift-v5.1 HelloSwift5Codable swift5.1 "-v"
-../../tools/build/compile5.sh action-swift-v5.1 SwiftyRequest5 swift:5.1 "-v"
-../../tools/build/compile5.sh action-swift-v5.1 SwiftyRequestCodable5 swift:5.1 "-v"
-
-
-cd actions
-make all
+## NEXT RELEASE
+ - Initial Release
diff --git a/core/swift53Action/Dockerfile b/core/swift53Action/Dockerfile
new file mode 100644
index 0000000..b7437d5
--- /dev/null
+++ b/core/swift53Action/Dockerfile
@@ -0,0 +1,62 @@
+#
+# 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.
+#
+
+# build go proxy from source
+FROM golang:1.15 AS builder_source
+RUN env CGO_ENABLED=0 go get github.com/apache/openwhisk-runtime-go/main && mv /go/bin/main /bin/proxy
+
+# or build it from a release
+FROM golang:1.15 AS builder_release
+ARG GO_PROXY_RELEASE_VERSION=1.15@1.16.0
+RUN curl -sL \
+ https://github.com/apache/openwhisk-runtime-go/archive/{$GO_PROXY_RELEASE_VERSION}.tar.gz\
+ | tar xzf -\
+ && cd openwhisk-runtime-go-*/main\
+ && GO111MODULE=on go build -o /bin/proxy
+
+FROM swift:5.3
+
+# select the builder to use
+ARG GO_PROXY_BUILD_FROM=release
+
+RUN rm -rf /var/lib/apt/lists/* && apt-get clean && apt-get -qq update \
+ && apt-get install -y --no-install-recommends locales python3 vim libssl-dev libicu-dev \
+ && 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"
+
+RUN mkdir -p /swiftAction
+WORKDIR /swiftAction
+
+COPY --from=builder_source /bin/proxy /bin/proxy_source
+COPY --from=builder_release /bin/proxy /bin/proxy_release
+RUN mv /bin/proxy_${GO_PROXY_BUILD_FROM} /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 swiftbuildandlink.sh /swiftAction/
+COPY main.swift /swiftAction/Sources/
+RUN swift build -c release; \
+ touch /swiftAction/Sources/main.swift; \
+ rm /swiftAction/.build/release/Action
+
+ENV OW_COMPILER=/bin/compile
+ENTRYPOINT [ "/bin/proxy" ]
diff --git a/tests/dat/actions/HelloSwift3/Package.swift b/core/swift53Action/Package.swift
similarity index 75%
rename from tests/dat/actions/HelloSwift3/Package.swift
rename to core/swift53Action/Package.swift
index e2437ec..181e352 100644
--- a/tests/dat/actions/HelloSwift3/Package.swift
+++ b/core/swift53Action/Package.swift
@@ -1,4 +1,4 @@
-// swift-tools-version:3.1.0
+// swift-tools-version:4.2
// The swift-tools-version declares the minimum version of Swift required to build this package.
/*
@@ -22,9 +22,16 @@ import PackageDescription
let package = Package(
name: "Action",
- dependencies: [
- .Package(url: "https://github.com/IBM-Swift/CCurl.git", "0.2.3"),
- .Package(url: "https://github.com/IBM-Swift/Kitura-net.git", "1.7.10"),
- .Package(url: "https://github.com/IBM-Swift/SwiftyJSON.git", "15.0.1")
- ]
+ products: [
+ .executable(
+ name: "Action",
+ targets: ["Action"]
+ )
+ ],
+ targets: [
+ .target(
+ name: "Action",
+ path: "."
+ )
+ ]
)
diff --git a/core/swift53Action/_Whisk.swift b/core/swift53Action/_Whisk.swift
new file mode 100644
index 0000000..3f18e9d
--- /dev/null
+++ b/core/swift53Action/_Whisk.swift
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+import Foundation
+import Dispatch
+#if canImport(FoundationNetworking)
+import FoundationNetworking
+#endif
+
+class Whisk {
+
+ static var baseUrl = ProcessInfo.processInfo.environment["__OW_API_HOST"]
+ static var apiKey = ProcessInfo.processInfo.environment["__OW_API_KEY"]
+ // This will allow user to modify the default JSONDecoder and JSONEncoder used by epilogue
+ static var jsonDecoder = JSONDecoder()
+ static var jsonEncoder = JSONEncoder()
+
+ class func invoke(actionNamed action : String, withParameters params : [String:Any], blocking: Bool = true) -> [String:Any] {
+ let parsedAction = parseQualifiedName(name: action)
+ let strBlocking = blocking ? "true" : "false"
+ let path = "/api/v1/namespaces/\(parsedAction.namespace)/actions/\(parsedAction.name)?blocking=\(strBlocking)"
+
+ return sendWhiskRequestSyncronish(uriPath: path, params: params, method: "POST")
+ }
+
+ class func trigger(eventNamed event : String, withParameters params : [String:Any]) -> [String:Any] {
+ let parsedEvent = parseQualifiedName(name: event)
+ let path = "/api/v1/namespaces/\(parsedEvent.namespace)/triggers/\(parsedEvent.name)?blocking=true"
+
+ return sendWhiskRequestSyncronish(uriPath: path, params: params, method: "POST")
+ }
+
+ class func createTrigger(triggerNamed trigger: String, withParameters params : [String:Any]) -> [String:Any] {
+ let parsedTrigger = parseQualifiedName(name: trigger)
+ let path = "/api/v1/namespaces/\(parsedTrigger.namespace)/triggers/\(parsedTrigger.name)"
+ return sendWhiskRequestSyncronish(uriPath: path, params: params, method: "PUT")
+ }
+
+ class func createRule(ruleNamed ruleName: String, withTrigger triggerName: String, andAction actionName: String) -> [String:Any] {
+ let parsedRule = parseQualifiedName(name: ruleName)
+ let path = "/api/v1/namespaces/\(parsedRule.namespace)/rules/\(parsedRule.name)"
+ let params = ["trigger":triggerName, "action":actionName]
+ return sendWhiskRequestSyncronish(uriPath: path, params: params, method: "PUT")
+ }
+
+ // handle the GCD dance to make the post async, but then obtain/return
+ // the result from this function sync
+ private class func sendWhiskRequestSyncronish(uriPath path: String, params : [String:Any], method: String) -> [String:Any] {
+ var response : [String:Any]!
+
+ let queue = DispatchQueue.global()
+ let invokeGroup = DispatchGroup()
+
+ invokeGroup.enter()
+ queue.async {
+ postUrlSession(uriPath: path, params: params, method: method, group: invokeGroup) { result in
+ response = result
+ }
+ }
+
+ // On one hand, FOREVER seems like an awfully long time...
+ // But on the other hand, I think we can rely on the system to kill this
+ // if it exceeds a reasonable execution time.
+ switch invokeGroup.wait(timeout: DispatchTime.distantFuture) {
+ case DispatchTimeoutResult.success:
+ break
+ case DispatchTimeoutResult.timedOut:
+ break
+ }
+
+ return response
+ }
+
+
+ /**
+ * Using new UrlSession
+ */
+ private class func postUrlSession(uriPath: String, params : [String:Any], method: String,group: DispatchGroup, callback : @escaping([String:Any]) -> Void) {
+
+ guard let encodedPath = uriPath.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) else {
+ callback(["error": "Error encoding uri path to make openwhisk REST call."])
+ return
+ }
+
+ let urlStr = "\(baseUrl!)\(encodedPath)"
+ if let url = URL(string: urlStr) {
+ var request = URLRequest(url: url)
+ request.httpMethod = method
+
+ do {
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
+ request.httpBody = try JSONSerialization.data(withJSONObject: params)
+
+ let loginData: Data = apiKey!.data(using: String.Encoding.utf8, allowLossyConversion: false)!
+ let base64EncodedAuthKey = loginData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
+ request.addValue("Basic \(base64EncodedAuthKey)", forHTTPHeaderField: "Authorization")
+ let session = URLSession(configuration: URLSessionConfiguration.default)
+
+ let task = session.dataTask(with: request, completionHandler: {data, response, error -> Void in
+
+ // exit group after we are done
+ defer {
+ group.leave()
+ }
+
+ if let error = error {
+ callback(["error":error.localizedDescription])
+ } else {
+
+ if let data = data {
+ do {
+ //let outputStr = String(data: data, encoding: String.Encoding.utf8) as String!
+ //print(outputStr)
+ let respJson = try JSONSerialization.jsonObject(with: data)
+ if respJson is [String:Any] {
+ callback(respJson as! [String:Any])
+ } else {
+ callback(["error":" response from server is not a dictionary"])
+ }
+ } catch {
+ callback(["error":"Error creating json from response: \(error)"])
+ }
+ }
+ }
+ })
+
+ task.resume()
+ } catch {
+ callback(["error":"Got error creating params body: \(error)"])
+ }
+ }
+ }
+
+ // separate an OpenWhisk qualified name (e.g. "/whisk.system/samples/date")
+ // into namespace and name components
+ private class func parseQualifiedName(name qualifiedName : String) -> (namespace : String, name : String) {
+ let defaultNamespace = "_"
+ let delimiter = "/"
+
+ let segments :[String] = qualifiedName.components(separatedBy: delimiter)
+
+ if segments.count > 2 {
+ return (segments[1], Array(segments[2..<segments.count]).joined(separator: delimiter))
+ } else if segments.count == 2 {
+ // case "/action" or "package/action"
+ let name = qualifiedName.hasPrefix(delimiter) ? segments[1] : segments.joined(separator: delimiter)
+ return (defaultNamespace, name)
+ } else {
+ return (defaultNamespace, segments[0])
+ }
+ }
+
+}
diff --git a/tests/dat/actions/sdk/swift3/createTrigger.swift b/core/swift53Action/build.gradle
similarity index 72%
rename from tests/dat/actions/sdk/swift3/createTrigger.swift
rename to core/swift53Action/build.gradle
index 5d31971..5a8c378 100644
--- a/tests/dat/actions/sdk/swift3/createTrigger.swift
+++ b/core/swift53Action/build.gradle
@@ -15,10 +15,5 @@
* limitations under the License.
*/
-func main(args: [String:Any]) -> [String:Any] {
- guard let triggerName = args["triggerName"] as? String else {
- return ["error": "You must specify a triggerName parameter!"]
- }
- print("Trigger Name: \(triggerName)")
- return Whisk.createTrigger(triggerNamed: triggerName, withParameters: [:])
-}
+ext.dockerImageName = 'action-swift-v5.3'
+apply from: '../../gradle/docker.gradle'
diff --git a/tests/dat/actions/HelloSwift3/Sources/main.swift b/core/swift53Action/main.swift
similarity index 100%
rename from tests/dat/actions/HelloSwift3/Sources/main.swift
rename to core/swift53Action/main.swift
diff --git a/core/swift53Action/swiftbuild.py b/core/swift53Action/swiftbuild.py
new file mode 100755
index 0000000..9b40903
--- /dev/null
+++ b/core/swift53Action/swiftbuild.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python3
+"""Swift Action Compiler
+#
+# 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 re
+import sys
+import codecs
+import subprocess
+from io import StringIO
+
+def eprint(*args, **kwargs):
+ print(*args, file=sys.stderr, **kwargs)
+
+def sources(launcher, source_dir, main):
+ actiondir = "%s/Sources" % source_dir
+ # copy the launcher fixing the main
+ dst = "%s/main.swift" % actiondir
+ with codecs.open(dst, 'a', 'utf-8') as d:
+ with codecs.open(launcher, 'r', 'utf-8') as e:
+ code = e.read()
+ 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, buildcmd):
+ # compile...
+ env = {
+ "PATH": os.environ["PATH"]
+ }
+ p = subprocess.Popen(buildcmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ cwd=dir,
+ env=env)
+ (o, e) = p.communicate()
+ # stdout/stderr may be either text or bytes, depending on Python
+ # version, so if bytes, decode to text. Note that in Python 2
+ # a string will match both types; so also skip decoding in that case
+ if isinstance(o, bytes) and not isinstance(o, str):
+ o = o.decode('utf-8')
+ if isinstance(e, bytes) and not isinstance(e, str):
+ e = e.decode('utf-8')
+ return p.returncode, o, e
+
+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(e)
+ print(o)
+ print(r)
+ return
+
+ bin_file = "%s/.build/release/Action" % source_dir
+ os.rename(bin_file, target_file)
+ if not os.path.isfile(target_file):
+ print("failed %s -> %s" % (bin_file, target_file))
+ return
+
+
+def main(argv):
+ if len(argv) < 4:
+ print("usage: <main-function> <source-dir> <target-dir>")
+ sys.exit(1)
+
+ main = argv[1]
+ source_dir = os.path.abspath(argv[2])
+ target = os.path.abspath("%s/exec" % argv[3])
+ launch = os.path.abspath(argv[0]+".launcher.swift")
+
+ 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/swift53Action/swiftbuild.py.launcher.swift b/core/swift53Action/swiftbuild.py.launcher.swift
new file mode 100644
index 0000000..a1a2cf0
--- /dev/null
+++ b/core/swift53Action/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)
+ fflush(stdout)
+ fflush(stderr)
+ write(3, buf, buf.count)
+}
+
+// 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/tests/dat/build.sh b/core/swift53Action/swiftbuildandlink.sh
similarity index 53%
copy from tests/dat/build.sh
copy to core/swift53Action/swiftbuildandlink.sh
index f016d00..c35a1ba 100755
--- a/tests/dat/build.sh
+++ b/core/swift53Action/swiftbuildandlink.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/bin/sh
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
@@ -15,21 +15,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-
-set -e
-
-../../tools/build/compile.sh HelloSwift3 swift:3.1.1 "-v"
-
-../../tools/build/compile.sh HelloSwift4 swift:4.1 "-v"
-../../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"
-
-../../tools/build/compile5.sh action-swift-v5.1 HelloSwift5 swift5.1 "-v"
-../../tools/build/compile5.sh action-swift-v5.1 HelloSwift5Codable swift5.1 "-v"
-../../tools/build/compile5.sh action-swift-v5.1 SwiftyRequest5 swift:5.1 "-v"
-../../tools/build/compile5.sh action-swift-v5.1 SwiftyRequestCodable5 swift:5.1 "-v"
-
-
-cd actions
-make all
+swift build -c release
diff --git a/settings.gradle b/settings.gradle
index c3edc42..78a9f66 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -23,6 +23,8 @@ include 'core:swift42Action'
include 'core:swift51Action'
+include 'core:swift53Action'
+
rootProject.name = 'runtime-swift'
gradle.ext.openwhisk = [
diff --git a/tests/dat/actions/sdk/swift3/createRule.swift b/tests/dat/actions/sdk/swift3/createRule.swift
deleted file mode 100644
index a6506d5..0000000
--- a/tests/dat/actions/sdk/swift3/createRule.swift
+++ /dev/null
@@ -1,30 +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.
- */
-
-func main(args: [String:Any]) -> [String:Any] {
- guard let triggerName = args["triggerName"] as? String else {
- return ["error": "You must specify a triggerName parameter!"]
- }
- guard let actionName = args["actionName"] as? String else {
- return ["error": "You must specify a actionName parameter!"]
- }
- guard let ruleName = args["ruleName"] as? String else {
- return ["error": "You must specify a ruleName parameter!"]
- }
- print("Rule Name: \(ruleName), Trigger Name: \(triggerName), actionName: \(actionName)")
- return Whisk.createRule(ruleNamed: ruleName, withTrigger: triggerName, andAction: actionName)
-}
diff --git a/tests/dat/actions/sdk/swift3/invoke.swift b/tests/dat/actions/sdk/swift3/invoke.swift
deleted file mode 100644
index 411157e..0000000
--- a/tests/dat/actions/sdk/swift3/invoke.swift
+++ /dev/null
@@ -1,33 +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.
- */
-
-import SwiftyJSON
-
-func main(args: [String:Any]) -> [String:Any] {
- let invokeResult = Whisk.invoke(actionNamed: "/whisk.system/utils/date", withParameters: [:])
- let dateActivation = JSON(invokeResult)
-
- // the date we are looking for is the result inside the date activation
- if let dateString = dateActivation["response"]["result"]["date"].string {
- print("It is now \(dateString)")
- } else {
- print("Could not parse date of of the response.")
- }
-
- // return the entire invokeResult
- return invokeResult
-}
diff --git a/tests/dat/actions/sdk/swift3/invokeNonBlocking.swift b/tests/dat/actions/sdk/swift3/invokeNonBlocking.swift
deleted file mode 100644
index 9e12d9f..0000000
--- a/tests/dat/actions/sdk/swift3/invokeNonBlocking.swift
+++ /dev/null
@@ -1,33 +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.
- */
-
-import SwiftyJSON
-
-func main(args: [String:Any]) -> [String:Any] {
- let invokeResult = Whisk.invoke(actionNamed: "/whisk.system/utils/date", withParameters: [:], blocking: false)
- let dateActivation = JSON(invokeResult)
-
- // the date we are looking for is the result inside the date activation
- if let activationId = dateActivation["activationId"].string {
- print("Invoked.")
- } else {
- print("Failed to invoke.")
- }
-
- // return the entire invokeResult
- return invokeResult
-}
diff --git a/tests/dat/build.sh b/tests/dat/build.sh
index f016d00..7dd9180 100755
--- a/tests/dat/build.sh
+++ b/tests/dat/build.sh
@@ -18,8 +18,6 @@
set -e
-../../tools/build/compile.sh HelloSwift3 swift:3.1.1 "-v"
-
../../tools/build/compile.sh HelloSwift4 swift:4.1 "-v"
../../tools/build/compile.sh SwiftyRequest swift:4.1 "-v"
../../tools/build/compile.sh SwiftyRequestCodable swift:4.1 "-v"
@@ -30,6 +28,11 @@ set -e
../../tools/build/compile5.sh action-swift-v5.1 SwiftyRequest5 swift:5.1 "-v"
../../tools/build/compile5.sh action-swift-v5.1 SwiftyRequestCodable5 swift:5.1 "-v"
+../../tools/build/compile5.sh action-swift-v5.3 HelloSwift5 swift5.3 "-v"
+../../tools/build/compile5.sh action-swift-v5.3 HelloSwift5Codable swift5.3 "-v"
+../../tools/build/compile5.sh action-swift-v5.3 SwiftyRequest5 swift:5.3 "-v"
+../../tools/build/compile5.sh action-swift-v5.3 SwiftyRequestCodable5 swift:5.3 "-v"
+
cd actions
make all
diff --git a/tests/dat/build/swift5.3/HelloSwift5.zip b/tests/dat/build/swift5.3/HelloSwift5.zip
new file mode 100644
index 0000000..a16e1be
Binary files /dev/null and b/tests/dat/build/swift5.3/HelloSwift5.zip differ
diff --git a/tests/dat/build/swift5.3/HelloSwift5Codable.zip b/tests/dat/build/swift5.3/HelloSwift5Codable.zip
new file mode 100644
index 0000000..2e462ec
Binary files /dev/null and b/tests/dat/build/swift5.3/HelloSwift5Codable.zip differ
diff --git a/tests/dat/build/swift5.3/SwiftyRequest5.zip b/tests/dat/build/swift5.3/SwiftyRequest5.zip
new file mode 100644
index 0000000..cc05056
Binary files /dev/null and b/tests/dat/build/swift5.3/SwiftyRequest5.zip differ
diff --git a/tests/dat/build/swift5.3/SwiftyRequestCodable5.zip b/tests/dat/build/swift5.3/SwiftyRequestCodable5.zip
new file mode 100644
index 0000000..e3c01a9
Binary files /dev/null and b/tests/dat/build/swift5.3/SwiftyRequestCodable5.zip differ
diff --git a/tests/src/test/scala/runtime/actionContainers/Swift53ActionContainerTests.scala b/tests/src/test/scala/runtime/actionContainers/Swift53ActionContainerTests.scala
new file mode 100644
index 0000000..0814f9a
--- /dev/null
+++ b/tests/src/test/scala/runtime/actionContainers/Swift53ActionContainerTests.scala
@@ -0,0 +1,121 @@
+/*
+ * 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 Swift53ActionContainerTests extends SwiftActionContainerTests {
+ override lazy val swiftContainerImageName = "action-swift-v5.3"
+ override lazy val swiftBinaryName = "tests/dat/build/swift5.3/HelloSwift5.zip"
+ lazy val partyCompile = "tests/dat/build/swift5.3/SwiftyRequest5.zip"
+ lazy val partyCompileCodable = "tests/dat/build/swift5.3/SwiftyRequestCodable5.zip"
+
+ val httpCode = """
+ | import Foundation
+ | import Dispatch
+ | #if canImport(FoundationNetworking)
+ | import FoundationNetworking
+ | #endif
+ | 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/dat/actions/sdk/swift3/trigger.swift b/tests/src/test/scala/runtime/actionContainers/Swift53CodableActionContainerTests.scala
similarity index 68%
rename from tests/dat/actions/sdk/swift3/trigger.swift
rename to tests/src/test/scala/runtime/actionContainers/Swift53CodableActionContainerTests.scala
index c3f7599..4158896 100644
--- a/tests/dat/actions/sdk/swift3/trigger.swift
+++ b/tests/src/test/scala/runtime/actionContainers/Swift53CodableActionContainerTests.scala
@@ -15,11 +15,13 @@
* limitations under the License.
*/
-func main(args: [String:Any]) -> [String:Any] {
- if let triggerName = args["triggerName"] as? String {
- print("Trigger Name: \(triggerName)")
- return Whisk.trigger(eventNamed: triggerName, withParameters: [:])
- } else {
- return ["error": "You must specify a triggerName parameter!"]
- }
+package runtime.actionContainers
+
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+
+@RunWith(classOf[JUnitRunner])
+class Swift53CodableActionContainerTests extends SwiftCodableActionContainerTests {
+ override lazy val swiftContainerImageName = "action-swift-v5.3"
+ override lazy val swiftBinaryName = "tests/dat/build/swift5.3/HelloSwift5Codable.zip"
}
diff --git a/tests/dat/actions/sdk/swift3/hello.swift b/tests/src/test/scala/runtime/sdk/Swift53SDKTests.scala
similarity index 75%
rename from tests/dat/actions/sdk/swift3/hello.swift
rename to tests/src/test/scala/runtime/sdk/Swift53SDKTests.scala
index 2f5d944..25226c9 100644
--- a/tests/dat/actions/sdk/swift3/hello.swift
+++ b/tests/src/test/scala/runtime/sdk/Swift53SDKTests.scala
@@ -15,13 +15,12 @@
* limitations under the License.
*/
-/**
- * Hello world as a Swift Whisk action.
- */
-func main(args: [String:Any]) -> [String:Any] {
- if let name = args["name"] as? String {
- return [ "greeting" : "Hello \(name)!" ]
- } else {
- return [ "greeting" : "Hello stranger!" ]
- }
+package runtime.sdk
+
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+
+@RunWith(classOf[JUnitRunner])
+class Swift53SDKTests extends SwiftSDKTests {
+ override lazy val actionKind = "swift:5.3"
}
diff --git a/tools/travis/build.sh b/tools/travis/build.sh
index 67962d1..6b9fbbc 100755
--- a/tools/travis/build.sh
+++ b/tools/travis/build.sh
@@ -57,4 +57,5 @@ TERM=dumb ./gradlew \
:core:swift41Action:distDocker \
:core:swift42Action:distDocker \
:core:swift51Action:distDocker \
+:core:swift53Action:distDocker \
-PdockerImagePrefix=${IMAGE_PREFIX}
diff --git a/tools/travis/publish.sh b/tools/travis/publish.sh
index 6bfce5a..9498c1e 100755
--- a/tools/travis/publish.sh
+++ b/tools/travis/publish.sh
@@ -36,6 +36,8 @@ elif [ ${RUNTIME_VERSION} == "4.2" ]; then
RUNTIME="swift42Action"
elif [ ${RUNTIME_VERSION} == "5.1" ]; then
RUNTIME="swift51Action"
+elif [ ${RUNTIME_VERSION} == "5.3" ]; then
+ RUNTIME="swift53Action"
fi
if [[ ! -z ${DOCKER_USER} ]] && [[ ! -z ${DOCKER_PASSWORD} ]]; then