1. Project Overview & Quickstart (apache/openwhisk)
File: README.md
OpenWhisk
OpenWhisk is a serverless functions platform for building cloud applications.
OpenWhisk offers a rich programming model for creating serverless APIs from functions,
composing functions into serverless workflows, and connecting events to functions using rules and triggers.
Learn more at http://openwhisk.apache.org.
- Quick Start (Deploy and Use OpenWhisk on your machine)
- Deploy to Kubernetes (For development and production)
- For project contributors and Docker deployments:
- Learn Concepts and Commands
- OpenWhisk Community and Support
- Project Repository Structure
Notice of Breaking Upgrade 10/17/2025
Apache Openwhisk has migrated to the Apache Pekko framework. The master branch as of 10/17/2025 uses Apache Pekko. This change results in a breaking change such that you must re-deploy new clusters and cutover traffic to the new cluster. All other changes should be transient to you other than instead of using Akka configuration overrides in your deployments, you would now need to update those to use the Pekko equivalent. A 3.x release branch will eventually follow this
change.
Quick Start
The easiest way to start using OpenWhisk is to install the "Standalone" OpenWhisk stack.
This is a full-featured OpenWhisk stack running as a Java process for convenience.
Serverless functions run within Docker containers. You will need Docker,
Java and Node.js available on your machine.
To get started:
git clone https://github.com/apache/openwhisk.git
cd openwhisk
./gradlew core:standalone:bootRunWhen the OpenWhisk stack is up, it will open your browser to a functions Playground,
typically served from http://localhost:3232. The Playground allows you create and run functions directly from your browser.To make use of all OpenWhisk features, you will need the OpenWhisk command line tool called
wskwhich you can download from https://s.apache.org/openwhisk-cli-download.
Please refer to the CLI configuration for additional details. Typically you
configure the CLI for Standalone OpenWhisk as follows:
wsk property set \
--apihost 'http://localhost:3233' \
--auth '23bc46b1-71f6-4ed5-8c54-816aa4f8c502:123zO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP'- Standalone OpenWhisk can be configured to deploy additional capabilities when that is desirable.
Additional resources are available here.
Deploy to Kubernetes
OpenWhisk can also be installed on a Kubernetes cluster. You can use
a managed Kubernetes cluster provisioned from a public cloud provider
(e.g., AKS, EKS, IKS, GKE), or a cluster you manage yourself.
Additionally for local development, OpenWhisk is compatible with Minikube,
and Kubernetes for Mac using the support built into Docker 18.06 (or higher).
To get started:
git clone https://github.com/apache/openwhisk-deploy-kube.gitThen follow the instructions in the OpenWhisk on Kubernetes README.md.
Learn Concepts and Commands
Browse the documentation to learn more. Here are some topics you may be
interested in:
- System overview
- Getting Started
- Create and invoke actions
- Create triggers and rules
- Use and create packages
- Browse and use the catalog
- OpenWhisk system details
- Implementing feeds
- Developing a runtime for a new language
OpenWhisk Community and Support
Report bugs, ask questions and request features here on GitHub.
You can also join the OpenWhisk Team on Slack https://openwhisk-team.slack.com and chat with developers. To get access to our public Slack team, request an invite https://openwhisk.apache.org/slack.html.
Project Repository Structure
The OpenWhisk system is built from a number of components. The picture below groups the components by their GitHub repos. Please open issues for a component against the appropriate repo (if in doubt just open against the main openwhisk repo).
What happens on an invocation?
This diagram depicts the steps which take place within Openwhisk when an action is invoked by the user:
File: ansible/README.md
Deploying OpenWhisk using Ansible
Getting started
If you want to deploy OpenWhisk locally using Ansible, you first need to install Ansible on your development environment:
Ubuntu users
sudo apt-get install python-pip
sudo pip install ansible==4.1.0
sudo pip install jinja2==3.0.1Docker for Mac users
sudo easy_install pip
sudo pip install ansible==4.1.0
pip install jinja2==3.0.1Docker for Mac does not provide any official ways to meet some requirements for OpenWhisk.
You need to depend on the workarounds until Docker provides official methods.
If you prefer Docker-machine to Docker for mac, you can follow instructions in docker-machine/README.md.
Enable Docker remote API
The remote Docker API is required for collecting logs using the Ansible playbook logs.yml.
Activate docker0 network (local dev only)
The OpenWhisk deployment via Ansible uses the docker0 network interface to deploy OpenWhisk and it does not exist on Docker for Mac environment.
An expedient workaround is to add alias for docker0 network to loopback interface.
sudo ifconfig lo0 alias 172.17.0.1/24Using Ansible
Caveat: All Ansible commands are meant to be executed from the ansible directory.
This is important because that's where ansible.cfg is located which contains generic settings that are needed for the remaining steps.
Set the environment for the commands below by running
ENVIRONMENT=local # or docker-machine or jenkins or vagrantThe default environment is local which works for Ubuntu and
Docker for Mac. To use the default environment, you may omit the -i parameter entirely. For older Mac installation using Docker Machine,
use -i environments/docker-machine.
In all instructions, replace <openwhisk_home> with the base directory of your OpenWhisk source tree. e.g. openwhisk
Ansible with pyenv (local dev only)
When using pyenv to manage your versions of python, the ansible python interpreter will use your system's default python, which may have a different version.
To make sure ansible uses the same version of python which you configured, execute:
echo -e "\nansible_python_interpreter: `which python`\n" >> ./environments/local/group_vars/allPreserving configuration and log directories on reboot
When using the local Ansible environment, configuration and log data is stored in /tmp by default. However, operating
system such as Linux and Mac clean the /tmp directory on reboot, resulting in failures when OpenWhisk tries to start
up again. To avoid this problem, export the OPENWHISK_TMP_DIR variable assigning it the path to a persistent
directory before deploying OpenWhisk.
Setup
This step should be executed once per development environment.
It will generate the hosts configuration file based on your environment settings.
This file is generated automatically for an ephemeral CouchDB instance during
setup.yml.
The default configuration does not run multiple instances of core components (e.g., controller, invoker, kafka).
You may elect to enable high-availability (HA) mode by passing the Ansible option -e mode=HA when executing this playbook.
This will configure your deployment with multiple instances (e.g., two Kafka instances, and two invokers).
In addition to the host file generation, you need to configure the database for your deployment. This is done
by modifying the file ansible/db_local.ini to provide the following properties.
[db_creds]
db_provider=
db_username=
db_password=
db_protocol=
db_host=
db_port=For convenience, you can use shell environment variables that are read by the playbook to generate the required db_local.ini file as shown below.
export OW_DB=CouchDB
export OW_DB_USERNAME=<your couchdb user>
export OW_DB_PASSWORD=<your couchdb password>
export OW_DB_PROTOCOL=<your couchdb protocol>
export OW_DB_HOST=<your couchdb host>
export OW_DB_PORT=<your couchdb port>
ansible-playbook -i environments/$ENVIRONMENT setup.ymlUse Cloudant as a datastore
export OW_DB=Cloudant
export OW_DB_USERNAME=<your cloudant user>
export OW_DB_PASSWORD=<your cloudant password>
export OW_DB_PROTOCOL=https
export OW_DB_HOST=<your cloudant user>.cloudant.com
export OW_DB_PORT=443
ansible-playbook -i environments/$ENVIRONMENT setup.ymlInstall Prerequisites
This step is not required for local environments since all prerequisites are already installed, and therefore may be skipped.
This step needs to be done only once per target environment. It will install necessary prerequisites on all target hosts in the environment.
ansible-playbook -i environments/$ENVIRONMENT prereq.ymlHint: During playbook execution the TASK [prereq : check for pip] can show as failed. This is normal if no pip is installed. The playbook will then move on and install pip on the target machines.
[Optional] Enable the new scheduler
You can enable the new scheduler of OpenWhisk.
It will run one more component called "scheduler" and ETCD.
Configure service providers for the scheduler
You can update service providers for the scheduler as follows.
common/scala/src/main/resources/reference.conf
If you are using ElasticSearch (recommended) then replace NoopDurationCheckerProvider with ElasticSearchDurationCheckerProvider below.
whisk.spi {
ArtifactStoreProvider = org.apache.openwhisk.core.database.CouchDbStoreProvider
ActivationStoreProvider = org.apache.openwhisk.core.database.ArtifactActivationStoreProvider
MessagingProvider = org.apache.openwhisk.connector.kafka.KafkaMessagingProvider
ContainerFactoryProvider = org.apache.openwhisk.core.containerpool.docker.DockerContainerFactoryProvider
LogStoreProvider = org.apache.openwhisk.core.containerpool.logging.DockerToActivationLogStoreProvider
LoadBalancerProvider = org.apache.openwhisk.core.loadBalancer.FPCPoolBalancer
EntitlementSpiProvider = org.apache.openwhisk.core.entitlement.FPCEntitlementProvider
AuthenticationDirectiveProvider = org.apache.openwhisk.core.controller.BasicAuthenticationDirective
InvokerProvider = org.apache.openwhisk.core.invoker.FPCInvokerReactive
InvokerServerProvider = org.apache.openwhisk.core.invoker.FPCInvokerServer
DurationCheckerProvider = org.apache.openwhisk.core.scheduler.queue.NoopDurationCheckerProvider
}
.
.
.Configure pause grace for the scheduler
Set the value of pause-grace to 10s by default
core/invoker/src/main/resources/application.conf
container-proxy {
timeouts {
# The "unusedTimeout" in the ContainerProxy,
#aka 'How long should a container sit idle until we kill it?'
idle-container = 10 minutes
pause-grace = 10 seconds
keeping-duration = 10 minutes
}
.
.
.Disable the scheduler
- You can disable the scheduler by configuring
scheduler_enable. - The scheduler is enabled by default.
ansible/environments/local/group_vars/all
scheduler_enable: false[Optional] Enable ElasticSearch Activation Store
When you use the new scheduler, it is recommended to use ElasticSearch as an activation store.
ansible/environments/local/group_vars
db_activation_backend: ElasticSearch
elastic_cluster_name: <your elasticsearch cluster name>
elastic_protocol: <your elasticsearch protocol>
elastic_index_pattern: <your elasticsearch index pattern>
elastic_base_volume: <your elasticsearch volume directory>
elastic_username: <your elasticsearch username>
elastic_password: <your elasticsearch username>You can also refer to this guide to deploy OpenWhisk using ElasticSearch.
Deploying Using CouchDB
- Make sure your
db_local.inifile is setup for CouchDB then execute:
cd <openwhisk_home>
./gradlew distDocker
cd ansible
ansible-playbook -i environments/$ENVIRONMENT couchdb.yml
ansible-playbook -i environments/$ENVIRONMENT initdb.yml
ansible-playbook -i environments/$ENVIRONMENT wipe.yml
ansible-playbook -i environments/$ENVIRONMENT openwhisk.yml
# installs a catalog of public packages and actions
ansible-playbook -i environments/$ENVIRONMENT postdeploy.yml
# to use the API gateway
ansible-playbook -i environments/$ENVIRONMENT apigateway.yml
ansible-playbook -i environments/$ENVIRONMENT routemgmt.yml- You need to run
initdb.ymlevery time you do a fresh deploy CouchDB to initialize the subjects database. - The
wipe.ymlplaybook should be run on a fresh deployment only, otherwise actions and activations will be lost. - Run
postdeploy.ymlafter deployment to install a catalog of useful packages. - To use the API Gateway, you'll need to run
apigateway.ymlandroutemgmt.yml. - Use
ansible-playbook -i environments/$ENVIRONMENT openwhisk.ymlto avoid wiping the data store. This is useful to start OpenWhisk after restarting your Operating System.
Limitation
You cannot run multiple CouchDB nodes on a single machine. This limitation comes from Erlang EPMD which CouchDB relies on to find other nodes.
To deploy multiple CouchDB nodes, they should be placed on different machines respectively otherwise their ports will clash.
Deploying Using Cloudant
- Make sure your
db_local.inifile is set up for Cloudant. See Setup. - Then execute:
cd <openwhisk_home>
./gradlew distDocker
cd ansible
ansible-playbook -i environments/$ENVIRONMENT initdb.yml
ansible-playbook -i environments/$ENVIRONMENT wipe.yml
ansible-playbook -i environments/$ENVIRONMENT apigateway.yml
ansible-playbook -i environments/$ENVIRONMENT openwhisk.yml
# installs a catalog of public packages and actions
ansible-playbook -i environments/$ENVIRONMENT postdeploy.yml
# to use the API gateway
ansible-playbook -i environments/$ENVIRONMENT apigateway.yml
ansible-playbook -i environments/$ENVIRONMENT routemgmt.yml- You need to run
initdbon Cloudant only once per Cloudant database to initialize the subjects database. - The
initdb.ymlplaybook will only initialize your database if it is not initialized already, else it will skip initialization steps. - The
wipe.ymlplaybook should be run on a fresh deployment only, otherwise actions and activations will be lost. - Run
postdeploy.ymlafter deployment to install a catalog of useful packages. - To use the API Gateway, you'll need to run
apigateway.ymlandroutemgmt.yml. - Use
ansible-playbook -i environments/$ENVIRONMENT openwhisk.ymlto avoid wiping the data store. This is useful to start OpenWhisk after restarting your Operating System.
Deploying Using MongoDB
You can choose MongoDB instead of CouchDB as the database backend to store entities.
- Deploy a mongodb server(Optional, for test and develop only, use an external MongoDB server in production).
You need to executepip install pymongofirst
ansible-playbook -i environments/<environment> mongodb.yml -e mongodb_data_volume="/tmp/mongo-data"- Then execute
cd <openwhisk_home>
./gradlew distDocker
cd ansible
ansible-playbook -i environments/<environment> initMongodb.yml -e mongodb_connect_string="mongodb://172.17.0.1:27017"
ansible-playbook -i environments/<environment> apigateway.yml -e mongodb_connect_string="mongodb://172.17.0.1:27017"
ansible-playbook -i environments/<environment> openwhisk.yml -e mongodb_connect_string="mongodb://172.17.0.1:27017" -e db_artifact_backend="MongoDB"
# installs a catalog of public packages and actions
ansible-playbook -i environments/<environment> postdeploy.yml
# to use the API gateway
ansible-playbook -i environments/<environment> apigateway.yml
ansible-playbook -i environments/<environment> routemgmt.ymlAvailable parameters for ansible are
mongodb:
connect_string: "{{ mongodb_connect_string }}"
database: "{{ mongodb_database | default('whisks') }}"
data_volume: "{{ mongodb_data_volume | default('mongo-data') }}"Using ElasticSearch to Store Activations
You can use ElasticSearch (ES) to store activations separately while other entities remain stored in CouchDB. There is an Ansible playbook to setup a simple ES cluster for testing and development purposes.
- Provide your custom ES related ansible arguments:
elastic_protocol="http"
elastic_index_pattern="openwhisk-%s" // this will be combined with namespace's name, so different namespace can use different index
elastic_base_volume="esdata" // name of docker volume to store ES data
elastic_cluster_name="openwhisk"
elastic_java_opts="-Xms1g -Xmx1g"
elastic_loglevel="INFO"
elastic_username="admin"
elastic_password="admin"
elasticsearch_connect_string="x.x.x.x:9200,y.y.y.y:9200" // if you want to use an external ES cluster, add it- Then execute:
cd <openwhisk_home>
./gradlew distDocker
cd ansible
# couchdb is still needed to store subjects and actions
ansible-playbook -i environments/$ENVIRONMENT couchdb.yml
ansible-playbook -i environments/$ENVIRONMENT initdb.yml
ansible-playbook -i environments/$ENVIRONMENT wipe.yml
# this will deploy a simple ES cluster, you can skip this to use external ES cluster
ansible-playbook -i environments/$ENVIRONMENT elasticsearch.yml
ansible-playbook -i environments/$ENVIRONMENT openwhisk.yml -e db_activation_backend=ElasticSearch
# installs a catalog of public packages and actions
ansible-playbook -i environments/$ENVIRONMENT postdeploy.yml
# to use the API gateway
ansible-playbook -i environments/$ENVIRONMENT apigateway.yml
ansible-playbook -i environments/$ENVIRONMENT routemgmt.ymlConfiguring the installation of `wsk` CLI
There are two installation modes to install wsk CLI: remote and local.
The mode "remote" means to download the wsk binaries from available web links.
By default, OpenWhisk sets the installation mode to remote and downloads the
binaries from the CLI
release page,
where OpenWhisk publishes the official wsk binaries.
The mode "local" means to build and install the wsk binaries from local CLI
project. You can download the source code of OpenWhisk CLI
here.
Let's assume your OpenWhisk CLI home directory is$OPENWHISK_HOME/../openwhisk-cli and you've already exportedOPENWHISK_HOME to be the root directory of this project. After you download
the CLI repository, use the gradle command to build the binaries (you can omit
the -PnativeBuild if you want to cross-compile for all supported platforms):
cd "$OPENWHISK_HOME/../openwhisk-cli"
./gradlew releaseBinaries -PnativeBuildThe binaries are generated and put into a tarball in the folder../openwhisk-cli/release. Then, use the following Ansible command
to (re-)configure the CLI installation:
export OPENWHISK_ENVIRONMENT=local # ... or whatever
ansible-playbook -i environments/$OPENWHISK_ENVIRONMENT edge.yml -e mode=clean
ansible-playbook -i environments/$OPENWHISK_ENVIRONMENT edge.yml \
-e cli_installation_mode=local \
-e openwhisk_cli_home="$OPENWHISK_HOME/../openwhisk-cli"The parameter cli_installation_mode specifies the CLI installation mode and
the parameter openwhisk_cli_home specifies the home directory of your local
OpenWhisk CLI. (n.b. openwhisk_cli_home defaults to$OPENWHISK_HOME/../openwhisk-cli.)
Once the CLI is installed, you can use it to work with Whisk.
Hot-swapping a Single Component
The playbook structure allows you to clean, deploy or re-deploy a single component as well as the entire OpenWhisk stack. Let's assume you have deployed the entire stack using the openwhisk.yml playbook. You then make a change to a single component, for example the invoker. You will probably want a new tag on the invoker image so you first build it using:
cd <openwhisk_home>
./gradlew :core:invoker:distDocker -PdockerImageTag=myNewInvokerThen all you need to do is re-deploy the invoker using the new image:
cd ansible
ansible-playbook -i environments/$ENVIRONMENT invoker.yml -e docker_image_tag=myNewInvokerHint: You can omit the Docker image tag parameters in which case latest will be used implicitly.
Cleaning a Single Component
You can remove a single component just as you would remove the entire deployment stack.
For example, if you wanted to remove only the controller you would run:
cd ansible
ansible-playbook -i environments/$ENVIRONMENT controller.yml -e mode=cleanCaveat: In distributed environments some components (e.g. Invoker, etc.) exist on multiple machines. So if you run a playbook to clean or deploy those components, it will run on all of the hosts targeted by the component's playbook.
Cleaning an OpenWhisk Deployment
Once you are done with the deployment you can clean it from the target environment.
ansible-playbook -i environments/$ENVIRONMENT openwhisk.yml -e mode=cleanRemoving all prereqs from an environment
This is usually not necessary, however in case you want to uninstall all prereqs from a target environment, execute:
ansible-playbook -i environments/$ENVIRONMENT prereq.yml -e mode=cleanLean Setup
To have a lean setup (no Kafka, Zookeeper and no Invokers as separate entities):
At Deploying Using CouchDB step, replace:
ansible-playbook -i environments/$ENVIRONMENT openwhisk.ymlby:
ansible-playbook -i environments/$ENVIRONMENT openwhisk.yml -e lean=trueTroubleshooting
Some of the more common problems and their solution are listed here.
Setuptools Version Mismatch
If you encounter the following error message during ansible execution
ERROR! Unexpected Exception: ... Requirement.parse('setuptools>=11.3'))your setuptools package is likely out of date. You can upgrade the package using this command:
pip install --upgrade setuptools --user pythonMac Setup - Python Interpreter
The MacOS environment assumes Python is installed in /usr/local/bin which is the default location when using brew.
The following error will occur if Python is located elsewhere:
ansible all -i environments/mac -m ping
ansible | FAILED! => {
"changed": false,
"failed": true,
"module_stderr": "/bin/sh: /usr/local/bin/python: No such file or directory\n",
"module_stdout": "",
"msg": "MODULE FAILURE",
"parsed": false
}An expedient workaround is to create a link to the expected location:
ln -s $(which python) /usr/local/bin/pythonAlternatively, you can also configure the location of Python interpreter in environments/<environment>/group_vars.
ansible_python_interpreter: "/usr/local/bin/python"Failed to import docker-py
After brew install ansible, the following lines are printed out:
==> Caveats
If you need Python to find the installed site-packages:
mkdir -p ~/Library/Python/2.7/lib/python/site-packages
echo '/usr/local/lib/python2.7/site-packages' > ~/Library/Python/2.7/lib/python/site-packages/homebrew.pthJust run the two commands to fix this issue.
Spaces in Paths
Ansible 2.1.0.0 and earlier versions do not support a space in file paths.
Many file imports and roles will not work correctly when included from a path that contains spaces.
If you encounter this error message during Ansible execution
fatal: [ansible]: FAILED! => {"failed": true, "msg": "need more than 1 value to unpack"}the path to your OpenWhisk ansible directory contains spaces. To fix this, please copy the source tree to a path
without spaces as there is no current fix available to this problem.
Changing limits
The default system throttling limits are configured in this file ./group_vars/all and may be changed by modifying the group_vars for your specific environment.
limits:
invocationsPerMinute: "{{ limit_invocations_per_minute | default(60) }}"
concurrentInvocations: "{{ limit_invocations_concurrent | default(30) }}"
firesPerMinute: "{{ limit_fires_per_minute | default(60) }}"
sequenceMaxLength: "{{ limit_sequence_max_length | default(50) }}"- The
limits.invocationsPerMinuterepresents the allowed namespace action invocations per minute. - The
limits.concurrentInvocationsrepresents the maximum concurrent invocations allowed per namespace. - The
limits.firesPerMinuterepresents the allowed namespace trigger firings per minute. - The
limits.sequenceMaxLengthrepresents the maximum length of a sequence action.
Set the timezone for containers
The default timezone for all system containers is UTC. The timezone may differ from your servers which could make it difficult to inspect logs. The timezone is configured globally in group_vars/all or by passing an extra variable -e docker_timezone=xxx when you run an ansible-playbook.
File: core/standalone/README.md
OpenWhisk Standalone Server
OpenWhisk standalone server is meant to run a simple OpenWhisk server for local development and test purposes. It can be
executed as a normal java application from command line.
java -jar openwhisk-standalone.jarThis should start the OpenWhisk server on port 3233 by default and launch a Playground UI at port 3232.
The Playground UI can be used to try out simple actions. To make use of all OpenWhisk features configure the cli and
then try out the samples.
This server by default uses a memory based store and does not depend on any other external service like Kafka and CouchDB.
It only needs Docker and Java to for running.
Few key points related to it
- Uses in memory store. Once the server is stopped all changes would be lost
- Bootstraps the
guestandwhisk.systemwith default keys - Supports running on MacOS, Linux and Windows (experimental) setup
- Can be customized to use any other storage like CouchDB
Build
To build this standalone server run
$ ./gradlew :core:standalone:buildThis would create the runnable jar in bin/ directory. If you directly want to run the
server then you can use following command
$ ./gradlew :core:standalone:bootRunTo pass argument to the run command use
$ ./gradlew :core:standalone:bootRun --args='-m runtimes.json'You can also build a standalone docker image with:
$ ./gradlew :core:standalone:distDockerUsage
OpenWhisk standalone server support various launch options
/* Detailed source-code truncated for AI context efficiency. */Sections below would illustrate some of the supported options
To change the default config you can provide a custom application.conf file via -c option. The application conf file
must always include the default standalone.conf
include classpath("standalone.conf")
whisk {
//Custom config
}Then pass this config file
java -jar openwhisk-standalone.jar -c custom.confAdding custom namespaces
If you need to register custom namespaces (aka users) then you can pass them via config file like below
include classpath("standalone.conf")
whisk {
users {
whisk-test = "cafebabe-cafe-babe-cafe-babecafebabe:007zO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP"
}
}Then pass this config file via -c option. You can check the users created from log
[2019-06-21T19:52:02.923Z] [INFO] [#tid_userBootstrap] [StandaloneOpenWhisk] Created user [guest]
[2019-06-21T19:52:03.008Z] [INFO] [#tid_userBootstrap] [StandaloneOpenWhisk] Created user [whisk.system]
[2019-06-21T19:52:03.094Z] [INFO] [#tid_userBootstrap] [StandaloneOpenWhisk] Created user [whisk.test]Using custom runtimes
To use custom runtime pass the runtime manifest via -m option
{
"runtimes": {
"ruby": [
{
"kind": "ruby:2.5",
"default": true,
"deprecated": false,
"attached": {
"attachmentName": "codefile",
"attachmentType": "text/plain"
},
"image": {
"prefix": "openwhisk",
"name": "action-ruby-v2.5",
"tag": "latest"
}
}
]
}
}The pass this file at launch time
java -jar openwhisk-standalone.jar -m custom-runtime.jsonYou can then see the runtime config reflect in http://localhost:3233
Using CouchDB
If you need to use CouchDB then you can launch the standalone server with --couchdb option. This would launch
a CouchDB server which would configured to store files in user home directory under .openwhisk/standalone folder.
If you need to connect to external CouchDB or any other supported artifact store then you can pass the required config
include classpath("standalone.conf")
whisk {
couchdb {
protocol = "http"
host = "172.17.0.1"
port = "5984"
username = "whisk_admin"
password = "some_passw0rd"
provider = "CouchDB"
databases {
WhiskAuth = "whisk_local_subjects"
WhiskEntity = "whisk_local_whisks"
WhiskActivation = "whisk_local_activations"
}
}
}Then pass this config file via -c option.
Note that Standalone OpenWhisk will not bootstrap users and actions (e.g., API Gateway and Playground UI)
when using an external database unless explicitly requested with --enable-bootstrap. This is to ensure
that default users and actions are not added to your external artifact store.
Using API Gateway
API Gateway mode can be enabled via --api-gw flag. In this mode upon launch a separate container for OpenWhisk API gateway
would be launched on port 3234 (can be changed with --api-gw-port). In this mode you can make use of the
API Gateway support.
Using Kafka
Standalone OpenWhisk supports launching an embedded kafka. This mode is mostly useful for developers working on OpenWhisk
implementation itself.
java -jar openwhisk-standalone.jar --kafkaIt also supports launching a Kafka UI based on Kafdrop 3 which enables seeing the topics created and structure of messages
exchanged on those topics.
java -jar openwhisk-standalone.jar --kafka --kafka-uiBy default the ui server would be accessible at http://localhost:9000. In case 9000 port is busy then a random port would
be selected. To find out the port look for message in log like below (or grep for whisk-kafka-drop-ui)
[ 9092 ] localhost:9092 (kafka)
[ 9092 ] 192.168.65.2:9091 (kafka-docker)
[ 2181 ] Zookeeper (zookeeper)
[ 9000 ] http://localhost:9000 (whisk-kafka-drop-ui)User Events
Standalone OpenWhisk supports emitting user events and displaying them via Grafana Dashboard. The metrics are
consumed by User Event Service which converts them into metrics consumed via Prometheus.
java -jar openwhisk-standalone.jar --user-eventsThis mode would launch an embedded Kafka, User Event service, Prometheus and Grafana with preconfigured dashboards.
Launched service details
[ 9092 ] localhost:9092 (kafka)
[ 9091 ] 192.168.65.2:9091 (kafka-docker)
[ 2181 ] Zookeeper (zookeeper)
[ 3235 ] http://localhost:3235 (whisk-user-events)
[ 9090 ] http://localhost:9090 (whisk-prometheus)
[ 3000 ] http://localhost:3000 (whisk-grafana)Using KubernetesContainerFactory
Standalone OpenWhisk can be configured to use KubernetesContainerFactory (KCF) via --dev-kcf option. This mode can be used to
simplify developing KubernetesContainerFactory.
Below mentioned steps are based on Kind tool for running local Kubernetes clusters using Docker container "nodes".
However this mode should work against any Kubernetes cluster if the the KUBECONFIG is properly set.
1. Install and configure Kind
We would use Kind to setup a local k8s. Follow the steps here to create a simple cluster.
$ kind create cluster --wait 5m
# Export the kind config for kubectl usage
$ export KUBECONFIG="$(kind get kubeconfig-path)"
# Configure the default namespace
$ kubectl config set-context --current --namespace=default
# See the config path
$ kind get kubeconfig-path
/Users/example/.kube/kind-config-kind2. Launch Standalone
# Launch it with `kubeconfig` system property set to kind config
$ java -Dkubeconfig="$(kind get kubeconfig-path)" -jar bin/openwhisk-standalone.jar --dev-kcfOnce started and required .wskprops configured to use the standalone server create a hello.js function
function main(params) {
greeting = 'hello, world'
var hello = {payload: greeting}
var result = {...hello, ...process.env}
console.log(greeting);
return result
}$ wsk action create hello hello.js
$ wsk action invoke hello -brThis shows an output like below indicating that KubernetesContainerFactory based invocation is working properly.
{
"HOME": "/root",
"HOSTNAME": "wsk0-2-prewarm-nodejs10",
"KUBERNETES_PORT": "tcp://10.96.0.1:443",
"KUBERNETES_PORT_443_TCP": "tcp://10.96.0.1:443",
"KUBERNETES_PORT_443_TCP_ADDR": "10.96.0.1",
"KUBERNETES_PORT_443_TCP_PORT": "443",
"KUBERNETES_PORT_443_TCP_PROTO": "tcp",
"KUBERNETES_SERVICE_HOST": "10.96.0.1",
"KUBERNETES_SERVICE_PORT": "443",
"KUBERNETES_SERVICE_PORT_HTTPS": "443",
"NODE_VERSION": "10.15.3",
"PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"PWD": "/nodejsAction",
"YARN_VERSION": "1.13.0",
"__OW_ACTION_NAME": "/guest/hello",
"__OW_ACTION_VERSION": "0.0.1",
"__OW_ACTIVATION_ID": "71e48d2d62e142eca48d2d62e192ec2d",
"__OW_API_HOST": "http://host.docker.internal:3233",
"__OW_DEADLINE": "1570223213407",
"__OW_NAMESPACE": "guest",
"__OW_TRANSACTION_ID": "iSOoNklk6V7l7eh8KJnvugidKEmaNJmv",
"payload": "hello, world"
}Launching OpenWhisk standalone with Docker
If you have docker and bash installed, you can launch the standalone openwhisk from the docker image with just:
bash <(curl -sL https://s.apache.org/openwhisk.sh)
The script will start the standalone controller with Docker, and will also try to open the playground. It was tested on Linux, OSX and Windows with Git Bash. If a browser does not automatically open the OpenWhisk playground, you can access it at http://localhost:3232.
The default standalone controller image is published as openwhisk/standalone:nightly for convenience.
If you do not want to execute arbitrary code straight from the net, you can look at this script, check it and run it when you feel safe.
If the playground is not enough, you can then install the wsk CLI and retrieve the command line to configure wsk with:
docker logs openwhisk | grep 'wsk property'
To properly shut down OpenWhisk and any additional containers it has created, use this script or run the command:
docker exec openwhisk stop
Extra Args for the Standalone OpenWhisk Docker Image
You can specify a different image to this script and/or pass additional parameters to Docker and/or to the standalone jar. The general format is:
bash <(curl -sL https://s.apache.org/openwhisk.sh) [<docker-parameters>...] [] [<standalone-jar-options>...]
e.g.
bash <(curl -sL https://s.apache.org/openwhisk.sh) -e SOME_DOCKER_ENV=a openwhisk/standalone:nightly --no-ui
Extra args are useful to configure the JVM running OpenWhisk and to propagate additional environment variables to containers running images. This feature is useful for example to enable debugging for actions.
You can pass additional parameters (for example set system properties) to the JVM running OpenWhisk setting the environment variable JVM_EXTRA_ARGS. For example -e JVM_EXTRA_ARGS=-Dconfig.loads allows to enable tracing of configuration. You can set any OpenWhisk parameter with this feature.
You can also set additional environment variables for each container running actions invoked by OpenWhisk by setting CONTAINER_EXTRA_ENV. For example, setting -e CONTAINER_EXTRA_ENV=__OW_DEBUG_PORT=8081 enables debugging for those images supporting starting the action under a debugger, like the typescript runtime.
File: core/monitoring/user-events/README.md
OpenWhisk User Events
This service connects to events topic and publishes the events to various services like Prometheus, Datadog etc via Kamon. Refer to user specific metrics on how to enable them.
Local Run
First configure and run
openwhisk docker-composethat can be found in the openwhisk-tools project.
- Start service inside the cluster (on the same docker-compose network:
openwhisk_default) - The service will be available on port
9095 - The endpoint for exposing the metrics for Prometheus can be found on
/metrics.
Usage
The service needs the following env variables to be set
KAFKA_HOSTS- For local env it can be set to172.17.0.1:9093. When using OpenWhisk Devtools based setup usekafka- Namespaces can be removed from reports by listing them inside the
reference.confusing thewhisk.user-events.ignored-namespacesconfiguration.
e.g:
whisk {
user-events {
ignored-namespaces = ["canary","testing"]
}
}- To rename metrics tags, use the below configuration. Currently, this configuration only applies to the Prometheus
Metrics. For example, herenamespacetag name will be replaced byow_namespacein all metrics.
whisk {
user-events {
rename-tags {
# rename/relabel prometheus metrics tags
"namespace" = "ow_namespae"
}
}
}Integrations
Prometheus
The docker container would run the service and expose the metrics in format required by Prometheus at 9095 port
Grafana
The Openwhisk - Action Performance Metrics Grafana4 dashboard is available on localhost port 3000 at this address:
http://localhost:3000/d/Oew1lvymk/openwhisk-action-performance-metrics
The latest version of the dashboard can be found in the "compose/dashboard/openwhisk_events.json"
File: core/cosmosdb/cache-invalidator/README.md
OpenWhisk Cache Invalidator Service
This service performs cache invalidation in an OpenWhisk cluster to enable cache event propagation in multi region setups.
Design
An OpenWhisk cluster uses a Kafka topic cacheInvalidation to communicate changes to any cached entity. Messages on this
topic are of the form
{"instanceId":"controller0","key":{"mainId":"guest/hello"}}When deploying OpenWhisk across multiple nodes which do not share a common Kafka instance, we need a way to propagate the
cache-change events across the cluster. For CosmosDB based setups this can be done by using CosmosDB ChangeFeed
support. It enables reading changes that are made to any specific collection.
This service makes use of change feed processor java library and listen to changes happening in whisks and subject
collections and then convert them into Kafka message events which can be sent to cacheInvalidation topic local to the cluster
Usage
The service needs following env variables to be set
KAFKA_HOSTS- For local env it can be set to172.17.0.1:9093. When using OpenWhisk Devtools based setup usekafkaCOSMOSDB_ENDPOINT- Endpoint URL like https://.documents.azure.com:443/ COSMOSDB_KEY- DB KeyCOSMOSDB_NAME- DB name
Upon startup it would create a collection to manage the lease data with name cache-invalidator-lease. For events sent by
this service instanceId are sent to cache-invalidator
Local Run
Setup the OpenWhisk cluster using devtools but have it connect to CosmosDB. This would also start
the Kafka Topic UI at port 8001. Then when changes are made to the database, you should see events sent to the Kafka
topic. For example, if a a package is created with wsk package create test-package using the guest account, the following
event is generated:
{"instanceId":"cache-invalidator","key":{"mainId":"guest/test-package"}}File: docs/dev/troubleshooting/build-failures.md
Build Failures
This guide details problems that may occur during the OpenWhisk build process.
Dependency download failures from JCenter
Occasionally build failures occur when the JCenter repository is experiencing problems. An example of such a failure
is shown below.
FAILURE: Build failed with an exception.
* What went wrong:
A problem occurred configuring root project 'openwhisk'.
> Could not resolve all files for configuration ':classpath'.
> Could not download groovy-all.jar (org.codehaus.groovy:groovy-all:2.4.7)
> Could not get resource 'https://jcenter.bintray.com/org/codehaus/groovy/groovy-all/2.4.7/groovy-all-2.4.7.jar'.
> Could not GET 'https://jcenter.bintray.com/org/codehaus/groovy/groovy-all/2.4.7/groovy-all-2.4.7.jar'.
> Connect to akamai.bintray.com:443 [akamai.bintray.com/23.45.134.89] failed: Connection timed out (Connection timed out)To determine if this error is indeed related to JCenter issues, check the JFrog Bintray
status page.
File: docs/dev/configuration.md
Scala component configuration
Our Scala components are in the process of switching from our own WhiskConfig to the TypesafeConfig based pureconfig. That will give us a more sane way of parsing environment values, overriding them and defining sensible defaults for them.
TypesafeConfig is able to override its values via JVM system properties. Unfortunately, docker only supports passing environment variables in a comfortable way.
To solve that mismatch, we use a script to transform environment variables into JVM system properties to spare the developer of manually overriding all the values via explicit environment -> TypesafeConfig value mapping. That script applies the following transformations to all environment variables starting with a defined prefix ("CONFIG_" in our case):
_becomes.(TypesafeConfig hierarchies are built using.)camelCasedbecomescamel-cased(Kebabcase is usually used for TypesafeConfig keys)PascalCasedstaysPascalCased(Defining classnames for overriding SPIs is crucial)
Examples:
CONFIG_whisk_loadbalancer_invokerBusyThresholdbecomes-Dwhisk.loadbalancer.invoker-busy-thresholdCONFIG_pekko_remote_netty_tcp_bindPortbecomes-Dpekko.remote.netty.tcp.bind-portCONFIG_whisk_spi_LogStoreProviderbecomes-Dwhisk.spi.LogStoreProvider
File: docs/dev/future.md
Potential Future Enhancements
Web actions potential future enhancements
It is worthwhile to consider how to make several of the features currently offered by web actions more generally available to all action. It may also be useful to generalize the web-export annotation to provide additional control over which HTTP methods an action is prepared the handle and similarly which content types it supports. One can imagine doing this with a richer web-export annotation which may define at least these additional properties:
methods: array of HTTP methods accepted.extensions: array of supported extensions.authentication: one of{"none", "builtin"}wherenoneis for anonymous access andbuiltinfor OpenWhisk authentication.
As in -a web-export '{ "methods": ["get", "post"], "extensions": ["http"], "authentication": "none" }' for a web action that only accepts get and post requests, handled .http extensions only, and permits anonymous access. A richer set of annotations will allow the controller to reject requests early if a web action does not support a particular method for example. The current implementation will accept get, post, put and delete HTTP methods without discrimination for any web action.
File: docs/dev/modules.md
Modules
Main
| Module | Description | Module Status |
|--- |--- |--- |
| openwhisk | Apache OpenWhisk is an open source serverless cloud platform | |
| openwhisk-apigateway | Apache OpenWhisk API Gateway service for exposing actions as REST interfaces. | |
| openwhisk-catalog | Curated catalog of Apache OpenWhisk packages to interface with event producers and consumers | |
| openwhisk-cli | Apache OpenWhisk Command Line Interface (CLI) | |
| openwhisk-composer | Apache OpenWhisk Composer provides a high-level programming model in JavaScript for composing serverless functions | |
| openwhisk-composer-python | Apache OpenWhisk Composer Python provides a high-level programming model in Python for composing serverless functions | |
| openwhisk-wskdeploy | Apache OpenWhisk utility for deploying and managing OpenWhisk projects and packages | |
Clients
| Module | Description | Module Status |
|--- |--- |--- |
| openwhisk-client-go | Go client library for the Apache OpenWhisk platform | |
| openwhisk-client-js | JavaScript client library for the Apache OpenWhisk platform | |
Runtimes
| Module | Description | Module Status |
|--- |--- |--- |
| openwhisk-runtime-deno | Apache OpenWhisk Runtime Deno supports Apache OpenWhisk functions written in Deno | |
| openwhisk-runtime-docker | Apache OpenWhisk SDK for building Docker "blackbox" runtimes | |
| openwhisk-runtime-dotnet | Apache OpenWhisk Runtime .Net supports Apache OpenWhisk functions written in .Net languages | |
| openwhisk-runtime-go | Apache OpenWhisk Runtime Go supports Apache OpenWhisk functions written in Go | |
| openwhisk-runtime-java | Apache OpenWhisk Runtime Java supports Apache OpenWhisk functions written in Java and other JVM-hosted languages | |
| openwhisk-runtime-nodejs | Apache OpenWhisk Runtime NodeJS supports Apache OpenWhisk functions written in JavaScript for NodeJS | |
| openwhisk-runtime-php | Apache OpenWhisk Runtime PHP supports Apache OpenWhisk functions written in PHP | |
| openwhisk-runtime-python | Apache OpenWhisk Runtime Python supports Apache OpenWhisk functions written in Python | |
| openwhisk-runtime-ruby | Apache OpenWhisk Runtime Ruby supports Apache OpenWhisk functions written in Ruby | |
| openwhisk-runtime-rust | Apache OpenWhisk Runtime Rust supports Apache OpenWhisk functions written in Rust | |
| openwhisk-runtime-swift | Apache OpenWhisk Runtime Swift supports Apache OpenWhisk functions written in Swift | |
Deployments
| Module | Description | Module Status |
|--- |--- |--- |
| openwhisk-deploy-kube | The Apache OpenWhisk Kubernetes Deployment repository supports deploying the Apache OpenWhisk system on Kubernetes and OpenShift clusters. | |
Packages
| Module | Description | Module Status |
|--- |--- |--- |
| openwhisk-package-alarms | Apache OpenWhisk package that can be used to create periodic, time-based alarms. | |
| openwhisk-package-cloudant | The Apache OpenWhisk cloudant package enables you to work with a Cloudant/CouchDB database | |
| openwhisk-package-deploy | Apache openwhisk | |
| openwhisk-package-kafka | Apache OpenWhisk package for communicating with Kafka or Message Hub | |
| openwhisk-package-pushnotifications | OpenWhisk Package for Bluemix Push Notifications Service | |
| openwhisk-pluggable-provider | Apache OpenWhisk pluggable trigger feed event provider | |
Samples and Examples
| Module | Description |
|--- |--- |
| openwhisk-slackinvite | Invite for Apache OpenWhisk Team on Slack |
Development Tools
| Module | Description |
|--- |--- |
| openwhisk-devtools | Development tools for building and deploying Apache OpenWhisk |
| openwhisk-intellij-plugin | Intellij plugin for Apache OpenWhisk |
| openwhisk-vscode-extension | VSCode extension for Apache OpenWhisk |
| openwhisk-wskdebug | Debugging and live development tool for Apache OpenWhisk |
Utilities
| Module | Description |
|--- |--- |
| openwhisk-release | Tools and documentation for Apache OpenWhisk Release Managers |
| openwhisk-utilities | Shared utilities used across Apache OpenWhisk project repositories. |
Others
| Module | Description |
|--- |--- |
| openwhisk-test | Test repo. for Apache OpenWhisk client-side tooling. |
| openwhisk-website | Apache OpenWhisk website (openwhisk.apache.org) content; built using Jekyll |
Archived
| Module | Description |
|--- |--- |
| openwhisk-GitHubSlackBot | [DEPRECATED] - Demonstration of integration of GitHub Pull Request management with Slack and using Alarms |
| openwhisk-client-python | [DEPRECATED] - REST API of OpenWhisk can be used directly from Python |
| openwhisk-client-swift | [DEPRECATED] - openwhisk-client-swift is a Swift client SDK for OpenWhisk with support for iOS, WatchOS2, and Darwin CLI apps |
| openwhisk-debugger | [DEPRECATED] - The OpenWhisk debugger project |
| openwhisk-deploy-mesos | Apache OpenWhisk deployment scripts and configuration files for running under Apache Mesos. |
| openwhisk-deploy-openshift | [DEPRECATED] - This project can be used to deploy Apache OpenWhisk to the OpenShift platform |
| openwhisk-external-resources | โจ Curated list of awesome OpenWhisk things โจ |
| openwhisk-package-jira | [DEPRECATED] - Interact with JIRA software software development tool used for issue tracking, and project management functions |
| openwhisk-package-rss | [DEPRECATED] - RSS feed package |
| openwhisk-package-template | [DEPRECATED] - This is a template to be use when creating new packages for OpenWhisk |
| openwhisk-playground | [DEPRECATED] - This library provides functionality of executing a snippet of source code as OpenWhisk action for OpenWhisk Xcode Source Editor Extension |
| openwhisk-podspecs | [DEPRECATED] - CocoaPods Podspecs repo for openwhisk-client-swift |
| openwhisk-runtime-ballerina | Apache OpenWhisk Runtime Ballerina supports Apache OpenWhisk functions written in Ballerina |
| openwhisk-sample-matos | [DEPRECATED] - sample application with Message Hub and Object Store |
| openwhisk-sample-slackbot | [DEPRECATED] - A proof-of-concept Slackbot to invoke OpenWhisk actions. |
| openwhisk-selfserve-test | [DEPRECATED] - Apache openwhisk |
| openwhisk-tutorial | [DEPRECATED] - An interactive learning environment for the Apache OpenWhisk command line |
| openwhisk-vscode | [DEPRECATED] - Visual Studio Code extension (prototype) for authoring OpenWhisk actions inside the editor. |
| openwhisk-workshop | [DEPRECATED] - OpenWhisk workshop to help developers learn how to build serverless applications using the platform. |
| openwhisk-xcode | [DEPRECATED] - Collection of OpenWhisk tools for OS X implemented in Swift 3. |
File: docs/images/README.md
This folder contains approved OpenWhisk logo images bearing the Trademark (TM) symbol that indicates that the OpenWhisk project has been granted (or has filed for) a trademark for the OpenWhisk name and/or logo in various geographies.
2. Official Technical Reference & Guides (apache/openwhisk-website)
Apache OpenWhisk Website
Apache OpenWhisk is a cloud-first distributed event-based programming service. It provides a programming model to upload event handlers to a cloud service, and register the handlers to respond to various events.
Quickstart for Local Development
Install Prerequisites
- Download and install Node.js (recommend v12 LTS): see https://nodejs.org/
- Download and install NPM (recommend v6.14 latest): see https://www.npmjs.com/package/npm
- Download and install Ruby (recommend v2.7.2 stable): see https://www.ruby-lang.org/en/documentation/installation/
Note Currently, the website's Jekyll version will not compile with Ruby version 3 or higher.
- If you need to manage multiple Ruby versions, you may use https://github.com/rbenv/rbenv. See Using rbenv below.
# Verify Node and Node Package Manager are installed
node -v
npm -v
# Verify Ruby is installed
ruby -v
# Install Jekyll, which is used to build the site
gem install jekyll bundlerClone and start development server
# Clone the repository (and correct branch).
git clone https://github.com/apache/openwhisk-website.git
# Move into the cloned repo.
cd openwhisk-website
# Install gem dependencies
bundle install
# Start the development server default location http://127.0.0.1:4000
bin/developValidate HTML, links and more
To make sure nothing is broken on the website we use html-proofer,
it checks to see if any of the links 404s, if the HTML is valid, images have alt tags and more.
Run the following command to run html-proofer:
bin/testHow to Build Jekyll to Output Static Files
If you need to generate static files, run the following:
# NOTE: Make sure you have already cloned the repository and moved into the directory.
# Build the site.
bundle exec jekyll buildThis will build static files to a directory called _site/ in the project root. This can be used for manual deployment to a static server or with build tools like Jenkins.
Updating CSS files
Some browsers will continue to use cached copies of ".css" files even though the styles/content in the files has changed significantly. If you make any changes that affect layout, you SHOULD increment the (artificial) version number on the query parameter on the <link> within default.html which will cause (force) some clients to pull down a new main.css file (and all the cascading CSS imports as well):
<link rel="stylesheet" href="{{ site.github.url }}/css/main-v1.css?v=1.12">for example, update "?v1.12" to "?v1.13".
<link rel="stylesheet" href="{{ site.github.url }}/css/main-v1.css?v=1.13">Using `rbenv`
If you need to maintain your existing system Ruby version, you can use the https://github.com/rbenv/rbenv utility to dynamically switch to the version this project requires.
The project already has a .ruby_version file which indicates it requires Ruby 2.7.2. All you need do is assure once rbenv is installed that you issue the following commands (one time) from the bash shell you wish to compile the project:
$ rbenv init
$ rbenv install 2.7.2
$ rbenv global 2.7.2
$ eval "$(rbenv init -)"When opening subsequent bash terminals, you need only issue the following command (as rbenv init has updated your system .bash_profile):
eval "$(rbenv init -)"You can verify the version changed by using:
$ rbenv global
$ ruby -vTroubleshooting
If you get an error 'bundle: command not found' attempting to build the site, you may need to manually install the 'bundler' package:
gem install bundlerJenkins build
The OpenWhisk.org website is automatically built and published to Apache at:
https://openwhisk.apache.org/
Current Jenkins build status can be checked here:
https://builds.apache.org/job/OpenWhisk-website/
Issues
Report website bugs, ask questions and request features here on GitHub.
You can also join our slack domain and chat with developers. Please visit here to sign up:
https://openwhisk.apache.org/slack.html
License
Licensed under the Apache License, Version 2.0 (the "License").
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.