| /* |
| * 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 parsers |
| |
| import ( |
| "strings" |
| |
| "github.com/apache/incubator-openwhisk-client-go/whisk" |
| "github.com/apache/incubator-openwhisk-wskdeploy/utils" |
| "github.com/apache/incubator-openwhisk-wskdeploy/wskenv" |
| ) |
| |
| // YAML schema key names |
| // DO NOT translate |
| const ( |
| YAML_KEY_ACTION = "action" |
| YAML_KEY_ANNOTATION = "annotation" |
| YAML_KEY_API = "api" |
| YAML_KEY_FEED = "feed" |
| YAML_KEY_MANIFEST = "manifest" |
| YAML_KEY_NAMESPACE = "namespace" |
| YAML_KEY_PACKAGE = "package" |
| YAML_KEY_PACKAGES = "packages" |
| YAML_KEY_PROJECT = "project" |
| YAML_KEY_RULE = "rule" |
| YAML_KEY_SEQUENCE = "sequence" |
| YAML_KEY_TRIGGER = "trigger" |
| YAML_KEY_SOURCE = "source" |
| YAML_KEY_BLACKBOX = "blackbox" |
| ) |
| |
| // YAML schema key values |
| const ( |
| YAML_VALUE_BRANCH_MASTER = "master" |
| ) |
| |
| // default values |
| const ( |
| DEFAULT_PACKAGE_LICENSE = "unlicensed" |
| DEFAULT_PACKAGE_VERSION = "0.0.1" |
| ) |
| |
| // Known Limit values |
| const ( |
| // supported |
| LIMIT_VALUE_TIMEOUT = "timeout" |
| LIMIT_VALUE_MEMORY_SIZE = "memorySize" |
| LIMIT_VALUE_LOG_SIZE = "logSize" |
| // unsupported |
| LIMIT_VALUE_CONCURRENT_ACTIVATIONS = "concurrentActivations" |
| LIMIT_VALUE_USER_INVOCATION_RATE = "userInvocationRate" |
| LIMIT_VALUE_CODE_SIZE = "codeSize" |
| LIMIT_VALUE_PARAMETER_SIZE = "parameterSize" |
| ) |
| |
| var LIMITS_SUPPORTED = [](string){ |
| LIMIT_VALUE_TIMEOUT, |
| LIMIT_VALUE_MEMORY_SIZE, |
| LIMIT_VALUE_LOG_SIZE, |
| } |
| |
| var LIMITS_UNSUPPORTED = [](string){ |
| LIMIT_VALUE_CONCURRENT_ACTIVATIONS, |
| LIMIT_VALUE_USER_INVOCATION_RATE, |
| LIMIT_VALUE_CODE_SIZE, |
| LIMIT_VALUE_PARAMETER_SIZE, |
| } |
| |
| // structs that denote the sample manifest.yaml, wrapped yaml.v2 |
| func NewYAMLParser() *YAMLParser { |
| return &YAMLParser{} |
| } |
| |
| type YAMLParser struct { |
| manifests []*YAML |
| lastID uint32 |
| } |
| |
| // Action is mapped to wsk.Action.* |
| // Used in both manifest and deployment files |
| type Action struct { |
| Name string |
| // TODO(): deprecate location in favor of function |
| Location string `yaml:"location"` |
| Version string `yaml:"version"` |
| Function string `yaml:"function"` |
| Code string `yaml:"code"` |
| Runtime string `yaml:"runtime,omitempty"` |
| Namespace string `yaml:"namespace"` |
| Credential string `yaml:"credential"` |
| ExposedUrl string `yaml:"exposedUrl"` |
| Webexport string `yaml:"web-export"` |
| Web string `yaml:"web"` |
| Main string `yaml:"main"` |
| Docker string `yaml:"docker,omitempty"` |
| Native bool `yaml:"native,omitempty"` |
| Conductor bool `yaml:"conductor,omitempty"` |
| Limits *Limits `yaml:"limits"` |
| Inputs map[string]Parameter `yaml:"inputs"` |
| Outputs map[string]Parameter `yaml:"outputs"` |
| Annotations map[string]interface{} `yaml:"annotations,omitempty"` |
| Include [][]string `yaml:"include,omitempty"` |
| Exclude []string `yaml:"exclude,omitempty"` |
| } |
| |
| type Limits struct { |
| Timeout *int `yaml:"timeout,omitempty"` //in ms, [100 ms,300000ms] |
| Memory *int `yaml:"memorySize,omitempty"` //in MB, [128 MB,512 MB] |
| Logsize *int `yaml:"logSize,omitempty"` //in MB, [0MB,10MB] |
| ConcurrentActivations *int `yaml:"concurrentActivations,omitempty"` //not changeable via APIs |
| UserInvocationRate *int `yaml:"userInvocationRate,omitempty"` //not changeable via APIs |
| CodeSize *int `yaml:"codeSize,omitempty"` //not changeable via APIs |
| ParameterSize *int `yaml:"parameterSize,omitempty"` //not changeable via APIs |
| } |
| |
| type Sequence struct { |
| Actions string `yaml:"actions"` |
| Web string `yaml:"web"` |
| Annotations map[string]interface{} `yaml:"annotations,omitempty"` |
| } |
| |
| type Dependency struct { |
| Version string `yaml:"version,omitempty"` |
| Location string `yaml:"location,omitempty"` |
| Inputs map[string]Parameter `yaml:"inputs"` |
| Annotations map[string]interface{} `yaml:"annotations"` |
| } |
| |
| type Parameter struct { |
| Type string `yaml:"type,omitempty"` |
| Description string `yaml:"description,omitempty"` |
| Value interface{} `yaml:"value,omitempty"` |
| Required bool `yaml:"required,omitempty"` |
| Default interface{} `yaml:"default,omitempty"` |
| Status string `yaml:"status,omitempty"` |
| Schema interface{} `yaml:"schema,omitempty"` |
| multiline bool |
| } |
| |
| // Trigger is mapped wsk.Trigger.* |
| type Trigger struct { |
| Feed string `yaml:"feed"` |
| Namespace string `yaml:"namespace"` |
| Credential string `yaml:"credential"` |
| Inputs map[string]Parameter `yaml:"inputs"` |
| Name string |
| Annotations map[string]interface{} `yaml:"annotations,omitempty"` |
| // TODO() this is propoagated from package to trigger within that package |
| //Parameters map[string]interface{} `yaml:parameters` |
| // TODO(): deprecated, please delete it |
| Source string `yaml:"source"` |
| } |
| |
| type Feed struct { |
| Namespace string `yaml:"namespace"` |
| Credential string `yaml:"credential"` |
| Inputs map[string]string `yaml:"inputs"` |
| Location string `yaml:"location"` |
| Action string `yaml:"action"` |
| // TODO(): need to define operation structure |
| Operations map[string]interface{} `yaml:"operations"` |
| Name string |
| } |
| |
| type Rule struct { |
| //mapping to wsk.Rule.Trigger |
| Trigger string `yaml:"trigger"` //used in manifest.yaml |
| //mapping to wsk.Rule.Action |
| Action string `yaml:"action"` //used in manifest.yaml |
| Rule string `yaml:"rule"` //used in manifest.yaml |
| //mapping to wsk.Rule.Name |
| Name string |
| Annotations map[string]interface{} `yaml:"annotations,omitempty"` |
| } |
| |
| type Repository struct { |
| Url string `yaml:"url"` |
| Description string `yaml:"description,omitempty"` |
| Credential string `yaml:"credential,omitempty"` |
| } |
| |
| type APIMethodResponse struct { |
| Method string `yaml:"method"` |
| Response string `yaml:"response"` |
| } |
| |
| type Package struct { |
| Packagename string `yaml:"name"` |
| Version string `yaml:"version"` //mandatory |
| License string `yaml:"license"` //mandatory |
| Public bool `yaml:"public,omitempty"` |
| Repositories []Repository `yaml:"repositories,omitempty"` |
| Dependencies map[string]Dependency `yaml:"dependencies"` |
| Namespace string `yaml:"namespace"` |
| Credential string `yaml:"credential"` |
| ApiHost string `yaml:"apiHost"` |
| ApigwAccessToken string `yaml:"apigwAccessToken"` |
| Actions map[string]Action `yaml:"actions"` |
| Triggers map[string]Trigger `yaml:"triggers"` |
| Feeds map[string]Feed `yaml:"feeds"` |
| Rules map[string]Rule `yaml:"rules"` |
| Inputs map[string]Parameter `yaml:"inputs"` |
| Sequences map[string]Sequence `yaml:"sequences"` |
| Annotations map[string]interface{} `yaml:"annotations,omitempty"` |
| Apis map[string]map[string]map[string]map[string]APIMethodResponse `yaml:"apis"` |
| } |
| |
| type Project struct { |
| Name string `yaml:"name"` |
| Namespace string `yaml:"namespace"` |
| Credential string `yaml:"credential"` |
| ApiHost string `yaml:"apiHost"` |
| ApigwAccessToken string `yaml:"apigwAccessToken"` |
| Version string `yaml:"version"` |
| Packages map[string]Package `yaml:"packages"` |
| Inputs map[string]Parameter `yaml: parameters` |
| } |
| |
| type YAML struct { |
| Project Project `yaml:"project"` |
| Packages map[string]Package `yaml:"packages"` |
| Filepath string //file path of the yaml file |
| } |
| |
| type DisplayInputs struct { |
| Name string |
| Inputs map[string]interface{} |
| } |
| |
| type PackageInputs struct { |
| PackageName string |
| Inputs map[string]Parameter |
| } |
| |
| // function to return web-export or web depending on what is specified |
| // in manifest and deployment files |
| func (action *Action) GetWeb() string { |
| if len(action.Web) == 0 && len(action.Webexport) != 0 { |
| return action.Webexport |
| } |
| return action.Web |
| } |
| |
| // function to return Project or Application depending on what is specified in |
| // manifest and deployment files |
| func (yaml *YAML) GetProject() Project { |
| return yaml.Project |
| } |
| |
| func convertPackageName(packageMap map[string]Package, inputs map[string]Parameter) map[string]Package { |
| packages := make(map[string]Package) |
| for packName, depPacks := range packageMap { |
| name := packName |
| packageName := wskenv.InterpolateStringWithEnvVar(packName) |
| if str, ok := packageName.(string); ok { |
| name = str |
| } |
| if inputs != nil { |
| if len(name) == 0 { |
| packName = wskenv.GetEnvVarName(packName) |
| name = wskenv.ConvertSingleName(inputs[packName].Value.(string)) |
| } |
| } |
| depPacks.Packagename = wskenv.ConvertSingleName(depPacks.Packagename) |
| packages[name] = depPacks |
| } |
| return packages |
| } |
| |
| func ReadEnvVariable(yaml *YAML) *YAML { |
| yaml.Project.Packages = convertPackageName(yaml.Project.Packages, yaml.Project.Inputs) |
| yaml.Packages = convertPackageName(yaml.Packages, nil) |
| return yaml |
| } |
| |
| //********************Trigger functions*************************// |
| //add the key/value array as the annotations of the trigger. |
| func (trigger *Trigger) ComposeWskTrigger(kvarr []whisk.KeyValue) *whisk.Trigger { |
| wsktrigger := new(whisk.Trigger) |
| wsktrigger.Name = trigger.Name |
| wsktrigger.Namespace = trigger.Namespace |
| pub := false |
| wsktrigger.Publish = &pub |
| wsktrigger.Annotations = kvarr |
| return wsktrigger |
| } |
| |
| //********************Rule functions*************************// |
| func (rule *Rule) ComposeWskRule() *whisk.Rule { |
| wskrule := new(whisk.Rule) |
| wskrule.Name = wskenv.ConvertSingleName(rule.Name) |
| //wskrule.Namespace = rule.Namespace // TODO() ? |
| pub := false |
| wskrule.Publish = &pub |
| wskrule.Trigger = wskenv.ConvertSingleName(rule.Trigger) |
| wskrule.Action = wskenv.ConvertSingleName(rule.Action) |
| return wskrule |
| } |
| |
| //********************Package functions*************************// |
| func (pkg *Package) ComposeWskPackage() *whisk.Package { |
| wskpag := new(whisk.Package) |
| wskpag.Name = pkg.Packagename |
| wskpag.Namespace = pkg.Namespace |
| pub := false |
| wskpag.Publish = &pub |
| wskpag.Version = pkg.Version |
| return wskpag |
| } |
| |
| func (pkg *Package) GetActionList() []Action { |
| var s1 []Action = make([]Action, 0) |
| for action_name, action := range pkg.Actions { |
| action.Name = action_name |
| s1 = append(s1, action) |
| } |
| return s1 |
| } |
| |
| func (pkg *Package) GetTriggerList() []Trigger { |
| var s1 []Trigger = make([]Trigger, 0) |
| for trigger_name, trigger := range pkg.Triggers { |
| trigger.Name = trigger_name |
| s1 = append(s1, trigger) |
| } |
| return s1 |
| } |
| |
| func (pkg *Package) GetRuleList() []Rule { |
| var s1 []Rule = make([]Rule, 0) |
| for rule_name, rule := range pkg.Rules { |
| rule.Name = rule_name |
| s1 = append(s1, rule) |
| } |
| return s1 |
| } |
| |
| //This is for parse the deployment yaml file. |
| func (pkg *Package) GetFeedList() []Feed { |
| var s1 []Feed = make([]Feed, 0) |
| for feed_name, feed := range pkg.Feeds { |
| feed.Name = feed_name |
| s1 = append(s1, feed) |
| } |
| return s1 |
| } |
| |
| // This is for parse the manifest yaml file. |
| func (pkg *Package) GetApis() []*whisk.Api { |
| var apis = make([]*whisk.Api, 0) |
| for k, v := range pkg.Apis { |
| var apiName string = k |
| for k, v := range v { |
| var gatewayBasePath string = k |
| for k, v := range v { |
| var gatewayRelPath string = k |
| for k, v := range v { |
| api := &whisk.Api{} |
| api.ApiName = apiName |
| api.GatewayBasePath = gatewayBasePath |
| api.GatewayRelPath = gatewayRelPath |
| action := &whisk.ApiAction{} |
| action.Name = k |
| action.BackendMethod = v.Method |
| api.Action = action |
| apis = append(apis, api) |
| } |
| } |
| } |
| } |
| return apis |
| } |
| |
| //********************YAML functions*************************// |
| func filterAnnotations(annotations whisk.KeyValueArr) map[string]interface{} { |
| res := make(map[string]interface{}) |
| for _, a := range annotations { |
| if a.Key != utils.MANAGED { |
| res[a.Key] = a.Value |
| } |
| } |
| |
| return res |
| } |
| |
| func (yaml *YAML) ComposeParsersPackage(wskpag whisk.Package) *Package { |
| pkg := new(Package) |
| pkg.Packagename = wskpag.Name |
| pkg.Namespace = wskpag.Namespace |
| pkg.Version = wskpag.Version |
| |
| for _, keyval := range wskpag.Parameters { |
| param := new(Parameter) |
| param.Value = keyval.Value |
| pkg.Inputs[keyval.Key] = *param |
| } |
| |
| pkg.Annotations = filterAnnotations(wskpag.Annotations) |
| return pkg |
| } |
| |
| func (yaml *YAML) ComposeParsersAction(wskact whisk.Action) *Action { |
| action := new(Action) |
| action.Name = wskact.Name |
| action.Namespace = wskact.Namespace |
| action.Version = wskact.Version |
| action.Main = wskact.Exec.Main |
| |
| action.Inputs = make(map[string]Parameter) |
| for _, keyval := range wskact.Parameters { |
| param := new(Parameter) |
| param.Value = keyval.Value |
| action.Inputs[keyval.Key] = *param |
| } |
| |
| action.Annotations = filterAnnotations(wskact.Annotations) |
| |
| runtime := strings.Split(wskact.Exec.Kind, ":")[0] |
| if strings.ToLower(runtime) == YAML_KEY_BLACKBOX { |
| // storing blackbox image reference without saving the code as its impossible |
| action.Docker = wskact.Exec.Image |
| } else { |
| action.Runtime = wskact.Exec.Kind |
| } |
| |
| return action |
| } |
| |
| func (yaml *YAML) ComposeParsersTrigger(wsktrg whisk.Trigger) *Trigger { |
| trigger := new(Trigger) |
| trigger.Name = wsktrg.Name |
| trigger.Namespace = wsktrg.Namespace |
| trigger.Inputs = make(map[string]Parameter) |
| for _, keyval := range wsktrg.Parameters { |
| param := new(Parameter) |
| param.Value = keyval.Value |
| trigger.Inputs[keyval.Key] = *param |
| } |
| |
| if feedname, isFeed := utils.IsFeedAction(&wsktrg); isFeed { |
| trigger.Source = feedname |
| } |
| trigger.Annotations = filterAnnotations(wsktrg.Annotations) |
| return trigger |
| } |
| |
| func (yaml *YAML) ComposeParsersDependency(binding whisk.Binding, bPkg whisk.Package) *Dependency { |
| dependency := new(Dependency) |
| dependency.Location = "/" + binding.Namespace + "/" + binding.Name |
| |
| dependency.Inputs = make(map[string]Parameter) |
| for _, keyval := range bPkg.Parameters { |
| param := new(Parameter) |
| param.Value = keyval.Value |
| dependency.Inputs[keyval.Key] = *param |
| } |
| return dependency |
| } |
| |
| func (yaml *YAML) ComposeParsersRule(wskrule whisk.Rule) *Rule { |
| rule := new(Rule) |
| rule.Name = wskrule.Name |
| |
| pa := wskrule.Action.(map[string]interface{})["path"].(string) |
| pa = strings.SplitN(pa, "/", 2)[1] |
| |
| rule.Action = pa + "/" + wskrule.Action.(map[string]interface{})["name"].(string) |
| rule.Trigger = wskrule.Trigger.(map[string]interface{})["name"].(string) |
| |
| rule.Annotations = filterAnnotations(wskrule.Annotations) |
| return rule |
| } |