runtimes/runtimes.go (489 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package runtimes import ( "crypto/tls" "encoding/json" "io/ioutil" "net/http" "net/url" "strings" "time" "github.com/apache/openwhisk-client-go/whisk" "github.com/apache/openwhisk-wskdeploy/utils" "github.com/apache/openwhisk-wskdeploy/wskderrors" "github.com/apache/openwhisk-wskdeploy/wski18n" "github.com/apache/openwhisk-wskdeploy/wskprint" ) const ( NODEJS_FILE_EXTENSION = "js" SWIFT_FILE_EXTENSION = "swift" PYTHON_FILE_EXTENSION = "py" JAVA_FILE_EXTENSION = "java" JAR_FILE_EXTENSION = "jar" CSHARP_FILE_EXTENSION = "cs" PHP_FILE_EXTENSION = "php" ZIP_FILE_EXTENSION = "zip" RUBY_FILE_EXTENSION = "rb" GO_FILE_EXTENSION = "go" RUST_FILE_EXTENSION = "rs" NODEJS_RUNTIME = "nodejs" SWIFT_RUNTIME = SWIFT_FILE_EXTENSION PYTHON_RUNTIME = "python" JAVA_RUNTIME = JAVA_FILE_EXTENSION DOTNET_RUNTIME = ZIP_FILE_EXTENSION PHP_RUNTIME = PHP_FILE_EXTENSION RUBY_RUNTIME = "ruby" GO_RUNTIME = GO_FILE_EXTENSION RUST_RUNTIME = "rust" HTTP_CONTENT_TYPE_KEY = "Content-Type" HTTP_CONTENT_TYPE_VALUE = "application/json; charset=UTF-8" RUNTIME_NOT_SPECIFIED = "NOT SPECIFIED" BLACKBOX = "blackbox" HTTPS = "https://" ) // Structs used to denote the OpenWhisk Runtime information type Limit struct { Apm uint `json:"actions_per_minute"` Tpm uint `json:"triggers_per_minute"` ConAction uint `json:"concurrent_actions"` } type Runtime struct { Deprecated bool `json:"deprecated"` Default bool `json:"default"` Kind string `json:"kind"` } type SupportInfo struct { Github string `json:"github"` Slack string `json:"slack"` } type OpenWhiskInfo struct { Support SupportInfo `json:"support"` Desc string `json:"description"` ApiPath []string `json:"api_paths"` Runtimes map[string][]Runtime `json:"runtimes"` Limits Limit `json:"limits"` } var FileExtensionRuntimeKindMap map[string]string var SupportedRunTimes map[string][]string var DefaultRunTimes map[string]string var FileRuntimeExtensionsMap map[string]string // We could get the openwhisk info from bluemix through running the command // `curl -k https://openwhisk.ng.bluemix.net` // hard coding it here in case of network unavailable or failure. func ParseOpenWhisk(apiHost string) (op OpenWhiskInfo, err error) { opURL := apiHost _, err = url.ParseRequestURI(opURL) if err != nil { opURL = HTTPS + opURL } req, _ := http.NewRequest("GET", opURL, nil) req.Header.Set(HTTP_CONTENT_TYPE_KEY, HTTP_CONTENT_TYPE_VALUE) tlsConfig := &tls.Config{ InsecureSkipVerify: true, } var netTransport = &http.Transport{ TLSClientConfig: tlsConfig, } var netClient = &http.Client{ Timeout: time.Second * utils.DEFAULT_HTTP_TIMEOUT, Transport: netTransport, } res, err := netClient.Do(req) if err != nil { // TODO() create an error errString := wski18n.T(wski18n.ID_ERR_RUNTIMES_GET_X_err_X, map[string]interface{}{"err": err.Error()}) whisk.Debug(whisk.DbgWarn, errString) if utils.Flags.Strict { errMessage := wski18n.T(wski18n.ID_ERR_RUNTIME_PARSER_ERROR, map[string]interface{}{wski18n.KEY_ERR: err.Error()}) err = wskderrors.NewRuntimeParserError(errMessage) return } } if res != nil { defer res.Body.Close() } // Local openwhisk deployment sometimes only returns "application/json" as the content type if err != nil || !strings.Contains(HTTP_CONTENT_TYPE_VALUE, res.Header.Get(HTTP_CONTENT_TYPE_KEY)) { stdout := wski18n.T(wski18n.ID_MSG_UNMARSHAL_LOCAL) wskprint.PrintOpenWhiskVerbose(utils.Flags.Verbose, stdout) err = json.Unmarshal(RUNTIME_DETAILS, &op) if err != nil { errMessage := wski18n.T(wski18n.ID_ERR_RUNTIME_PARSER_ERROR, map[string]interface{}{wski18n.KEY_ERR: err.Error()}) err = wskderrors.NewRuntimeParserError(errMessage) } } else { b, _ := ioutil.ReadAll(res.Body) if b != nil && len(b) > 0 { stdout := wski18n.T(wski18n.ID_MSG_UNMARSHAL_NETWORK_X_url_X, map[string]interface{}{"url": opURL}) wskprint.PrintOpenWhiskVerbose(utils.Flags.Verbose, stdout) err = json.Unmarshal(b, &op) if err != nil { errMessage := wski18n.T(wski18n.ID_ERR_RUNTIME_PARSER_ERROR, map[string]interface{}{wski18n.KEY_ERR: err.Error()}) err = wskderrors.NewRuntimeParserError(errMessage) } } } return } func ConvertToMap(op OpenWhiskInfo) (rt map[string][]string) { rt = make(map[string][]string) for k, v := range op.Runtimes { rt[k] = make([]string, 0, len(v)) for i := range v { if !v[i].Deprecated { rt[k] = append(rt[k], v[i].Kind) } if v[i].Default { rt[k] = append(rt[k], strings.Split(v[i].Kind, ":")[0]+":default") } } } return } func DefaultRuntimes(op OpenWhiskInfo) (rt map[string]string) { rt = make(map[string]string) for k, v := range op.Runtimes { for i := range v { if !v[i].Deprecated { if v[i].Default { rt[k] = v[i].Kind } } } } return } func FileExtensionRuntimes(op OpenWhiskInfo) (ext map[string]string) { ext = make(map[string]string) for k := range op.Runtimes { if strings.Contains(k, NODEJS_RUNTIME) { ext[NODEJS_FILE_EXTENSION] = k } else if strings.Contains(k, PYTHON_RUNTIME) { ext[PYTHON_FILE_EXTENSION] = k } else if strings.Contains(k, SWIFT_RUNTIME) { ext[SWIFT_FILE_EXTENSION] = k } else if strings.Contains(k, PHP_RUNTIME) { ext[PHP_FILE_EXTENSION] = k } else if strings.Contains(k, JAVA_RUNTIME) { ext[JAVA_FILE_EXTENSION] = k ext[JAR_FILE_EXTENSION] = k } else if strings.Contains(k, RUBY_RUNTIME) { ext[RUBY_FILE_EXTENSION] = k } else if strings.Contains(k, RUST_RUNTIME) { ext[RUST_FILE_EXTENSION] = k } else if strings.Contains(k, GO_RUNTIME) { ext[GO_FILE_EXTENSION] = k } else if strings.Contains(k, DOTNET_RUNTIME) { ext[CSHARP_FILE_EXTENSION] = k ext[ZIP_FILE_EXTENSION] = k } else if strings.Contains(k, RUST_RUNTIME) { ext[RUST_FILE_EXTENSION] = k } } return } func FileRuntimeExtensions(op OpenWhiskInfo) (rte map[string]string) { rte = make(map[string]string) for k, v := range op.Runtimes { for i := range v { if !v[i].Deprecated { if strings.Contains(k, NODEJS_RUNTIME) { rte[v[i].Kind] = NODEJS_FILE_EXTENSION } else if strings.Contains(k, PYTHON_RUNTIME) { rte[v[i].Kind] = PYTHON_FILE_EXTENSION } else if strings.Contains(k, SWIFT_RUNTIME) { rte[v[i].Kind] = SWIFT_FILE_EXTENSION } else if strings.Contains(k, PHP_RUNTIME) { rte[v[i].Kind] = PHP_FILE_EXTENSION } else if strings.Contains(k, JAVA_RUNTIME) { rte[v[i].Kind] = JAVA_FILE_EXTENSION } else if strings.Contains(k, RUBY_RUNTIME) { rte[v[i].Kind] = RUBY_FILE_EXTENSION } else if strings.Contains(k, RUST_RUNTIME) { rte[v[i].Kind] = RUST_FILE_EXTENSION } else if strings.Contains(k, GO_RUNTIME) { rte[v[i].Kind] = GO_FILE_EXTENSION } else if strings.Contains(k, DOTNET_RUNTIME) { rte[v[i].Kind] = CSHARP_FILE_EXTENSION } else if strings.Contains(k, RUST_RUNTIME) { rte[v[i].Kind] = RUST_FILE_EXTENSION } } } } return } func CheckRuntimeConsistencyWithFileExtension(ext string, runtime string) bool { rt := FileExtensionRuntimeKindMap[ext] for _, v := range SupportedRunTimes[rt] { if runtime == v { return true } } return false } func CheckExistRuntime(rtname string, runtimes map[string][]string) bool { for _, v := range runtimes { for i := range v { if rtname == v[i] { return true } } } return false } func ListOfSupportedRuntimes(runtimes map[string][]string) (rt []string) { for _, v := range runtimes { for _, t := range v { rt = append(rt, t) } } return } var RUNTIME_DETAILS = []byte(`{ "runtimes": { "nodejs": [ { "kind": "nodejs:6", "default": false, "image": { "prefix": "openwhisk", "name": "nodejs6action", "tag": "nightly" }, "deprecated": true, "attached": { "attachmentName": "codefile", "attachmentType": "text/plain" } }, { "kind": "nodejs:8", "default": false, "image": { "prefix": "openwhisk", "name": "action-nodejs-v8", "tag": "nightly" }, "deprecated": false, "attached": { "attachmentName": "codefile", "attachmentType": "text/plain" } }, { "kind": "nodejs:10", "default": true, "image": { "prefix": "openwhisk", "name": "action-nodejs-v10", "tag": "nightly" }, "deprecated": false, "attached": { "attachmentName": "codefile", "attachmentType": "text/plain" }, "stemCells": [ { "count": 2, "memory": "256 MB" } ] }, { "kind": "nodejs:12", "default": false, "image": { "prefix": "openwhisk", "name": "action-nodejs-v12", "tag": "nightly" }, "deprecated": false, "attached": { "attachmentName": "codefile", "attachmentType": "text/plain" } } ], "python": [ { "kind": "python:2", "default": false, "image": { "prefix": "openwhisk", "name": "python2action", "tag": "1.13.0-incubating" }, "deprecated": false, "attached": { "attachmentName": "codefile", "attachmentType": "text/plain" } }, { "kind": "python:3", "default": true, "image": { "prefix": "openwhisk", "name": "python3action", "tag": "nightly" }, "deprecated": false, "attached": { "attachmentName": "codefile", "attachmentType": "text/plain" } } ], "swift": [ { "kind": "swift:5.4", "default": true, "image": { "prefix": "openwhisk", "name": "action-swift-v4.2", "tag": "nightly" }, "deprecated": false, "attached": { "attachmentName": "codefile", "attachmentType": "text/plain" } } ], "java": [ { "kind": "java:8", "default": true, "image": { "prefix": "openwhisk", "name": "java8action", "tag": "nightly" }, "deprecated": false, "attached": { "attachmentName": "codefile", "attachmentType": "text/plain" }, "requireMain": true } ], "php": [ { "kind": "php:7.3", "default": false, "deprecated": false, "image": { "prefix": "openwhisk", "name": "action-php-v7.3", "tag": "nightly" }, "attached": { "attachmentName": "codefile", "attachmentType": "text/plain" } }, { "kind": "php:7.4", "default": true, "deprecated": false, "image": { "prefix": "openwhisk", "name": "action-php-v7.4", "tag": "nightly" }, "attached": { "attachmentName": "codefile", "attachmentType": "text/plain" } } ], "ruby": [ { "kind": "ruby:2.5", "default": true, "deprecated": false, "attached": { "attachmentName": "codefile", "attachmentType": "text/plain" }, "image": { "prefix": "openwhisk", "name": "action-ruby-v2.5", "tag": "nightly" } } ], "go": [ { "kind": "go:1.17", "default": true, "deprecated": false, "attached": { "attachmentName": "codefile", "attachmentType": "text/plain" }, "image": { "prefix": "openwhisk", "name": "action-golang-v1.17", "tag": "nightly" } } ], "dotnet": [ { "kind": "dotnet:2.2", "default": true, "deprecated": false, "requireMain": true, "image": { "prefix": "openwhisk", "name": "action-dotnet-v2.2", "tag": "nightly" }, "attached": { "attachmentName": "codefile", "attachmentType": "text/plain" } } ], "ballerina": [ { "kind": "ballerina:0.990", "default": true, "image": { "prefix": "openwhisk", "name": "action-ballerina-v0.990.2", "tag": "nightly" }, "deprecated": false, "attached": { "attachmentName": "codefile", "attachmentType": "text/plain" } } ], "rust": [ { "kind": "rust:1.34", "default": true, "deprecated": false, "attached": { "attachmentName": "codefile", "attachmentType": "text/plain" }, "image": { "prefix": "openwhisk", "name": "actionloop-rust-v1.34", "tag": "latest" } } ] }, "blackboxes": [ { "prefix": "openwhisk", "name": "dockerskeleton", "tag": "nightly" } ] }`)