Compare commits

..

1 Commits

Author SHA1 Message Date
48716e2738 Add user support for custom containers
Custom containers in the compose file can have a directory
mounted to store state for things like a database. This requires
a specific user since influxdb/postgresql and other containers
create folders and update permissions on start-up.

Tested with influxdb on Ubuntu with userid 1000, which failed
before the change.

Adds a grace period in the e2e tests.

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2021-01-04 09:45:34 +00:00
38 changed files with 633 additions and 1156 deletions

View File

@ -8,19 +8,10 @@
<!--- If describing a bug, tell us what happens instead of the expected behavior --> <!--- If describing a bug, tell us what happens instead of the expected behavior -->
<!--- If suggesting a change/improvement, explain the difference from current behavior --> <!--- If suggesting a change/improvement, explain the difference from current behavior -->
## Are you a GitHub Sponsor (Yes/No?) ## Possible Solution
Check at: https://github.com/sponsors/openfaas
- [ ] Yes
- [ ] No
## List all Possible Solutions
<!--- Not obligatory, but suggest a fix/reason for the bug, --> <!--- Not obligatory, but suggest a fix/reason for the bug, -->
<!--- or ideas how to implement the addition or change --> <!--- or ideas how to implement the addition or change -->
## List the one solution that you would recommend
<!--- If you were to be on the hook for this change. -->
## Steps to Reproduce (for bugs) ## Steps to Reproduce (for bugs)
<!--- Provide a link to a live example, or an unambiguous set of steps to --> <!--- Provide a link to a live example, or an unambiguous set of steps to -->
<!--- reproduce this bug. Include code to reproduce, if relevant --> <!--- reproduce this bug. Include code to reproduce, if relevant -->
@ -47,6 +38,4 @@ containerd -version
uname -a uname -a
cat /etc/os-release cat /etc/os-release
faasd version
``` ```

View File

@ -12,7 +12,7 @@ jobs:
GO111MODULE: off GO111MODULE: off
strategy: strategy:
matrix: matrix:
go-version: [1.15.x] go-version: [1.13.x]
os: [ubuntu-latest] os: [ubuntu-latest]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:

View File

@ -9,7 +9,7 @@ jobs:
publish: publish:
strategy: strategy:
matrix: matrix:
go-version: [ 1.15.x ] go-version: [ 1.13.x ]
os: [ ubuntu-latest ] os: [ ubuntu-latest ]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:

View File

@ -63,6 +63,4 @@ test-e2e:
/usr/local/bin/faas-cli list /usr/local/bin/faas-cli list
sleep 3 sleep 3
journalctl -t openfaas-fn:figlet --no-pager journalctl -t openfaas-fn:figlet --no-pager
/usr/local/bin/faas-cli logs figlet --since 15m --follow=false | grep Forking
# Removed due to timing issue in CI on GitHub Actions
# /usr/local/bin/faas-cli logs figlet --since 15m --follow=false | grep Forking

382
README.md
View File

@ -7,50 +7,195 @@
faasd is [OpenFaaS](https://github.com/openfaas/) reimagined, but without the cost and complexity of Kubernetes. It runs on a single host with very modest requirements, making it fast and easy to manage. Under the hood it uses [containerd](https://containerd.io/) and [Container Networking Interface (CNI)](https://github.com/containernetworking/cni) along with the same core OpenFaaS components from the main project. faasd is [OpenFaaS](https://github.com/openfaas/) reimagined, but without the cost and complexity of Kubernetes. It runs on a single host with very modest requirements, making it fast and easy to manage. Under the hood it uses [containerd](https://containerd.io/) and [Container Networking Interface (CNI)](https://github.com/containernetworking/cni) along with the same core OpenFaaS components from the main project.
![faasd logo](docs/media/social.png) ## When should you use faasd over OpenFaaS on Kubernetes?
## Use-cases and tutorials * You have a cost sensitive project - run faasd on a 5-10 USD VPS or on your Raspberry Pi
* When you just need a few functions or microservices, without the cost of a cluster
* When you don't have the bandwidth to learn or manage Kubernetes
* To deploy embedded apps in IoT and edge use-cases
* To shrink-wrap applications for use with a customer or client
faasd is just another way to run OpenFaaS, so many things you read in the docs or in blog posts will work the same way. faasd does not create the same maintenance burden you'll find with maintaining, upgrading, and securing a Kubernetes cluster. You can deploy it and walk away, in the worst case, just deploy a new VM and deploy your functions again.
Videos and overviews: ## About faasd
* [Exploring of serverless use-cases from commercial and personal users (YouTube)](https://www.youtube.com/watch?v=mzuXVuccaqI) * is a single Golang binary
* [Meet faasd. Look Ma No Kubernetes! (YouTube)](https://www.youtube.com/watch?v=ZnZJXI377ak) * uses the same core components and ecosystem of OpenFaaS
* is multi-arch, so works on Intel `x86_64` and ARM out the box
* can be set-up and left alone to run your applications
![demo](https://pbs.twimg.com/media/EPNQz00W4AEwDxM?format=jpg&name=small)
> Demo of faasd running in KVM
## Try faasd for the first time
faasd is OpenFaaS, so many things you read in the docs or in blog posts will work the same way.
Use-cases and tutorials: Use-cases and tutorials:
* [Serverless Node.js that you can run anywhere](https://www.openfaas.com/blog/serverless-nodejs/) * [Deploy via GitHub Actions](https://www.openfaas.com/blog/openfaas-functions-with-github-actions/)
* [Simple Serverless with Golang Functions and Microservices](https://www.openfaas.com/blog/golang-serverless/)
* [Build a Flask microservice with OpenFaaS](https://www.openfaas.com/blog/openfaas-flask/)
* [Get started with Java 11 and Vert.x on Kubernetes with OpenFaaS](https://www.openfaas.com/blog/get-started-with-java-openjdk11/)
* [Deploy to faasd via GitHub Actions](https://www.openfaas.com/blog/openfaas-functions-with-github-actions/)
* [Scrape and automate websites with Puppeteer](https://www.openfaas.com/blog/puppeteer-scraping/) * [Scrape and automate websites with Puppeteer](https://www.openfaas.com/blog/puppeteer-scraping/)
* [Serverless Node.js that you can run anywhere](https://www.openfaas.com/blog/serverless-nodejs/)
* [Build a Flask microservice with OpenFaaS](https://www.openfaas.com/blog/openfaas-flask/)
Additional resources: Additional resources:
* The official handbook - [Serverless For Everyone Else](https://gumroad.com/l/serverless-for-everyone-else)
* For reference: [OpenFaaS docs](https://docs.openfaas.com) * For reference: [OpenFaaS docs](https://docs.openfaas.com)
* For use-cases and tutorials: [OpenFaaS blog](https://openfaas.com/blog/) * For use-cases and tutorials: [OpenFaaS blog](https://openfaas.com/blog/)
* For self-paced learning: [OpenFaaS workshop](https://github.com/openfaas/workshop/) * For self-paced learning: [OpenFaaS workshop](https://github.com/openfaas/workshop/)
### About faasd ## Deploy faasd
* faasd is a static Golang binary The easiest way to deploy faasd is with cloud-init, we give several examples below, and post IaaS platforms will accept "user-data" pasted into their UI, or via their API.
* uses the same core components and ecosystem of OpenFaaS
* uses containerd for its runtime and CNI for networking
* is multi-arch, so works on Intel `x86_64` and ARM out the box
* can run almost any other stateful container through its `docker-compose.yaml` file
Most importantly, it's easy to manage so you can set it up and leave it alone to run your functions. If you don't use cloud-init, or have already created your Linux server you can use the installation script. This approach also works for Raspberry Pi:
![demo](https://pbs.twimg.com/media/EPNQz00W4AEwDxM?format=jpg&name=small) ```bash
git clone https://github.com/openfaas/faasd
cd faasd
> Demo of faasd running asynchronous functions ./hack/install.sh
```
Watch the video: [faasd walk-through with cloud-init and Multipass](https://www.youtube.com/watch?v=WX1tZoSXy8E) For trying out fasad on MacOS or Windows, we recommend using multipass and its cloud-init option.
### What does faasd deploy? ### Run locally on MacOS, Linux, or Windows with multipass
* [Get up and running with your own faasd installation on your Mac/Ubuntu or Windows with cloud-config](/docs/MULTIPASS.md)
### DigitalOcean tutorial with Terraform and TLS
The terraform can be adapted for any IaaS provider:
* [Bring a lightweight Serverless experience to DigitalOcean with Terraform and faasd](https://www.openfaas.com/blog/faasd-tls-terraform/)
See also: [Build a Serverless appliance with faasd and cloud-init](https://blog.alexellis.io/deploy-serverless-faasd-with-cloud-init/)
### Get started on armhf / Raspberry Pi
You can run this tutorial on your Raspberry Pi, or adapt the steps for a regular Linux VM/VPS host.
* [faasd - lightweight Serverless for your Raspberry Pi](https://blog.alexellis.io/faasd-for-lightweight-serverless/)
### Terraform for DigitalOcean
Automate everything within < 60 seconds and get a public URL and IP address back. Customise as required, or adapt to your preferred cloud such as AWS EC2.
* [Provision faasd 0.9.10 on DigitalOcean with Terraform 0.12.0](docs/bootstrap/README.md)
* [Provision faasd on DigitalOcean with built-in TLS support](docs/bootstrap/digitalocean-terraform/README.md)
## Operational concerns
### A note on private repos / registries
To use private image repos, `~/.docker/config.json` needs to be copied to `/var/lib/faasd/.docker/config.json`.
If you'd like to set up your own private registry, [see this tutorial](https://blog.alexellis.io/get-a-tls-enabled-docker-registry-in-5-minutes/).
Beware that running `docker login` on MacOS and Windows may create an empty file with your credentials stored in the system helper.
Alternatively, use you can use the `registry-login` command from the OpenFaaS Cloud bootstrap tool (ofc-bootstrap):
```bash
curl -sLSf https://raw.githubusercontent.com/openfaas-incubator/ofc-bootstrap/master/get.sh | sudo sh
ofc-bootstrap registry-login --username <your-registry-username> --password-stdin
# (the enter your password and hit return)
```
The file will be created in `./credentials/`
> Note for the GitHub container registry, you should use `ghcr.io` Container Registry and not the previous generation of "Docker Package Registry". [See notes on migrating](https://docs.github.com/en/free-pro-team@latest/packages/getting-started-with-github-container-registry/migrating-to-github-container-registry-for-docker-images)
### Logs for functions
You can view the logs of functions using `journalctl`:
```bash
journalctl -t openfaas-fn:FUNCTION_NAME
faas-cli store deploy figlet
journalctl -t openfaas-fn:figlet -f &
echo logs | faas-cli invoke figlet
```
### Logs for the core services
Core services as defined in the docker-compose.yaml file are deployed as containers by faasd.
View the logs for a component by giving its NAME:
```bash
journalctl -t default:NAME
journalctl -t default:gateway
journalctl -t default:queue-worker
```
You can also use `-f` to follow the logs, or `--lines` to tail a number of lines, or `--since` to give a timeframe.
### Exposing core services
The OpenFaaS stack is made up of several core services including NATS and Prometheus. You can expose these through the `docker-compose.yaml` file located at `/var/lib/faasd`.
Expose the gateway to all adapters:
```yaml
gateway:
ports:
- "8080:8080"
```
Expose Prometheus only to 127.0.0.1:
```yaml
prometheus:
ports:
- "127.0.0.1:9090:9090"
```
### Upgrading faasd
To upgrade `faasd` either re-create your VM using Terraform, or simply replace the faasd binary with a newer one.
```bash
systemctl stop faasd-provider
systemctl stop faasd
# Replace /usr/local/bin/faasd with the desired release
# Replace /var/lib/faasd/docker-compose.yaml with the matching version for
# that release.
# Remember to keep any custom patches you make such as exposing additional
# ports, or updating timeout values
systemctl start faasd
systemctl start faasd-provider
```
You could also perform this task over SSH, or use a configuration management tool.
> Note: if you are using Caddy or Let's Encrypt for free SSL certificates, that you may hit rate-limits for generating new certificates if you do this too often within a given week.
### Memory limits for functions
Memory limits for functions are supported. When the limit is exceeded the function will be killed.
Example:
```yaml
functions:
figlet:
skip_build: true
image: functions/figlet:latest
limits:
memory: 20Mi
```
## What does faasd deploy?
* faasd - itself, and its [faas-provider](https://github.com/openfaas/faas-provider) for containerd - CRUD for functions and services, implements the OpenFaaS REST API * faasd - itself, and its [faas-provider](https://github.com/openfaas/faas-provider) for containerd - CRUD for functions and services, implements the OpenFaaS REST API
* [Prometheus](https://github.com/prometheus/prometheus) - for monitoring of services, metrics, scaling and dashboards * [Prometheus](https://github.com/prometheus/prometheus) - for monitoring of services, metrics, scaling and dashboards
@ -58,7 +203,7 @@ Watch the video: [faasd walk-through with cloud-init and Multipass](https://www.
* [OpenFaaS queue-worker for NATS](https://github.com/openfaas/nats-queue-worker) - run your invocations in the background without adding any code. See also: [asynchronous invocations](https://docs.openfaas.com/reference/triggers/#async-nats-streaming) * [OpenFaaS queue-worker for NATS](https://github.com/openfaas/nats-queue-worker) - run your invocations in the background without adding any code. See also: [asynchronous invocations](https://docs.openfaas.com/reference/triggers/#async-nats-streaming)
* [NATS](https://nats.io) for asynchronous processing and queues * [NATS](https://nats.io) for asynchronous processing and queues
faasd relies on industry-standard tools for running containers: You'll also need:
* [CNI](https://github.com/containernetworking/plugins) * [CNI](https://github.com/containernetworking/plugins)
* [containerd](https://github.com/containerd/containerd) * [containerd](https://github.com/containerd/containerd)
@ -66,84 +211,15 @@ faasd relies on industry-standard tools for running containers:
You can use the standard [faas-cli](https://github.com/openfaas/faas-cli) along with pre-packaged functions from *the Function Store*, or build your own using any OpenFaaS template. You can use the standard [faas-cli](https://github.com/openfaas/faas-cli) along with pre-packaged functions from *the Function Store*, or build your own using any OpenFaaS template.
### When should you use faasd over OpenFaaS on Kubernetes? ### Manual / developer instructions
* To deploy microservices and functions that you can update and monitor remotely See [here for manual / developer instructions](docs/DEV.md)
* When you don't have the bandwidth to learn or manage Kubernetes
* To deploy embedded apps in IoT and edge use-cases
* To distribute applications to a customer or client
* You have a cost sensitive project - run faasd on a 1GB VM for 5-10 USD / mo or on your Raspberry Pi
* When you just need a few functions or microservices, without the cost of a cluster
faasd does not create the same maintenance burden you'll find with maintaining, upgrading, and securing a Kubernetes cluster. You can deploy it and walk away, in the worst case, just deploy a new VM and deploy your functions again. ## Getting help
## Learning faasd ### Docs
The faasd project is MIT licensed and open source, and you will find some documentation, blog posts and videos for free. The [OpenFaaS docs](https://docs.openfaas.com/) provide a wealth of information and are kept up to date with new features.
However, "Serverless For Everyone Else" is the official handbook and was written to contribute funds towards the upkeep and maintenance of the project.
### The official handbook and docs for faasd
<a href="https://gumroad.com/l/serverless-for-everyone-else">
<img src="https://www.alexellis.io/serverless.png" width="40%"></a>
You'll learn how to deploy code in any language, lift and shift Dockerfiles, run requests in queues, write background jobs and to integrate with databases. faasd packages the same code as OpenFaaS, so you get built-in metrics for your HTTP endpoints, a user-friendly CLI, pre-packaged functions and templates from the store and a UI.
Topics include:
* Should you deploy to a VPS or Raspberry Pi?
* Deploying your server with bash, cloud-init or terraform
* Using a private container registry
* Finding functions in the store
* Building your first function with Node.js
* Using environment variables for configuration
* Using secrets from functions, and enabling authentication tokens
* Customising templates
* Monitoring your functions with Grafana and Prometheus
* Scheduling invocations and background jobs
* Tuning timeouts, parallelism, running tasks in the background
* Adding TLS to faasd and custom domains for functions
* Self-hosting on your Raspberry Pi
* Adding a database for storage with InfluxDB and Postgresql
* Troubleshooting and logs
* CI/CD with GitHub Actions and multi-arch
* Taking things further, community and case-studies
View sample pages, reviews and testimonials on Gumroad:
["Serverless For Everyone Else"](https://gumroad.com/l/serverless-for-everyone-else)
### Deploy faasd
The easiest way to deploy faasd is with cloud-init, we give several examples below, and post IaaS platforms will accept "user-data" pasted into their UI, or via their API.
For trying it out on MacOS or Windows, we recommend using [multipass](https://multipass.run) to run faasd in a VM.
If you don't use cloud-init, or have already created your Linux server you can use the installation script as per below:
```bash
git clone https://github.com/openfaas/faasd --depth=1
cd faasd
./hack/install.sh
```
> This approach also works for Raspberry Pi
It's recommended that you do not install Docker on the same host as faasd, since 1) they may both use different versions of containerd and 2) docker's networking rules can disrupt faasd's networking. When using faasd - make your faasd server a faasd server, and build container image on your laptop or in a CI pipeline.
#### Deployment tutorials
* [Use multipass on Windows, MacOS or Linux](/docs/MULTIPASS.md)
* [Deploy to DigitalOcean with Terraform and TLS](https://www.openfaas.com/blog/faasd-tls-terraform/)
* [Deploy to any IaaS with cloud-init](https://blog.alexellis.io/deploy-serverless-faasd-with-cloud-init/)
* [Deploy faasd to your Raspberry Pi](https://blog.alexellis.io/faasd-for-lightweight-serverless/)
Terraform scripts:
* [Provision faasd on DigitalOcean with Terraform](docs/bootstrap/README.md)
* [Provision faasd with TLS on DigitalOcean with Terraform](docs/bootstrap/digitalocean-terraform/README.md)
### Function and template store ### Function and template store
@ -151,19 +227,113 @@ For community functions see `faas-cli store --help`
For templates built by the community see: `faas-cli template store list`, you can also use the `dockerfile` template if you just want to migrate an existing service without the benefits of using a template. For templates built by the community see: `faas-cli template store list`, you can also use the `dockerfile` template if you just want to migrate an existing service without the benefits of using a template.
### Training and courses
#### LinuxFoundation training course
The founder of faasd and OpenFaaS has written a training course for the LinuxFoundation which also covers how to use OpenFaaS on Kubernetes. Much of the same concepts can be applied to faasd, and the course is free:
* [Introduction to Serverless on Kubernetes](https://www.edx.org/course/introduction-to-serverless-on-kubernetes)
#### Community workshop
[The OpenFaaS workshop](https://github.com/openfaas/workshop/) is a set of 12 self-paced labs and provides a great starting point for learning the features of openfaas. Not all features will be available or usable with faasd.
### Community support ### Community support
Commercial users and solo business owners should become OpenFaaS GitHub Sponsors to receive regular email updates on changes, tutorials and new features. An active community of almost 3000 users awaits you on Slack. Over 250 of those users are also contributors and help maintain the code.
If you are learning faasd, or want to share your use-case, you can join the OpenFaaS Slack community.
* [Become an OpenFaaS GitHub Sponsor](https://github.com/sponsors/openfaas/)
* [Join Slack](https://slack.openfaas.io/) * [Join Slack](https://slack.openfaas.io/)
### Backlog, features and known issues ## Roadmap
For completed features, WIP and upcoming roadmap see: ### Supported operations
See [ROADMAP.md](docs/ROADMAP.md) * `faas login`
* `faas up`
* `faas list`
* `faas describe`
* `faas deploy --update=true --replace=false`
* `faas invoke --async`
* `faas invoke`
* `faas rm`
* `faas store list/deploy/inspect`
* `faas version`
* `faas namespace`
* `faas secret`
* `faas logs`
Are you looking to hack on faasd? Follow the [developer instructions](docs/DEV.md) for a manual installation, or use the `hack/install.sh` script and pick up from there. Scale from and to zero is also supported. On a Dell XPS with a small, pre-pulled image unpausing an existing task took 0.19s and starting a task for a killed function took 0.39s. There may be further optimizations to be gained.
Other operations are pending development in the provider such as:
* `faas auth` - supported for Basic Authentication, but SSO, OAuth2 & OIDC may require a patch
### Backlog
Should have:
* [ ] Resolve core services from functions by populating/sharing `/etc/hosts` between `faasd` and `faasd-provider`
* [ ] Docs or examples on how to use the various connectors and connector-sdk
* [ ] Monitor and restart any of the core components at runtime if the container stops
* [ ] Asynchronous deletion instead of synchronous
Nice to Have:
* [ ] Total memory limits - if a node has 1GB of RAM, don't allow more than 1000MB of RAM to be reserved via limits
* [ ] Offer live rolling-updates, with zero downtime - requires moving to IDs vs. names for function containers
* [ ] Multiple replicas per function
### Known-issues
#### Non 200 HTTP status code upon first use
This issue appears to happen sporadically and only for some users.
If you get a non 200 HTTP code from the gateway, or caddy after installing faasd, check the logs of faasd:
```bash
sudo journalctl -t faasd
```
If you see the following error:
```
unable to dial to 10.62.0.5:8080, error: dial tcp 10.62.0.5:8080: connect: no route to host
```
Restart the faasd service with:
```bash
sudo systemctl restart faasd
```
### Completed
* [x] Provide a cloud-init configuration for faasd bootstrap
* [x] Configure core services from a docker-compose.yaml file
* [x] Store and fetch logs from the journal
* [x] Add support for using container images in third-party public registries
* [x] Add support for using container images in private third-party registries
* [x] Provide a cloud-config.txt file for automated deployments of `faasd`
* [x] Inject / manage IPs between core components for service to service communication - i.e. so Prometheus can scrape the OpenFaaS gateway - done via `/etc/hosts` mount
* [x] Add queue-worker and NATS
* [x] Create faasd.service and faasd-provider.service
* [x] Self-install / create systemd service via `faasd install`
* [x] Restart containers upon restart of faasd
* [x] Clear / remove containers and tasks with SIGTERM / SIGINT
* [x] Determine armhf/arm64 containers to run for gateway
* [x] Configure `basic_auth` to protect the OpenFaaS gateway and faasd-provider HTTP API
* [x] Setup custom working directory for faasd `/var/lib/faasd/`
* [x] Use CNI to create network namespaces and adapters
* [x] Optionally expose core services from the docker-compose.yaml file, locally or to all adapters.
* [x] ~~[containerd can't pull image from Github Docker Package Registry](https://github.com/containerd/containerd/issues/3291)~~ ghcr.io support
* [x] Provide [simple Caddyfile example](https://blog.alexellis.io/https-inlets-local-endpoints/) in the README showing how to expose the faasd proxy on port 80/443 with TLS
* [x] Annotation support
* [x] Hard memory limits for functions
* [x] Terraform for DigitalOcean
* [x] [Store and retrieve annotations in function spec](https://github.com/openfaas/faasd/pull/86) - in progress
* [x] An installer for faasd and dependencies - runc, containerd
WIP:
* [ ] Terraform for AWS

View File

@ -7,7 +7,6 @@ package_update: true
packages: packages:
- runc - runc
- git
runcmd: runcmd:
- curl -sLSf https://github.com/containerd/containerd/releases/download/v1.3.5/containerd-1.3.5-linux-amd64.tar.gz > /tmp/containerd.tar.gz && tar -xvf /tmp/containerd.tar.gz -C /usr/local/bin/ --strip-components=1 - curl -sLSf https://github.com/containerd/containerd/releases/download/v1.3.5/containerd-1.3.5-linux-amd64.tar.gz > /tmp/containerd.tar.gz && tar -xvf /tmp/containerd.tar.gz -C /usr/local/bin/ --strip-components=1
@ -18,8 +17,8 @@ runcmd:
- mkdir -p /opt/cni/bin - mkdir -p /opt/cni/bin
- curl -sSL https://github.com/containernetworking/plugins/releases/download/v0.8.5/cni-plugins-linux-amd64-v0.8.5.tgz | tar -xz -C /opt/cni/bin - curl -sSL https://github.com/containernetworking/plugins/releases/download/v0.8.5/cni-plugins-linux-amd64-v0.8.5.tgz | tar -xz -C /opt/cni/bin
- mkdir -p /go/src/github.com/openfaas/ - mkdir -p /go/src/github.com/openfaas/
- cd /go/src/github.com/openfaas/ && git clone --depth 1 --branch 0.11.0 https://github.com/openfaas/faasd - cd /go/src/github.com/openfaas/ && git clone --depth 1 --branch 0.9.10 https://github.com/openfaas/faasd
- curl -fSLs "https://github.com/openfaas/faasd/releases/download/0.11.0/faasd" --output "/usr/local/bin/faasd" && chmod a+x "/usr/local/bin/faasd" - curl -fSLs "https://github.com/openfaas/faasd/releases/download/0.9.10/faasd" --output "/usr/local/bin/faasd" && chmod a+x "/usr/local/bin/faasd"
- cd /go/src/github.com/openfaas/faasd/ && /usr/local/bin/faasd install - cd /go/src/github.com/openfaas/faasd/ && /usr/local/bin/faasd install
- systemctl status -l containerd --no-pager - systemctl status -l containerd --no-pager
- journalctl -u faasd-provider --no-pager - journalctl -u faasd-provider --no-pager

View File

@ -93,10 +93,7 @@ func runInstall(_ *cobra.Command, _ []string) error {
return err return err
} }
fmt.Println(`Check status with: fmt.Println(`Login with:
sudo journalctl -u faasd --lines 100 -f
Login with:
sudo cat /var/lib/faasd/secrets/basic-auth-password | faas-cli login -s`) sudo cat /var/lib/faasd/secrets/basic-auth-password | faas-cli login -s`)
return nil return nil

View File

@ -46,12 +46,7 @@ var rootCommand = &cobra.Command{
Use: "faasd", Use: "faasd",
Short: "Start faasd", Short: "Start faasd",
Long: ` Long: `
faasd - Serverless For Everyone Else faasd - serverless without Kubernetes
Learn how to build, secure, and monitor functions with faasd with
the eBook:
https://gumroad.com/l/serverless-for-everyone-else
`, `,
RunE: runRootCommand, RunE: runRootCommand,
SilenceUsage: true, SilenceUsage: true,

View File

@ -1,7 +1,7 @@
version: "3.7" version: "3.7"
services: services:
basic-auth-plugin: basic-auth-plugin:
image: ghcr.io/openfaas/basic-auth:0.21.0 image: "docker.io/openfaas/basic-auth-plugin:0.18.18${ARCH_SUFFIX}"
environment: environment:
- port=8080 - port=8080
- secret_mount_path=/run/secrets - secret_mount_path=/run/secrets
@ -19,7 +19,7 @@ services:
- CAP_NET_RAW - CAP_NET_RAW
nats: nats:
image: docker.io/library/nats-streaming:0.22.0 image: docker.io/library/nats-streaming:0.11.2
command: command:
- "/nats-streaming-server" - "/nats-streaming-server"
- "-m" - "-m"
@ -41,7 +41,7 @@ services:
- "127.0.0.1:9090:9090" - "127.0.0.1:9090:9090"
gateway: gateway:
image: ghcr.io/openfaas/gateway:0.21.0 image: "docker.io/openfaas/gateway:0.19.1${ARCH_SUFFIX}"
environment: environment:
- basic_auth=true - basic_auth=true
- functions_provider_url=http://faasd-provider:8081/ - functions_provider_url=http://faasd-provider:8081/
@ -55,7 +55,6 @@ services:
- auth_proxy_pass_body=false - auth_proxy_pass_body=false
- secret_mount_path=/run/secrets - secret_mount_path=/run/secrets
- scale_from_zero=true - scale_from_zero=true
- function_namespace=openfaas-fn
volumes: volumes:
# we assume cwd == /var/lib/faasd # we assume cwd == /var/lib/faasd
- type: bind - type: bind
@ -74,7 +73,7 @@ services:
- "8080:8080" - "8080:8080"
queue-worker: queue-worker:
image: ghcr.io/openfaas/queue-worker:0.12.2 image: docker.io/openfaas/queue-worker:0.11.2
environment: environment:
- faas_nats_address=nats - faas_nats_address=nats
- faas_nats_port=4222 - faas_nats_port=4222

View File

@ -1,11 +1,9 @@
## Instructions for hacking on faasd itself ## Manual installation of faasd for development
> Note: if you're just wanting to try out faasd, then it's likely that you're on the wrong page. This is a detailed set of instructions for those wanting to contribute or customise faasd. Feel free to go back to the homepage and pick a tutorial instead. > Note: if you're just wanting to try out faasd, then it's likely that you're on the wrong page. This is a detailed set of instructions for those wanting to contribute or customise faasd. Feel free to go back to the homepage and pick a tutorial instead.
### Pre-reqs ### Pre-reqs
> It's recommended that you do not install Docker on the same host as faasd, since 1) they may both use different versions of containerd and 2) docker's networking rules can disrupt faasd's networking. When using faasd - make your faasd server a faasd server, and build container image on your laptop or in a CI pipeline.
* Linux * Linux
PC / Cloud - any Linux that containerd works on should be fair game, but faasd is tested with Ubuntu 18.04 PC / Cloud - any Linux that containerd works on should be fair game, but faasd is tested with Ubuntu 18.04
@ -22,7 +20,7 @@
If you're using multipass, then allocate sufficient resources: If you're using multipass, then allocate sufficient resources:
```bash ```sh
multipass launch \ multipass launch \
--mem 4G \ --mem 4G \
-c 2 \ -c 2 \
@ -34,7 +32,7 @@ multipass shell faasd
### Get runc ### Get runc
```bash ```sh
sudo apt update \ sudo apt update \
&& sudo apt install -qy \ && sudo apt install -qy \
runc \ runc \
@ -58,7 +56,7 @@ curl -sLS https://cli.openfaas.com | sudo sh
Then run: Then run:
```bash ```sh
export ARCH=amd64 export ARCH=amd64
export CNI_VERSION=v0.8.5 export CNI_VERSION=v0.8.5
@ -83,7 +81,7 @@ You have three options - binaries for PC, binaries for armhf, or build from sour
* Install containerd `x86_64` only * Install containerd `x86_64` only
```bash ```sh
export VER=1.3.5 export VER=1.3.5
curl -sSL https://github.com/containerd/containerd/releases/download/v$VER/containerd-$VER-linux-amd64.tar.gz > /tmp/containerd.tar.gz \ curl -sSL https://github.com/containerd/containerd/releases/download/v$VER/containerd-$VER-linux-amd64.tar.gz > /tmp/containerd.tar.gz \
&& sudo tar -xvf /tmp/containerd.tar.gz -C /usr/local/bin/ --strip-components=1 && sudo tar -xvf /tmp/containerd.tar.gz -C /usr/local/bin/ --strip-components=1
@ -95,13 +93,13 @@ containerd -version
Building `containerd` on armhf is extremely slow, so I've provided binaries for you. Building `containerd` on armhf is extremely slow, so I've provided binaries for you.
```bash ```sh
curl -sSL https://github.com/alexellis/containerd-armhf/releases/download/v1.3.5/containerd.tgz | sudo tar -xvz --strip-components=2 -C /usr/local/bin/ curl -sSL https://github.com/alexellis/containerd-armhf/releases/download/v1.3.5/containerd.tgz | sudo tar -xvz --strip-components=2 -C /usr/local/bin/
``` ```
* Or clone / build / install [containerd](https://github.com/containerd/containerd) from source: * Or clone / build / install [containerd](https://github.com/containerd/containerd) from source:
```bash ```sh
export GOPATH=$HOME/go/ export GOPATH=$HOME/go/
mkdir -p $GOPATH/src/github.com/containerd mkdir -p $GOPATH/src/github.com/containerd
cd $GOPATH/src/github.com/containerd cd $GOPATH/src/github.com/containerd
@ -118,7 +116,7 @@ containerd -version
#### Ensure containerd is running #### Ensure containerd is running
```bash ```sh
curl -sLS https://raw.githubusercontent.com/containerd/containerd/v1.3.5/containerd.service > /tmp/containerd.service curl -sLS https://raw.githubusercontent.com/containerd/containerd/v1.3.5/containerd.service > /tmp/containerd.service
# Extend the timeouts for low-performance VMs # Extend the timeouts for low-performance VMs
@ -134,7 +132,7 @@ sudo systemctl restart containerd
Or run ad-hoc. This step can be useful for exploring why containerd might fail to start. Or run ad-hoc. This step can be useful for exploring why containerd might fail to start.
```bash ```sh
sudo containerd & sudo containerd &
``` ```
@ -142,13 +140,13 @@ sudo containerd &
> This is required to allow containers in containerd to access the Internet via your computer's primary network interface. > This is required to allow containers in containerd to access the Internet via your computer's primary network interface.
```bash ```sh
sudo /sbin/sysctl -w net.ipv4.conf.all.forwarding=1 sudo /sbin/sysctl -w net.ipv4.conf.all.forwarding=1
``` ```
Make the setting permanent: Make the setting permanent:
```bash ```sh
echo "net.ipv4.conf.all.forwarding=1" | sudo tee -a /etc/sysctl.conf echo "net.ipv4.conf.all.forwarding=1" | sudo tee -a /etc/sysctl.conf
``` ```
@ -156,7 +154,7 @@ echo "net.ipv4.conf.all.forwarding=1" | sudo tee -a /etc/sysctl.conf
#### Get build packages #### Get build packages
```bash ```sh
sudo apt update \ sudo apt update \
&& sudo apt install -qy \ && sudo apt install -qy \
runc \ runc \
@ -168,8 +166,8 @@ You may find alternative package names for CentOS and other Linux distributions.
#### Install Go 1.13 (x86_64) #### Install Go 1.13 (x86_64)
```bash ```sh
curl -SLf https://golang.org/dl/go1.16.linux-amd64.tar.gz > /tmp/go.tgz curl -sSLf https://dl.google.com/go/go1.13.6.linux-amd64.tar.gz > /tmp/go.tgz
sudo rm -rf /usr/local/go/ sudo rm -rf /usr/local/go/
sudo mkdir -p /usr/local/go/ sudo mkdir -p /usr/local/go/
sudo tar -xvf /tmp/go.tgz -C /usr/local/go/ --strip-components=1 sudo tar -xvf /tmp/go.tgz -C /usr/local/go/ --strip-components=1
@ -182,15 +180,15 @@ go version
You should also add the following to `~/.bash_profile`: You should also add the following to `~/.bash_profile`:
```bash ```sh
echo "export GOPATH=\$HOME/go/" | tee -a $HOME/.bash_profile echo "export GOPATH=\$HOME/go/" | tee -a $HOME/.bash_profile
echo "export PATH=\$PATH:/usr/local/go/bin/" | tee -a $HOME/.bash_profile echo "export PATH=\$PATH:/usr/local/go/bin/" | tee -a $HOME/.bash_profile
``` ```
#### Or on Raspberry Pi (armhf) #### Or on Raspberry Pi (armhf)
```bash ```sh
curl -SLsf https://golang.org/dl/go1.16.linux-armv6l.tar.gz > go.tgz curl -SLsf https://dl.google.com/go/go1.13.6.linux-armv6l.tar.gz > go.tgz
sudo rm -rf /usr/local/go/ sudo rm -rf /usr/local/go/
sudo mkdir -p /usr/local/go/ sudo mkdir -p /usr/local/go/
sudo tar -xvf go.tgz -C /usr/local/go/ --strip-components=1 sudo tar -xvf go.tgz -C /usr/local/go/ --strip-components=1
@ -203,7 +201,7 @@ go version
#### Clone faasd and its systemd unit files #### Clone faasd and its systemd unit files
```bash ```sh
mkdir -p $GOPATH/src/github.com/openfaas/ mkdir -p $GOPATH/src/github.com/openfaas/
cd $GOPATH/src/github.com/openfaas/ cd $GOPATH/src/github.com/openfaas/
git clone https://github.com/openfaas/faasd git clone https://github.com/openfaas/faasd
@ -211,7 +209,7 @@ git clone https://github.com/openfaas/faasd
#### Build `faasd` from source (optional) #### Build `faasd` from source (optional)
```bash ```sh
cd $GOPATH/src/github.com/openfaas/faasd cd $GOPATH/src/github.com/openfaas/faasd
cd faasd cd faasd
make local make local
@ -222,7 +220,7 @@ sudo cp bin/faasd /usr/local/bin
#### Or, download and run `faasd` (binaries) #### Or, download and run `faasd` (binaries)
```bash ```sh
# For x86_64 # For x86_64
export SUFFIX="" export SUFFIX=""
@ -233,7 +231,7 @@ export SUFFIX="-armhf"
export SUFFIX="-arm64" export SUFFIX="-arm64"
# Then download # Then download
curl -fSLs "https://github.com/openfaas/faasd/releases/download/0.11.0/faasd$SUFFIX" \ curl -fSLs "https://github.com/openfaas/faasd/releases/download/0.9.10/faasd$SUFFIX" \
-o "/tmp/faasd" \ -o "/tmp/faasd" \
&& chmod +x "/tmp/faasd" && chmod +x "/tmp/faasd"
sudo mv /tmp/faasd /usr/local/bin/ sudo mv /tmp/faasd /usr/local/bin/
@ -243,7 +241,7 @@ sudo mv /tmp/faasd /usr/local/bin/
This step installs faasd as a systemd unit file, creates files in `/var/lib/faasd`, and writes out networking configuration for the CNI bridge networking plugin. This step installs faasd as a systemd unit file, creates files in `/var/lib/faasd`, and writes out networking configuration for the CNI bridge networking plugin.
```bash ```sh
sudo faasd install sudo faasd install
2020/02/17 17:38:06 Writing to: "/var/lib/faasd/secrets/basic-auth-password" 2020/02/17 17:38:06 Writing to: "/var/lib/faasd/secrets/basic-auth-password"
@ -256,13 +254,13 @@ You can now log in either from this machine or a remote machine using the OpenFa
Check that faasd is ready: Check that faasd is ready:
```bash ```
sudo journalctl -u faasd sudo journalctl -u faasd
``` ```
You should see output like: You should see output like:
```bash ```
Feb 17 17:46:35 gold-survive faasd[4140]: 2020/02/17 17:46:35 Starting faasd proxy on 8080 Feb 17 17:46:35 gold-survive faasd[4140]: 2020/02/17 17:46:35 Starting faasd proxy on 8080
Feb 17 17:46:35 gold-survive faasd[4140]: Gateway: 10.62.0.5:8080 Feb 17 17:46:35 gold-survive faasd[4140]: Gateway: 10.62.0.5:8080
Feb 17 17:46:35 gold-survive faasd[4140]: 2020/02/17 17:46:35 [proxy] Wait for done Feb 17 17:46:35 gold-survive faasd[4140]: 2020/02/17 17:46:35 [proxy] Wait for done
@ -271,7 +269,7 @@ Feb 17 17:46:35 gold-survive faasd[4140]: 2020/02/17 17:46:35 [proxy] Begin list
To get the CLI for the command above run: To get the CLI for the command above run:
```bash ```sh
curl -sSLf https://cli.openfaas.com | sudo sh curl -sSLf https://cli.openfaas.com | sudo sh
``` ```
@ -327,7 +325,7 @@ faasd provider
Look in `hosts` in the current working folder or in `/var/lib/faasd/` to get the IP for the gateway or Prometheus Look in `hosts` in the current working folder or in `/var/lib/faasd/` to get the IP for the gateway or Prometheus
```bash ```sh
127.0.0.1 localhost 127.0.0.1 localhost
10.62.0.1 faasd-provider 10.62.0.1 faasd-provider

View File

@ -69,7 +69,7 @@ export IP="192.168.64.14"
You can also try to use `jq` to get the IP into a variable: You can also try to use `jq` to get the IP into a variable:
```sh ```sh
export IP=$(multipass info faasd --format json| jq -r '.info.faasd.ipv4[0]') export IP=$(multipass info faasd --format json| jq '.info.faasd.ipv4[0]' | tr -d '\"')
``` ```
Connect to the IP listed: Connect to the IP listed:
@ -115,7 +115,7 @@ You can also checkout the other store functions: `faas-cli store list`
* Try the UI * Try the UI
Head over to the UI from your laptop and remember that your password is in the `basic-auth-password` file. The username is `admin`: Head over to the UI from your laptop and remember that your password is in the `basic-auth-password` file. The username is `admin.:
``` ```
echo http://$IP:8080 echo http://$IP:8080

View File

@ -1,116 +0,0 @@
# faasd backlog and features
## Supported operations
* `faas login`
* `faas up`
* `faas list`
* `faas describe`
* `faas deploy --update=true --replace=false`
* `faas invoke --async`
* `faas invoke`
* `faas rm`
* `faas store list/deploy/inspect`
* `faas version`
* `faas namespace`
* `faas secret`
* `faas logs`
* `faas auth` - supported for Basic Authentication and OpenFaaS PRO with OIDC and Single-sign On.
Scale from and to zero is also supported. On a Dell XPS with a small, pre-pulled image unpausing an existing task took 0.19s and starting a task for a killed function took 0.39s. There may be further optimizations to be gained.
## Constraints vs OpenFaaS on Kubernetes
faasd suits certain use-cases as mentioned in the README file, for those who want a solution which can scale out horizontally with minimum effort, Kubernetes or K3s is a valid option.
### One replica per function
Functions only support one replica, so cannot scale horizontally, but can scale vertically.
Workaround: deploy one uniquely named function per replica.
### Scale from zero may give a non-200
When scaling from zero there is no health check implemented, so the request may arrive before your HTTP server is ready to serve a request, and therefore give a non-200 code.
Workaround: Do not scale to zero, or have your client retry HTTP calls.
### No clustering is available
No clustering is available in faasd, however you can still apply fault-tolerance and high availability techniques.
Workaround: deploy multiple faasd instances and use a hardware or software load-balancer. Take regular VM/host snapshots or backups.
### No rolling updates
When running `faas-cli deploy`, your old function is removed before the new one is started. This may cause a small amount of downtime, depending on the timeouts and grace periods you set.
Workaround: deploy uniquely named functions per version, and switch an Ingress or Reverse Proxy record to point at a new version once it is ready.
## Known issues
### Non 200 HTTP status from the gateway upon first use
This issue appears to happen sporadically and only for some users.
If you get a non 200 HTTP code from the gateway, or caddy after installing faasd, check the logs of faasd:
```bash
sudo journalctl -u faasd
```
If you see the following error:
```
unable to dial to 10.62.0.5:8080, error: dial tcp 10.62.0.5:8080: connect: no route to host
```
Restart the faasd service with:
```bash
sudo systemctl restart faasd
```
## Backlog
Should have:
* [ ] Resolve core services from functions by populating/sharing `/etc/hosts` between `faasd` and `faasd-provider`
* [ ] Docs or examples on how to use the various connectors and connector-sdk
* [ ] Monitor and restart any of the core components at runtime if the container stops
* [ ] Asynchronous deletion instead of synchronous
Nice to Have:
* [ ] Terraform for AWS (in-progress)
* [ ] Total memory limits - if a node has 1GB of RAM, don't allow more than 1000MB of RAM to be reserved via limits
* [ ] Offer live rolling-updates, with zero downtime - requires moving to IDs vs. names for function containers
* [ ] Multiple replicas per function
### Completed
* [x] Provide a cloud-init configuration for faasd bootstrap
* [x] Configure core services from a docker-compose.yaml file
* [x] Store and fetch logs from the journal
* [x] Add support for using container images in third-party public registries
* [x] Add support for using container images in private third-party registries
* [x] Provide a cloud-config.txt file for automated deployments of `faasd`
* [x] Inject / manage IPs between core components for service to service communication - i.e. so Prometheus can scrape the OpenFaaS gateway - done via `/etc/hosts` mount
* [x] Add queue-worker and NATS
* [x] Create faasd.service and faasd-provider.service
* [x] Self-install / create systemd service via `faasd install`
* [x] Restart containers upon restart of faasd
* [x] Clear / remove containers and tasks with SIGTERM / SIGINT
* [x] Determine armhf/arm64 containers to run for gateway
* [x] Configure `basic_auth` to protect the OpenFaaS gateway and faasd-provider HTTP API
* [x] Setup custom working directory for faasd `/var/lib/faasd/`
* [x] Use CNI to create network namespaces and adapters
* [x] Optionally expose core services from the docker-compose.yaml file, locally or to all adapters.
* [x] ~~[containerd can't pull image from Github Docker Package Registry](https://github.com/containerd/containerd/issues/3291)~~ ghcr.io support
* [x] Provide [simple Caddyfile example](https://blog.alexellis.io/https-inlets-local-endpoints/) in the README showing how to expose the faasd proxy on port 80/443 with TLS
* [x] Annotation support
* [x] Hard memory limits for functions
* [x] Terraform for DigitalOcean
* [x] [Store and retrieve annotations in function spec](https://github.com/openfaas/faasd/pull/86) - in progress
* [x] An installer for faasd and dependencies - runc, containerd

View File

@ -7,7 +7,6 @@ package_update: true
packages: packages:
- runc - runc
- git
runcmd: runcmd:
- curl -sLSf https://github.com/containerd/containerd/releases/download/v1.3.5/containerd-1.3.5-linux-amd64.tar.gz > /tmp/containerd.tar.gz && tar -xvf /tmp/containerd.tar.gz -C /usr/local/bin/ --strip-components=1 - curl -sLSf https://github.com/containerd/containerd/releases/download/v1.3.5/containerd-1.3.5-linux-amd64.tar.gz > /tmp/containerd.tar.gz && tar -xvf /tmp/containerd.tar.gz -C /usr/local/bin/ --strip-components=1
@ -20,8 +19,8 @@ runcmd:
- mkdir -p /var/lib/faasd/secrets/ - mkdir -p /var/lib/faasd/secrets/
- echo ${gw_password} > /var/lib/faasd/secrets/basic-auth-password - echo ${gw_password} > /var/lib/faasd/secrets/basic-auth-password
- echo admin > /var/lib/faasd/secrets/basic-auth-user - echo admin > /var/lib/faasd/secrets/basic-auth-user
- cd /go/src/github.com/openfaas/ && git clone --depth 1 --branch 0.11.0 https://github.com/openfaas/faasd - cd /go/src/github.com/openfaas/ && git clone --depth 1 --branch 0.9.10 https://github.com/openfaas/faasd
- curl -fSLs "https://github.com/openfaas/faasd/releases/download/0.11.0/faasd" --output "/usr/local/bin/faasd" && chmod a+x "/usr/local/bin/faasd" - curl -fSLs "https://github.com/openfaas/faasd/releases/download/0.9.10/faasd" --output "/usr/local/bin/faasd" && chmod a+x "/usr/local/bin/faasd"
- cd /go/src/github.com/openfaas/faasd/ && /usr/local/bin/faasd install - cd /go/src/github.com/openfaas/faasd/ && /usr/local/bin/faasd install
- systemctl status -l containerd --no-pager - systemctl status -l containerd --no-pager
- journalctl -u faasd-provider --no-pager - journalctl -u faasd-provider --no-pager

View File

@ -41,8 +41,8 @@ runcmd:
- mkdir -p /var/lib/faasd/secrets/ - mkdir -p /var/lib/faasd/secrets/
- echo ${gw_password} > /var/lib/faasd/secrets/basic-auth-password - echo ${gw_password} > /var/lib/faasd/secrets/basic-auth-password
- echo admin > /var/lib/faasd/secrets/basic-auth-user - echo admin > /var/lib/faasd/secrets/basic-auth-user
- cd /go/src/github.com/openfaas/ && git clone --depth 1 --branch 0.11.0 https://github.com/openfaas/faasd - cd /go/src/github.com/openfaas/ && git clone --depth 1 --branch 0.9.10 https://github.com/openfaas/faasd
- curl -fSLs "https://github.com/openfaas/faasd/releases/download/0.11.0/faasd" --output "/usr/local/bin/faasd" && chmod a+x "/usr/local/bin/faasd" - curl -fSLs "https://github.com/openfaas/faasd/releases/download/0.9.10/faasd" --output "/usr/local/bin/faasd" && chmod a+x "/usr/local/bin/faasd"
- cd /go/src/github.com/openfaas/faasd/ && /usr/local/bin/faasd install - cd /go/src/github.com/openfaas/faasd/ && /usr/local/bin/faasd install
- systemctl status -l containerd --no-pager - systemctl status -l containerd --no-pager
- journalctl -u faasd-provider --no-pager - journalctl -u faasd-provider --no-pager

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

4
go.mod
View File

@ -1,6 +1,6 @@
module github.com/openfaas/faasd module github.com/openfaas/faasd
go 1.15 go 1.13
require ( require (
github.com/Microsoft/hcsshim v0.8.7-0.20190820203702-9e921883ac92 // indirect github.com/Microsoft/hcsshim v0.8.7-0.20190820203702-9e921883ac92 // indirect
@ -30,7 +30,7 @@ require (
github.com/opencontainers/runc v1.0.0-rc9 // indirect github.com/opencontainers/runc v1.0.0-rc9 // indirect
github.com/opencontainers/runtime-spec v1.0.2 github.com/opencontainers/runtime-spec v1.0.2
github.com/openfaas/faas v0.0.0-20201205125747-9bbb25e3c7c4 github.com/openfaas/faas v0.0.0-20201205125747-9bbb25e3c7c4
github.com/openfaas/faas-provider v0.17.3 github.com/openfaas/faas-provider v0.15.3
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/prometheus/procfs v0.2.0 // indirect github.com/prometheus/procfs v0.2.0 // indirect
github.com/sethvargo/go-password v0.1.3 github.com/sethvargo/go-password v0.1.3

10
go.sum
View File

@ -181,14 +181,8 @@ github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
github.com/openfaas/faas v0.0.0-20201205125747-9bbb25e3c7c4 h1:JJjthDw7WziZQ7sC5C+M2872mIdud5R+s6Cb0cXyPuA= github.com/openfaas/faas v0.0.0-20201205125747-9bbb25e3c7c4 h1:JJjthDw7WziZQ7sC5C+M2872mIdud5R+s6Cb0cXyPuA=
github.com/openfaas/faas v0.0.0-20201205125747-9bbb25e3c7c4/go.mod h1:E0m2rLup0Vvxg53BKxGgaYAGcZa3Xl+vvL7vSi5yQ14= github.com/openfaas/faas v0.0.0-20201205125747-9bbb25e3c7c4/go.mod h1:E0m2rLup0Vvxg53BKxGgaYAGcZa3Xl+vvL7vSi5yQ14=
github.com/openfaas/faas-provider v0.17.0 h1:4rT8CosKhI5xaAMqbyihEgR6KefO/ViJdF0a8THTgwM= github.com/openfaas/faas-provider v0.15.3 h1:tfjuL5F/tdoUr1J65XrUADyoe59x38VzN1w0DvBaTRk=
github.com/openfaas/faas-provider v0.17.0/go.mod h1:fq1JL0mX4rNvVVvRLaLRJ3H6o667sHuyP5p/7SZEe98= github.com/openfaas/faas-provider v0.15.3/go.mod h1:fq1JL0mX4rNvVVvRLaLRJ3H6o667sHuyP5p/7SZEe98=
github.com/openfaas/faas-provider v0.17.1 h1:P5xTLN+/08PLLh4auIlO/PaUD/J3BUTmaC3en8N5zbs=
github.com/openfaas/faas-provider v0.17.1/go.mod h1:fq1JL0mX4rNvVVvRLaLRJ3H6o667sHuyP5p/7SZEe98=
github.com/openfaas/faas-provider v0.17.2 h1:jZ+Z83A/tyJoI1AnpyLN3o0B4K0UEsz1YJ3erASMu+s=
github.com/openfaas/faas-provider v0.17.2/go.mod h1:fq1JL0mX4rNvVVvRLaLRJ3H6o667sHuyP5p/7SZEe98=
github.com/openfaas/faas-provider v0.17.3 h1:LN76lrXUKAx27o5X8l+daKWEzsdiW2E99jMOlI1SO5Q=
github.com/openfaas/faas-provider v0.17.3/go.mod h1:fq1JL0mX4rNvVVvRLaLRJ3H6o667sHuyP5p/7SZEe98=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

View File

@ -12,7 +12,7 @@ export REPO="faasd"
version="" version=""
echo "Finding latest version from GitHub" echo "Finding latest version from GitHub"
version=$(curl -sI https://github.com/$OWNER/$REPO/releases/latest | grep -i "location:" | awk -F"/" '{ printf "%s", $NF }' | tr -d '\r') version=$(curl -sI https://github.com/$OWNER/$REPO/releases/latest | grep -i location | awk -F"/" '{ printf "%s", $NF }' | tr -d '\r')
echo "$version" echo "$version"
if [ ! $version ]; then if [ ! $version ]; then
@ -150,7 +150,7 @@ install_caddy() {
;; ;;
esac esac
curl -sSL "https://github.com/caddyserver/caddy/releases/download/v2.4.3/caddy_2.4.3_linux_${suffix}.tar.gz" | $SUDO tar -xvz -C /usr/bin/ caddy curl -sSL "https://github.com/caddyserver/caddy/releases/download/v2.2.1/caddy_2.2.1_linux_${suffix}.tar.gz" | $SUDO tar -xvz -C /usr/bin/ caddy
$SUDO curl -fSLs https://raw.githubusercontent.com/caddyserver/dist/master/init/caddy.service --output /etc/systemd/system/caddy.service $SUDO curl -fSLs https://raw.githubusercontent.com/caddyserver/dist/master/init/caddy.service --output /etc/systemd/system/caddy.service
$SUDO mkdir -p /etc/caddy $SUDO mkdir -p /etc/caddy
@ -189,7 +189,7 @@ install_faas_cli() {
verify_system verify_system
install_required_packages install_required_packages
$SUDO /sbin/sysctl -w net.ipv4.conf.all.forwarding=1 /sbin/sysctl -w net.ipv4.conf.all.forwarding=1
echo "net.ipv4.conf.all.forwarding=1" | $SUDO tee -a /etc/sysctl.conf echo "net.ipv4.conf.all.forwarding=1" | $SUDO tee -a /etc/sysctl.conf
install_cni_plugins install_cni_plugins

View File

@ -1,7 +1,6 @@
package cninetwork package cninetwork
import ( import (
"bufio"
"context" "context"
"fmt" "fmt"
"io" "io"
@ -11,7 +10,6 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"strings"
"github.com/containerd/containerd" "github.com/containerd/containerd"
gocni "github.com/containerd/go-cni" gocni "github.com/containerd/go-cni"
@ -21,31 +19,21 @@ import (
const ( const (
// CNIBinDir describes the directory where the CNI binaries are stored // CNIBinDir describes the directory where the CNI binaries are stored
CNIBinDir = "/opt/cni/bin" CNIBinDir = "/opt/cni/bin"
// CNIConfDir describes the directory where the CNI plugin's configuration is stored // CNIConfDir describes the directory where the CNI plugin's configuration is stored
CNIConfDir = "/etc/cni/net.d" CNIConfDir = "/etc/cni/net.d"
// NetNSPathFmt gives the path to the a process network namespace, given the pid // NetNSPathFmt gives the path to the a process network namespace, given the pid
NetNSPathFmt = "/proc/%d/ns/net" NetNSPathFmt = "/proc/%d/ns/net"
// CNIResultsDir is the directory CNI stores allocated IP for containers
// CNIDataDir is the directory CNI stores allocated IP for containers CNIResultsDir = "/var/lib/cni/results"
CNIDataDir = "/var/run/cni"
// defaultCNIConfFilename is the vanity filename of default CNI configuration file // defaultCNIConfFilename is the vanity filename of default CNI configuration file
defaultCNIConfFilename = "10-openfaas.conflist" defaultCNIConfFilename = "10-openfaas.conflist"
// defaultNetworkName names the "docker-bridge"-like CNI plugin-chain installed when no other CNI configuration is present. // defaultNetworkName names the "docker-bridge"-like CNI plugin-chain installed when no other CNI configuration is present.
// This value appears in iptables comments created by CNI. // This value appears in iptables comments created by CNI.
defaultNetworkName = "openfaas-cni-bridge" defaultNetworkName = "openfaas-cni-bridge"
// defaultBridgeName is the default bridge device name used in the defaultCNIConf // defaultBridgeName is the default bridge device name used in the defaultCNIConf
defaultBridgeName = "openfaas0" defaultBridgeName = "openfaas0"
// defaultSubnet is the default subnet used in the defaultCNIConf -- this value is set to not collide with common container networking subnets: // defaultSubnet is the default subnet used in the defaultCNIConf -- this value is set to not collide with common container networking subnets:
defaultSubnet = "10.62.0.0/16" defaultSubnet = "10.62.0.0/16"
// defaultIfPrefix is the interface name to be created in the container
defaultIfPrefix = "eth"
) )
// defaultCNIConf is a CNI configuration that enables network access to containers (docker-bridge style) // defaultCNIConf is a CNI configuration that enables network access to containers (docker-bridge style)
@ -62,7 +50,6 @@ var defaultCNIConf = fmt.Sprintf(`
"ipam": { "ipam": {
"type": "host-local", "type": "host-local",
"subnet": "%s", "subnet": "%s",
"dataDir": "%s",
"routes": [ "routes": [
{ "dst": "0.0.0.0/0" } { "dst": "0.0.0.0/0" }
] ]
@ -73,7 +60,7 @@ var defaultCNIConf = fmt.Sprintf(`
} }
] ]
} }
`, defaultNetworkName, defaultBridgeName, defaultSubnet, CNIDataDir) `, defaultNetworkName, defaultBridgeName, defaultSubnet)
// InitNetwork writes configlist file and initializes CNI network // InitNetwork writes configlist file and initializes CNI network
func InitNetwork() (gocni.CNI, error) { func InitNetwork() (gocni.CNI, error) {
@ -88,14 +75,11 @@ func InitNetwork() (gocni.CNI, error) {
netConfig := path.Join(CNIConfDir, defaultCNIConfFilename) netConfig := path.Join(CNIConfDir, defaultCNIConfFilename)
if err := ioutil.WriteFile(netConfig, []byte(defaultCNIConf), 644); err != nil { if err := ioutil.WriteFile(netConfig, []byte(defaultCNIConf), 644); err != nil {
return nil, fmt.Errorf("cannot write network config: %s", defaultCNIConfFilename) return nil, fmt.Errorf("cannot write network config: %s", defaultCNIConfFilename)
}
}
// Initialize CNI library // Initialize CNI library
cni, err := gocni.New( cni, err := gocni.New(gocni.WithPluginConfDir(CNIConfDir),
gocni.WithPluginConfDir(CNIConfDir), gocni.WithPluginDir([]string{CNIBinDir}))
gocni.WithPluginDir([]string{CNIBinDir}),
gocni.WithInterfacePrefix(defaultIfPrefix),
)
if err != nil { if err != nil {
return nil, fmt.Errorf("error initializing cni: %s", err) return nil, fmt.Errorf("error initializing cni: %s", err)
@ -147,61 +131,43 @@ func DeleteCNINetwork(ctx context.Context, cni gocni.CNI, client *containerd.Cli
return errors.Wrapf(containerErr, "Unable to find container: %s, error: %s", name, containerErr) return errors.Wrapf(containerErr, "Unable to find container: %s, error: %s", name, containerErr)
} }
// GetIPAddress returns the IP address from container based on container name and PID // GetIPAddress returns the IP address of the created container
func GetIPAddress(container string, PID uint32) (string, error) { func GetIPAddress(result *gocni.CNIResult, task containerd.Task) (net.IP, error) {
CNIDir := path.Join(CNIDataDir, defaultNetworkName) // Get the IP of the created interface
var ip net.IP
files, err := ioutil.ReadDir(CNIDir) for ifName, config := range result.Interfaces {
if err != nil { if config.Sandbox == netNamespace(task) {
return "", fmt.Errorf("failed to read CNI dir for container %s: %v", container, err) for _, ipConfig := range config.IPConfigs {
} if ifName != "lo" && ipConfig.IP.To4() != nil {
ip = ipConfig.IP
for _, file := range files {
// each fileName is an IP address
fileName := file.Name()
resultsFile := filepath.Join(CNIDir, fileName)
found, err := isCNIResultForPID(resultsFile, container, PID)
if err != nil {
return "", err
}
if found {
return fileName, nil
} }
} }
}
return "", fmt.Errorf("unable to get IP address for container: %s", container) }
if ip == nil {
return nil, fmt.Errorf("unable to get IP address for: %s", task.ID())
}
return ip, nil
} }
// isCNIResultForPID confirms if the CNI result file contains the func GetIPfromPID(pid int) (*net.IP, error) {
// process name, PID and interface name // https://github.com/weaveworks/weave/blob/master/net/netdev.go
//
// Example:
//
// /var/run/cni/openfaas-cni-bridge/10.62.0.2
//
// nats-621
// eth1
func isCNIResultForPID(fileName, container string, PID uint32) (bool, error) {
found := false
f, err := os.Open(fileName) peerIDs, err := ConnectedToBridgeVethPeerIds(defaultBridgeName)
if err != nil { if err != nil {
return false, fmt.Errorf("failed to open CNI IP file for %s: %v", fileName, err) return nil, fmt.Errorf("unable to find peers on: %s %s", defaultBridgeName, err)
}
defer f.Close()
reader := bufio.NewReader(f)
processLine, _ := reader.ReadString('\n')
if strings.Contains(processLine, fmt.Sprintf("%s-%d", container, PID)) {
ethNameLine, _ := reader.ReadString('\n')
if strings.Contains(ethNameLine, defaultIfPrefix) {
found = true
}
} }
return found, nil addrs, addrsErr := GetNetDevsByVethPeerIds(pid, peerIDs)
if addrsErr != nil {
return nil, fmt.Errorf("unable to find address for veth pair using: %v %s", peerIDs, addrsErr)
}
if len(addrs) > 0 && len(addrs[0].CIDRs) > 0 {
return &addrs[0].CIDRs[0].IP, nil
}
return nil, fmt.Errorf("no IP found for function")
} }
// CNIGateway returns the gateway for default subnet // CNIGateway returns the gateway for default subnet

View File

@ -1,63 +0,0 @@
package cninetwork
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
)
func Test_isCNIResultForPID_Found(t *testing.T) {
body := `nats-621
eth1`
fileName := `10.62.0.2`
container := "nats"
PID := uint32(621)
fullPath := filepath.Join(os.TempDir(), fileName)
err := ioutil.WriteFile(fullPath, []byte(body), 0700)
if err != nil {
t.Fatalf(err.Error())
}
defer func() {
os.Remove(fullPath)
}()
got, err := isCNIResultForPID(fullPath, container, PID)
if err != nil {
t.Fatalf(err.Error())
}
want := true
if got != want {
t.Fatalf("want %v, but got %v", want, got)
}
}
func Test_isCNIResultForPID_NoMatch(t *testing.T) {
body := `nats-621
eth1`
fileName := `10.62.0.3`
container := "gateway"
PID := uint32(621)
fullPath := filepath.Join(os.TempDir(), fileName)
err := ioutil.WriteFile(fullPath, []byte(body), 0700)
if err != nil {
t.Fatalf(err.Error())
}
defer func() {
os.Remove(fullPath)
}()
got, err := isCNIResultForPID(fullPath, container, PID)
if err != nil {
t.Fatalf(err.Error())
}
want := false
if got != want {
t.Fatalf("want %v, but got %v", want, got)
}
}

118
pkg/cninetwork/weave.go Normal file
View File

@ -0,0 +1,118 @@
// Copyright Weaveworks
// github.com/weaveworks/weave/net
package cninetwork
import (
"fmt"
"net"
"os"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netns"
)
type Dev struct {
Name string `json:"Name,omitempty"`
MAC net.HardwareAddr `json:"MAC,omitempty"`
CIDRs []*net.IPNet `json:"CIDRs,omitempty"`
}
// ConnectedToBridgeVethPeerIds returns peer indexes of veth links connected to
// the given bridge. The peer index is used to query from a container netns
// whether the container is connected to the bridge.
func ConnectedToBridgeVethPeerIds(bridgeName string) ([]int, error) {
var ids []int
br, err := netlink.LinkByName(bridgeName)
if err != nil {
return nil, err
}
links, err := netlink.LinkList()
if err != nil {
return nil, err
}
for _, link := range links {
if _, isveth := link.(*netlink.Veth); isveth && link.Attrs().MasterIndex == br.Attrs().Index {
peerID := link.Attrs().ParentIndex
if peerID == 0 {
// perhaps running on an older kernel where ParentIndex doesn't work.
// as fall-back, assume the peers are consecutive
peerID = link.Attrs().Index - 1
}
ids = append(ids, peerID)
}
}
return ids, nil
}
// Lookup the weave interface of a container
func GetWeaveNetDevs(processID int) ([]Dev, error) {
peerIDs, err := ConnectedToBridgeVethPeerIds("weave")
if err != nil {
return nil, err
}
return GetNetDevsByVethPeerIds(processID, peerIDs)
}
func GetNetDevsByVethPeerIds(processID int, peerIDs []int) ([]Dev, error) {
// Bail out if this container is running in the root namespace
netnsRoot, err := netns.GetFromPid(1)
if err != nil {
return nil, fmt.Errorf("unable to open root namespace: %s", err)
}
defer netnsRoot.Close()
netnsContainer, err := netns.GetFromPid(processID)
if err != nil {
// Unable to find a namespace for this process - just return nothing
if os.IsNotExist(err) {
return nil, nil
}
return nil, fmt.Errorf("unable to open process %d namespace: %s", processID, err)
}
defer netnsContainer.Close()
if netnsRoot.Equal(netnsContainer) {
return nil, nil
}
// convert list of peerIDs into a map for faster lookup
indexes := make(map[int]struct{})
for _, id := range peerIDs {
indexes[id] = struct{}{}
}
var netdevs []Dev
err = WithNetNS(netnsContainer, func() error {
links, err := netlink.LinkList()
if err != nil {
return err
}
for _, link := range links {
if _, found := indexes[link.Attrs().Index]; found {
netdev, err := linkToNetDev(link)
if err != nil {
return err
}
netdevs = append(netdevs, netdev)
}
}
return nil
})
return netdevs, err
}
// Get the weave bridge interface.
// NB: Should be called from the root network namespace.
func GetBridgeNetDev(bridgeName string) (Dev, error) {
link, err := netlink.LinkByName(bridgeName)
if err != nil {
return Dev{}, err
}
return linkToNetDev(link)
}

View File

@ -0,0 +1,10 @@
// +build darwin
package cninetwork
import "github.com/vishvananda/netlink"
func linkToNetDev(link netlink.Link) (Dev, error) {
return Dev{}, nil
}

View File

@ -0,0 +1,19 @@
// +build linux
package cninetwork
import "github.com/vishvananda/netlink"
func linkToNetDev(link netlink.Link) (Dev, error) {
addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
if err != nil {
return Dev{}, err
}
netDev := Dev{Name: link.Attrs().Name, MAC: link.Attrs().HardwareAddr}
for _, addr := range addrs {
netDev.CIDRs = append(netDev.CIDRs, addr.IPNet)
}
return netDev, nil
}

View File

@ -3,11 +3,4 @@ package pkg
const ( const (
// FunctionNamespace is the default containerd namespace functions are created // FunctionNamespace is the default containerd namespace functions are created
FunctionNamespace = "openfaas-fn" FunctionNamespace = "openfaas-fn"
// faasdNamespace is the containerd namespace services are created
faasdNamespace = "openfaas"
faasServicesPullAlways = false
defaultSnapshotter = "overlayfs"
) )

View File

@ -29,8 +29,8 @@ import (
const annotationLabelPrefix = "com.openfaas.annotations." const annotationLabelPrefix = "com.openfaas.annotations."
// MakeDeployHandler returns a handler to deploy a function
func MakeDeployHandler(client *containerd.Client, cni gocni.CNI, secretMountPath string, alwaysPull bool) func(w http.ResponseWriter, r *http.Request) { func MakeDeployHandler(client *containerd.Client, cni gocni.CNI, secretMountPath string, alwaysPull bool) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
if r.Body == nil { if r.Body == nil {
@ -147,7 +147,6 @@ func deploy(ctx context.Context, req types.FunctionDeployment, client *container
containerd.WithSnapshotter(snapshotter), containerd.WithSnapshotter(snapshotter),
containerd.WithNewSnapshot(name+"-snapshot", image), containerd.WithNewSnapshot(name+"-snapshot", image),
containerd.WithNewSpec(oci.WithImageConfig(image), containerd.WithNewSpec(oci.WithImageConfig(image),
oci.WithHostname(name),
oci.WithCapabilities([]string{"CAP_NET_RAW"}), oci.WithCapabilities([]string{"CAP_NET_RAW"}),
oci.WithMounts(mounts), oci.WithMounts(mounts),
oci.WithEnv(envs), oci.WithEnv(envs),
@ -200,18 +199,17 @@ func createTask(ctx context.Context, client *containerd.Client, container contai
log.Printf("Container ID: %s\tTask ID %s:\tTask PID: %d\t\n", name, task.ID(), task.Pid()) log.Printf("Container ID: %s\tTask ID %s:\tTask PID: %d\t\n", name, task.ID(), task.Pid())
labels := map[string]string{} labels := map[string]string{}
_, err := cninetwork.CreateCNINetwork(ctx, cni, task, labels) network, err := cninetwork.CreateCNINetwork(ctx, cni, task, labels)
if err != nil { if err != nil {
return err return err
} }
ip, err := cninetwork.GetIPAddress(name, task.Pid()) ip, err := cninetwork.GetIPAddress(network, task)
if err != nil { if err != nil {
return err return err
} }
log.Printf("%s has IP: %s.\n", name, ip.String())
log.Printf("%s has IP: %s.\n", name, ip)
_, waitErr := task.Wait(ctx) _, waitErr := task.Wait(ctx)
if waitErr != nil { if waitErr != nil {

View File

@ -5,9 +5,6 @@ import (
"fmt" "fmt"
"log" "log"
"strings" "strings"
"time"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
@ -25,10 +22,6 @@ type Function struct {
IP string IP string
labels map[string]string labels map[string]string
annotations map[string]string annotations map[string]string
secrets []string
envVars map[string]string
envProcess string
createdAt time.Time
} }
// ListFunctions returns a map of all functions with running tasks on namespace // ListFunctions returns a map of all functions with running tasks on namespace
@ -78,28 +71,11 @@ func GetFunction(client *containerd.Client, name string) (Function, error) {
labels, annotations := buildLabelsAndAnnotations(allLabels) labels, annotations := buildLabelsAndAnnotations(allLabels)
spec, err := c.Spec(ctx)
if err != nil {
return Function{}, fmt.Errorf("unable to load function spec for reading secrets: %s, error %s", name, err)
}
info, err := c.Info(ctx)
if err != nil {
return Function{}, fmt.Errorf("can't load info for: %s, error %s", name, err)
}
envVars, envProcess := readEnvFromProcessEnv(spec.Process.Env)
secrets := readSecretsFromMounts(spec.Mounts)
fn.name = containerName fn.name = containerName
fn.namespace = faasd.FunctionNamespace fn.namespace = faasd.FunctionNamespace
fn.image = image.Name() fn.image = image.Name()
fn.labels = labels fn.labels = labels
fn.annotations = annotations fn.annotations = annotations
fn.secrets = secrets
fn.envVars = envVars
fn.envProcess = envProcess
fn.createdAt = info.CreatedAt
replicas := 0 replicas := 0
task, err := c.Task(ctx, nil) task, err := c.Task(ctx, nil)
@ -115,11 +91,11 @@ func GetFunction(client *containerd.Client, name string) (Function, error) {
fn.pid = task.Pid() fn.pid = task.Pid()
// Get container IP address // Get container IP address
ip, err := cninetwork.GetIPAddress(name, task.Pid()) ip, err := cninetwork.GetIPfromPID(int(task.Pid()))
if err != nil { if err != nil {
return Function{}, err return Function{}, err
} }
fn.IP = ip fn.IP = ip.String()
} }
} else { } else {
replicas = 0 replicas = 0
@ -129,42 +105,6 @@ func GetFunction(client *containerd.Client, name string) (Function, error) {
return fn, nil return fn, nil
} }
func readEnvFromProcessEnv(env []string) (map[string]string, string) {
foundEnv := make(map[string]string)
fprocess := ""
for _, e := range env {
kv := strings.Split(e, "=")
if len(kv) == 1 {
continue
}
if kv[0] == "PATH" {
continue
}
if kv[0] == "fprocess" {
fprocess = kv[1]
continue
}
foundEnv[kv[0]] = kv[1]
}
return foundEnv, fprocess
}
func readSecretsFromMounts(mounts []specs.Mount) []string {
secrets := []string{}
for _, mnt := range mounts {
x := strings.Split(mnt.Destination, "/var/openfaas/secrets/")
if len(x) > 1 {
secrets = append(secrets, x[1])
}
}
return secrets
}
// buildLabelsAndAnnotations returns a separated list with labels first, // buildLabelsAndAnnotations returns a separated list with labels first,
// followed by annotations by checking each key of ctrLabels for a prefix. // followed by annotations by checking each key of ctrLabels for a prefix.
func buildLabelsAndAnnotations(ctrLabels map[string]string) (map[string]string, map[string]string) { func buildLabelsAndAnnotations(ctrLabels map[string]string) (map[string]string, map[string]string) {

View File

@ -2,8 +2,6 @@ package handlers
import ( import (
"fmt" "fmt"
"github.com/opencontainers/runtime-spec/specs-go"
"reflect"
"testing" "testing"
) )
@ -29,58 +27,3 @@ func Test_BuildLabelsAndAnnotationsFromServiceSpec_Annotations(t *testing.T) {
t.Errorf("want: '%s' entry in annotation map got: key not found", "current-time") t.Errorf("want: '%s' entry in annotation map got: key not found", "current-time")
} }
} }
func Test_SplitMountToSecrets(t *testing.T) {
type test struct {
Name string
Input []specs.Mount
Expected []string
}
tests := []test{
{Name: "No matching openfaas secrets", Input: []specs.Mount{{Destination: "/foo/"}}, Expected: []string{}},
{Name: "Nil mounts", Input: nil, Expected: []string{}},
{Name: "No Mounts", Input: []specs.Mount{{Destination: "/foo/"}}, Expected: []string{}},
{Name: "One Mounts IS secret", Input: []specs.Mount{{Destination: "/var/openfaas/secrets/secret1"}}, Expected: []string{"secret1"}},
{Name: "Multiple Mounts 1 secret", Input: []specs.Mount{{Destination: "/var/openfaas/secrets/secret1"}, {Destination: "/some/other/path"}}, Expected: []string{"secret1"}},
{Name: "Multiple Mounts all secrets", Input: []specs.Mount{{Destination: "/var/openfaas/secrets/secret1"}, {Destination: "/var/openfaas/secrets/secret2"}}, Expected: []string{"secret1", "secret2"}},
}
for _, tc := range tests {
t.Run(tc.Name, func(t *testing.T) {
got := readSecretsFromMounts(tc.Input)
if !reflect.DeepEqual(got, tc.Expected) {
t.Fatalf("expected %s, got %s", tc.Expected, got)
}
})
}
}
func Test_ProcessEnvToEnvVars(t *testing.T) {
type test struct {
Name string
Input []string
Expected map[string]string
fprocess string
}
tests := []test{
{Name: "No matching EnvVars", Input: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "fprocess=python index.py"}, Expected: make(map[string]string), fprocess: "python index.py"},
{Name: "No EnvVars", Input: []string{}, Expected: make(map[string]string), fprocess: ""},
{Name: "One EnvVar", Input: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "fprocess=python index.py", "env=this"}, Expected: map[string]string{"env": "this"}, fprocess: "python index.py"},
{Name: "Multiple EnvVars", Input: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "this=that", "env=var", "fprocess=python index.py"}, Expected: map[string]string{"this": "that", "env": "var"}, fprocess: "python index.py"},
{Name: "Nil EnvVars", Input: nil, Expected: make(map[string]string)},
}
for _, tc := range tests {
t.Run(tc.Name, func(t *testing.T) {
got, fprocess := readEnvFromProcessEnv(tc.Input)
if !reflect.DeepEqual(got, tc.Expected) {
t.Fatalf("expected: %s, got: %s", tc.Expected, got)
}
if fprocess != tc.fprocess {
t.Fatalf("expected fprocess: %s, got: %s", tc.fprocess, got)
}
})
}
}

View File

@ -22,10 +22,10 @@ func MakeInfoHandler(version, sha string) http.HandlerFunc {
defer r.Body.Close() defer r.Body.Close()
} }
infoResponse := types.ProviderInfo{ infoResponse := types.InfoResponse{
Orchestration: OrchestrationIdentifier, Orchestration: OrchestrationIdentifier,
Name: ProviderName, Provider: ProviderName,
Version: &types.VersionInfo{ Version: types.ProviderVersion{
Release: version, Release: version,
SHA: sha, SHA: sha,
}, },

View File

@ -15,14 +15,14 @@ func Test_InfoHandler(t *testing.T) {
r := httptest.NewRequest("GET", "/", nil) r := httptest.NewRequest("GET", "/", nil)
handler(w, r) handler(w, r)
resp := types.ProviderInfo{} resp := types.InfoResponse{}
err := json.Unmarshal(w.Body.Bytes(), &resp) err := json.Unmarshal(w.Body.Bytes(), &resp)
if err != nil { if err != nil {
t.Fatalf("unexpected error unmarshalling the response") t.Fatalf("unexpected error unmarshalling the response")
} }
if resp.Name != ProviderName { if resp.Provider != ProviderName {
t.Fatalf("expected provider %q, got %q", ProviderName, resp.Name) t.Fatalf("expected provider %q, got %q", ProviderName, resp.Provider)
} }
if resp.Orchestration != OrchestrationIdentifier { if resp.Orchestration != OrchestrationIdentifier {

View File

@ -31,10 +31,6 @@ func MakeReadHandler(client *containerd.Client) func(w http.ResponseWriter, r *h
Namespace: fn.namespace, Namespace: fn.namespace,
Labels: labels, Labels: labels,
Annotations: annotations, Annotations: annotations,
Secrets: fn.secrets,
EnvVars: fn.envVars,
EnvProcess: fn.envProcess,
CreatedAt: fn.createdAt,
}) })
} }
@ -42,5 +38,6 @@ func MakeReadHandler(client *containerd.Client) func(w http.ResponseWriter, r *h
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write(body) w.Write(body)
} }
} }

View File

@ -23,10 +23,6 @@ func MakeReplicaReaderHandler(client *containerd.Client) func(w http.ResponseWri
Namespace: f.namespace, Namespace: f.namespace,
Labels: &f.labels, Labels: &f.labels,
Annotations: &f.annotations, Annotations: &f.annotations,
Secrets: f.secrets,
EnvVars: f.envVars,
EnvProcess: f.envProcess,
CreatedAt: f.createdAt,
} }
functionBytes, _ := json.Marshal(found) functionBytes, _ := json.Marshal(found)

View File

@ -27,34 +27,31 @@ func Remove(ctx context.Context, client *containerd.Client, name string) error {
container, containerErr := client.LoadContainer(ctx, name) container, containerErr := client.LoadContainer(ctx, name)
if containerErr == nil { if containerErr == nil {
taskFound := true found := true
t, err := container.Task(ctx, nil) t, err := container.Task(ctx, nil)
if err != nil { if err != nil {
if errdefs.IsNotFound(err) { if errdefs.IsNotFound(err) {
taskFound = false found = false
} else { } else {
return fmt.Errorf("unable to get task %s: ", err) return fmt.Errorf("unable to get task %s: ", err)
} }
} }
if taskFound { if found {
status, err := t.Status(ctx) status, _ := t.Status(ctx)
if err != nil { fmt.Printf("Status of %s is: %s\n", name, status.Status)
log.Printf("Unable to get status for: %s, error: %s", name, err.Error())
} else {
log.Printf("Status of %s is: %s\n", name, status.Status)
}
log.Printf("Need to kill task: %s\n", name) log.Printf("Need to kill %s\n", name)
if err = killTask(ctx, t); err != nil { err := killTask(ctx, t)
if err != nil {
return fmt.Errorf("error killing task %s, %s, %s", container.ID(), name, err) return fmt.Errorf("error killing task %s, %s, %s", container.ID(), name, err)
} }
} }
if err := container.Delete(ctx, containerd.WithSnapshotCleanup); err != nil { err = container.Delete(ctx, containerd.WithSnapshotCleanup)
if err != nil {
return fmt.Errorf("error deleting container %s, %s, %s", container.ID(), name, err) return fmt.Errorf("error deleting container %s, %s, %s", container.ID(), name, err)
} }
} else { } else {
service := client.SnapshotService("") service := client.SnapshotService("")
key := name + "snapshot" key := name + "snapshot"
@ -73,7 +70,6 @@ func killTask(ctx context.Context, task containerd.Task) error {
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
wg.Add(1) wg.Add(1)
var err error var err error
go func() { go func() {
defer wg.Done() defer wg.Done()
if task != nil { if task != nil {
@ -118,7 +114,6 @@ func getResolver(ctx context.Context, configFile *configfile.ConfigFile) (remote
} }
return ac.Username, ac.Password, nil return ac.Username, ac.Password, nil
} }
authOpts := []docker.AuthorizerOpt{docker.WithAuthCreds(credFunc)} authOpts := []docker.AuthorizerOpt{docker.WithAuthCreds(credFunc)}
authorizer := docker.NewDockerAuthorizer(authOpts...) authorizer := docker.NewDockerAuthorizer(authOpts...)
opts := docker.ResolverOptions{ opts := docker.ResolverOptions{
@ -133,7 +128,7 @@ func PrepareImage(ctx context.Context, client *containerd.Client, imageName, sna
resolver remotes.Resolver resolver remotes.Resolver
) )
if _, statErr := os.Stat(filepath.Join(dockerConfigDir, config.ConfigFileName)); statErr == nil { if _, stErr := os.Stat(filepath.Join(dockerConfigDir, config.ConfigFileName)); stErr == nil {
configFile, err := config.Load(dockerConfigDir) configFile, err := config.Load(dockerConfigDir)
if err != nil { if err != nil {
return nil, err return nil, err
@ -142,8 +137,8 @@ func PrepareImage(ctx context.Context, client *containerd.Client, imageName, sna
if err != nil { if err != nil {
return empty, err return empty, err
} }
} else if !os.IsNotExist(statErr) { } else if !os.IsNotExist(stErr) {
return empty, statErr return empty, stErr
} }
var image containerd.Image var image containerd.Image
@ -155,6 +150,7 @@ func PrepareImage(ctx context.Context, client *containerd.Client, imageName, sna
image = img image = img
} else { } else {
img, err := client.GetImage(ctx, imageName) img, err := client.GetImage(ctx, imageName)
if err != nil { if err != nil {
if !errdefs.IsNotFound(err) { if !errdefs.IsNotFound(err) {
@ -191,11 +187,9 @@ func pullImage(ctx context.Context, client *containerd.Client, resolver remotes.
rOpts := []containerd.RemoteOpt{ rOpts := []containerd.RemoteOpt{
containerd.WithPullUnpack, containerd.WithPullUnpack,
} }
if resolver != nil { if resolver != nil {
rOpts = append(rOpts, containerd.WithResolver(resolver)) rOpts = append(rOpts, containerd.WithResolver(resolver))
} }
img, err := client.Pull(ctx, imageName, rOpts...) img, err := client.Pull(ctx, imageName, rOpts...)
if err != nil { if err != nil {
return empty, fmt.Errorf("cannot pull: %s", err) return empty, fmt.Errorf("cannot pull: %s", err)

View File

@ -26,7 +26,11 @@ import (
) )
const ( const (
defaultSnapshotter = "overlayfs"
workingDirectoryPermission = 0644 workingDirectoryPermission = 0644
// faasdNamespace is the containerd namespace services are created
faasdNamespace = "default"
faasServicesPullAlways = false
) )
type Service struct { type Service struct {
@ -172,7 +176,6 @@ func (s *Supervisor) Start(svcs []Service) error {
containerd.WithImage(image), containerd.WithImage(image),
containerd.WithNewSnapshot(svc.Name+"-snapshot", image), containerd.WithNewSnapshot(svc.Name+"-snapshot", image),
containerd.WithNewSpec(oci.WithImageConfig(image), containerd.WithNewSpec(oci.WithImageConfig(image),
oci.WithHostname(svc.Name),
withUserOrDefault(svc.User), withUserOrDefault(svc.User),
oci.WithCapabilities(svc.Caps), oci.WithCapabilities(svc.Caps),
oci.WithMounts(mounts), oci.WithMounts(mounts),
@ -194,19 +197,19 @@ func (s *Supervisor) Start(svcs []Service) error {
} }
labels := map[string]string{} labels := map[string]string{}
_, err = cninetwork.CreateCNINetwork(ctx, s.cni, task, labels) network, err := cninetwork.CreateCNINetwork(ctx, s.cni, task, labels)
if err != nil { if err != nil {
log.Printf("Error creating CNI for %s: %s", svc.Name, err) log.Printf("Error creating CNI for %s: %s", svc.Name, err)
return err return err
} }
ip, err := cninetwork.GetIPAddress(svc.Name, task.Pid()) ip, err := cninetwork.GetIPAddress(network, task)
if err != nil { if err != nil {
log.Printf("Error getting IP for %s: %s", svc.Name, err) log.Printf("Error getting IP for %s: %s", svc.Name, err)
return err return err
} }
log.Printf("%s has IP: %s\n", newContainer.ID(), ip) log.Printf("%s has IP: %s\n", newContainer.ID(), ip.String())
hosts, err := ioutil.ReadFile("hosts") hosts, err := ioutil.ReadFile("hosts")
if err != nil { if err != nil {

View File

@ -36,6 +36,7 @@ import (
const ( const (
watchdogPort = "8080" watchdogPort = "8080"
defaultContentType = "text/plain" defaultContentType = "text/plain"
errMissingFunctionName = "Please provide a valid route /function/function_name."
) )
// BaseURLResolver URL resolver for proxy requests // BaseURLResolver URL resolver for proxy requests
@ -74,9 +75,8 @@ func NewHandlerFunc(config types.FaaSConfig, resolver BaseURLResolver) http.Hand
http.MethodPut, http.MethodPut,
http.MethodPatch, http.MethodPatch,
http.MethodDelete, http.MethodDelete,
http.MethodGet, http.MethodGet:
http.MethodOptions,
http.MethodHead:
proxyRequest(w, r, proxyClient, resolver) proxyRequest(w, r, proxyClient, resolver)
default: default:
@ -136,15 +136,15 @@ func proxyRequest(w http.ResponseWriter, originalReq *http.Request, proxyClient
pathVars := mux.Vars(originalReq) pathVars := mux.Vars(originalReq)
functionName := pathVars["name"] functionName := pathVars["name"]
if functionName == "" { if functionName == "" {
httputil.Errorf(w, http.StatusBadRequest, "Provide function name in the request path") httputil.Errorf(w, http.StatusBadRequest, errMissingFunctionName)
return return
} }
functionAddr, resolveErr := resolver.Resolve(functionName) functionAddr, resolveErr := resolver.Resolve(functionName)
if resolveErr != nil { if resolveErr != nil {
// TODO: Should record the 404/not found error in Prometheus. // TODO: Should record the 404/not found error in Prometheus.
log.Printf("resolver error: no endpoints for %s: %s\n", functionName, resolveErr.Error()) log.Printf("resolver error: cannot find %s: %s\n", functionName, resolveErr.Error())
httputil.Errorf(w, http.StatusServiceUnavailable, "No endpoints available for: %s.", functionName) httputil.Errorf(w, http.StatusNotFound, "Cannot find service: %s.", functionName)
return return
} }
@ -153,7 +153,6 @@ func proxyRequest(w http.ResponseWriter, originalReq *http.Request, proxyClient
httputil.Errorf(w, http.StatusInternalServerError, "Failed to resolve service: %s.", functionName) httputil.Errorf(w, http.StatusInternalServerError, "Failed to resolve service: %s.", functionName)
return return
} }
if proxyReq.Body != nil { if proxyReq.Body != nil {
defer proxyReq.Body.Close() defer proxyReq.Body.Close()
} }
@ -168,10 +167,7 @@ func proxyRequest(w http.ResponseWriter, originalReq *http.Request, proxyClient
httputil.Errorf(w, http.StatusInternalServerError, "Can't reach service for: %s.", functionName) httputil.Errorf(w, http.StatusInternalServerError, "Can't reach service for: %s.", functionName)
return return
} }
if response.Body != nil {
defer response.Body.Close() defer response.Body.Close()
}
log.Printf("%s took %f seconds\n", functionName, seconds.Seconds()) log.Printf("%s took %f seconds\n", functionName, seconds.Seconds())
@ -180,9 +176,7 @@ func proxyRequest(w http.ResponseWriter, originalReq *http.Request, proxyClient
w.Header().Set("Content-Type", getContentType(originalReq.Header, response.Header)) w.Header().Set("Content-Type", getContentType(originalReq.Header, response.Header))
w.WriteHeader(response.StatusCode) w.WriteHeader(response.StatusCode)
if response.Body != nil {
io.Copy(w, response.Body) io.Copy(w, response.Body)
}
} }
// buildProxyRequest creates a request object for the proxy request, it will ensure that // buildProxyRequest creates a request object for the proxy request, it will ensure that

View File

@ -1,49 +1,94 @@
package types package types
import "time"
// FunctionDeployment represents a request to create or update a Function. // FunctionDeployment represents a request to create or update a Function.
type FunctionDeployment struct { type FunctionDeployment struct {
// Service is the name of the function deployment // Service corresponds to a Service
Service string `json:"service"` Service string `json:"service"`
// Image is a fully-qualified container image // Image corresponds to a Docker image
Image string `json:"image"` Image string `json:"image"`
// Namespace for the function, if supported by the faas-provider // Network is specific to Docker Swarm - default overlay network is: func_functions
Namespace string `json:"namespace,omitempty"` Network string `json:"network"`
// EnvProcess overrides the fprocess environment variable and can be used // EnvProcess corresponds to the fprocess variable for your container watchdog.
// with the watchdog EnvProcess string `json:"envProcess"`
EnvProcess string `json:"envProcess,omitempty"`
// EnvVars can be provided to set environment variables for the function runtime. // EnvVars provides overrides for functions.
EnvVars map[string]string `json:"envVars,omitempty"` EnvVars map[string]string `json:"envVars"`
// Constraints are specific to the faas-provider. // RegistryAuth is the registry authentication (optional)
Constraints []string `json:"constraints,omitempty"` // in the same encoded format as Docker native credentials
// (see ~/.docker/config.json)
RegistryAuth string `json:"registryAuth,omitempty"`
// Constraints are specific to back-end orchestration platform
Constraints []string `json:"constraints"`
// Secrets list of secrets to be made available to function // Secrets list of secrets to be made available to function
Secrets []string `json:"secrets,omitempty"` Secrets []string `json:"secrets"`
// Labels are metadata for functions which may be used by the // Labels are metadata for functions which may be used by the
// faas-provider or the gateway // back-end for making scheduling or routing decisions
Labels *map[string]string `json:"labels,omitempty"` Labels *map[string]string `json:"labels"`
// Annotations are metadata for functions which may be used by the // Annotations are metadata for functions which may be used by the
// faas-provider or the gateway // back-end for management, orchestration, events and build tasks
Annotations *map[string]string `json:"annotations,omitempty"` Annotations *map[string]string `json:"annotations"`
// Limits for function // Limits for function
Limits *FunctionResources `json:"limits,omitempty"` Limits *FunctionResources `json:"limits"`
// Requests of resources requested by function // Requests of resources requested by function
Requests *FunctionResources `json:"requests,omitempty"` Requests *FunctionResources `json:"requests"`
// ReadOnlyRootFilesystem removes write-access from the root filesystem // ReadOnlyRootFilesystem removes write-access from the root filesystem
// mount-point. // mount-point.
ReadOnlyRootFilesystem bool `json:"readOnlyRootFilesystem,omitempty"` ReadOnlyRootFilesystem bool `json:"readOnlyRootFilesystem"`
// Namespace for the function to be deployed into
Namespace string `json:"namespace,omitempty"`
}
// FunctionResources Memory and CPU
type FunctionResources struct {
Memory string `json:"memory"`
CPU string `json:"cpu"`
}
// FunctionStatus exported for system/functions endpoint
type FunctionStatus struct {
// Name corresponds to a Service
Name string `json:"name"`
// Image corresponds to a Docker image
Image string `json:"image"`
// InvocationCount count of invocations
InvocationCount float64 `json:"invocationCount"`
// Replicas desired within the cluster
Replicas uint64 `json:"replicas"`
// EnvProcess is the process to pass to the watchdog, if in use
EnvProcess string `json:"envProcess"`
// AvailableReplicas is the count of replicas ready to receive
// invocations as reported by the backend
AvailableReplicas uint64 `json:"availableReplicas"`
// Labels are metadata for functions which may be used by the
// backend for making scheduling or routing decisions
Labels *map[string]string `json:"labels"`
// Annotations are metadata for functions which may be used by the
// backend for management, orchestration, events and build tasks
Annotations *map[string]string `json:"annotations"`
// Namespace where the function can be accessed
Namespace string `json:"namespace,omitempty"`
} }
// Secret for underlying orchestrator // Secret for underlying orchestrator
@ -52,71 +97,3 @@ type Secret struct {
Namespace string `json:"namespace,omitempty"` Namespace string `json:"namespace,omitempty"`
Value string `json:"value,omitempty"` Value string `json:"value,omitempty"`
} }
// FunctionResources Memory and CPU
type FunctionResources struct {
Memory string `json:"memory,omitempty"`
CPU string `json:"cpu,omitempty"`
}
// FunctionStatus exported for system/functions endpoint
type FunctionStatus struct {
// Name is the name of the function deployment
Name string `json:"name"`
// Image is a fully-qualified container image
Image string `json:"image"`
// Namespace for the function, if supported by the faas-provider
Namespace string `json:"namespace,omitempty"`
// EnvProcess overrides the fprocess environment variable and can be used
// with the watchdog
EnvProcess string `json:"envProcess,omitempty"`
// EnvVars set environment variables for the function runtime
EnvVars map[string]string `json:"envVars,omitempty"`
// Constraints are specific to the faas-provider
Constraints []string `json:"constraints,omitempty"`
// Secrets list of secrets to be made available to function
Secrets []string `json:"secrets,omitempty"`
// Labels are metadata for functions which may be used by the
// faas-provider or the gateway
Labels *map[string]string `json:"labels,omitempty"`
// Annotations are metadata for functions which may be used by the
// faas-provider or the gateway
Annotations *map[string]string `json:"annotations,omitempty"`
// Limits for function
Limits *FunctionResources `json:"limits,omitempty"`
// Requests of resources requested by function
Requests *FunctionResources `json:"requests,omitempty"`
// ReadOnlyRootFilesystem removes write-access from the root filesystem
// mount-point.
ReadOnlyRootFilesystem bool `json:"readOnlyRootFilesystem,omitempty"`
// ================
// Fields for status
// ================
// InvocationCount count of invocations
InvocationCount float64 `json:"invocationCount,omitempty"`
// Replicas desired within the cluster
Replicas uint64 `json:"replicas,omitempty"`
// AvailableReplicas is the count of replicas ready to receive
// invocations as reported by the faas-provider
AvailableReplicas uint64 `json:"availableReplicas,omitempty"`
// CreatedAt is the time read back from the faas backend's
// data store for when the function or its container was created.
CreatedAt time.Time `json:"createdAt,omitempty"`
}

View File

@ -9,21 +9,20 @@ type ScaleServiceRequest struct {
Replicas uint64 `json:"replicas"` Replicas uint64 `json:"replicas"`
} }
// InfoResponse provides information about the underlying provider
type InfoResponse struct {
Provider string `json:"provider"`
Version ProviderVersion `json:"version"`
Orchestration string `json:"orchestration"`
}
// ProviderVersion provides the commit sha and release version number of the underlying provider
type ProviderVersion struct {
SHA string `json:"sha"`
Release string `json:"release"`
}
// DeleteFunctionRequest delete a deployed function // DeleteFunctionRequest delete a deployed function
type DeleteFunctionRequest struct { type DeleteFunctionRequest struct {
FunctionName string `json:"functionName"` FunctionName string `json:"functionName"`
} }
// ProviderInfo provides information about the configured provider
type ProviderInfo struct {
Name string `json:"provider"`
Version *VersionInfo `json:"version"`
Orchestration string `json:"orchestration"`
}
// VersionInfo provides the commit message, sha and release version number
type VersionInfo struct {
CommitMessage string `json:"commit_message,omitempty"`
SHA string `json:"sha"`
Release string `json:"release"`
}

49
vendor/modules.txt generated vendored
View File

@ -2,7 +2,6 @@
github.com/Microsoft/go-winio github.com/Microsoft/go-winio
github.com/Microsoft/go-winio/pkg/guid github.com/Microsoft/go-winio/pkg/guid
# github.com/Microsoft/hcsshim v0.8.7-0.20190820203702-9e921883ac92 # github.com/Microsoft/hcsshim v0.8.7-0.20190820203702-9e921883ac92
## explicit
github.com/Microsoft/hcsshim github.com/Microsoft/hcsshim
github.com/Microsoft/hcsshim/internal/cow github.com/Microsoft/hcsshim/internal/cow
github.com/Microsoft/hcsshim/internal/hcs github.com/Microsoft/hcsshim/internal/hcs
@ -22,23 +21,17 @@ github.com/Microsoft/hcsshim/internal/vmcompute
github.com/Microsoft/hcsshim/internal/wclayer github.com/Microsoft/hcsshim/internal/wclayer
github.com/Microsoft/hcsshim/osversion github.com/Microsoft/hcsshim/osversion
# github.com/alexellis/go-execute v0.0.0-20200124154445-8697e4e28c5e # github.com/alexellis/go-execute v0.0.0-20200124154445-8697e4e28c5e
## explicit
github.com/alexellis/go-execute/pkg/v1 github.com/alexellis/go-execute/pkg/v1
# github.com/alexellis/k3sup v0.0.0-20200607084134-629c0bc6b50f # github.com/alexellis/k3sup v0.0.0-20200607084134-629c0bc6b50f
## explicit
github.com/alexellis/k3sup/pkg/env github.com/alexellis/k3sup/pkg/env
# github.com/compose-spec/compose-go v0.0.0-20200528042322-36d8ce368e05 # github.com/compose-spec/compose-go v0.0.0-20200528042322-36d8ce368e05
## explicit
github.com/compose-spec/compose-go/envfile github.com/compose-spec/compose-go/envfile
github.com/compose-spec/compose-go/interpolation github.com/compose-spec/compose-go/interpolation
github.com/compose-spec/compose-go/loader github.com/compose-spec/compose-go/loader
github.com/compose-spec/compose-go/schema github.com/compose-spec/compose-go/schema
github.com/compose-spec/compose-go/template github.com/compose-spec/compose-go/template
github.com/compose-spec/compose-go/types github.com/compose-spec/compose-go/types
# github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327
## explicit
# github.com/containerd/containerd v1.3.2 # github.com/containerd/containerd v1.3.2
## explicit
github.com/containerd/containerd github.com/containerd/containerd
github.com/containerd/containerd/api/services/containers/v1 github.com/containerd/containerd/api/services/containers/v1
github.com/containerd/containerd/api/services/content/v1 github.com/containerd/containerd/api/services/content/v1
@ -91,22 +84,17 @@ github.com/containerd/containerd/snapshots/proxy
github.com/containerd/containerd/sys github.com/containerd/containerd/sys
github.com/containerd/containerd/version github.com/containerd/containerd/version
# github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02 # github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02
## explicit
github.com/containerd/continuity/fs github.com/containerd/continuity/fs
github.com/containerd/continuity/pathdriver github.com/containerd/continuity/pathdriver
github.com/containerd/continuity/syscallx github.com/containerd/continuity/syscallx
github.com/containerd/continuity/sysx github.com/containerd/continuity/sysx
# github.com/containerd/fifo v0.0.0-20190816180239-bda0ff6ed73c # github.com/containerd/fifo v0.0.0-20190816180239-bda0ff6ed73c
## explicit
github.com/containerd/fifo github.com/containerd/fifo
# github.com/containerd/go-cni v0.0.0-20200107172653-c154a49e2c75 # github.com/containerd/go-cni v0.0.0-20200107172653-c154a49e2c75
## explicit
github.com/containerd/go-cni github.com/containerd/go-cni
# github.com/containerd/ttrpc v1.0.0 # github.com/containerd/ttrpc v1.0.0
## explicit
github.com/containerd/ttrpc github.com/containerd/ttrpc
# github.com/containerd/typeurl v1.0.0 # github.com/containerd/typeurl v1.0.0
## explicit
github.com/containerd/typeurl github.com/containerd/typeurl
# github.com/containernetworking/cni v0.7.1 # github.com/containernetworking/cni v0.7.1
github.com/containernetworking/cni/libcni github.com/containernetworking/cni/libcni
@ -116,45 +104,37 @@ github.com/containernetworking/cni/pkg/types/020
github.com/containernetworking/cni/pkg/types/current github.com/containernetworking/cni/pkg/types/current
github.com/containernetworking/cni/pkg/version github.com/containernetworking/cni/pkg/version
# github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf # github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
## explicit
github.com/coreos/go-systemd/journal github.com/coreos/go-systemd/journal
# github.com/docker/cli v0.0.0-20191105005515-99c5edceb48d # github.com/docker/cli v0.0.0-20191105005515-99c5edceb48d
## explicit
github.com/docker/cli/cli/config github.com/docker/cli/cli/config
github.com/docker/cli/cli/config/configfile github.com/docker/cli/cli/config/configfile
github.com/docker/cli/cli/config/credentials github.com/docker/cli/cli/config/credentials
github.com/docker/cli/cli/config/types github.com/docker/cli/cli/config/types
# github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible # github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible
## explicit
github.com/docker/distribution/digestset github.com/docker/distribution/digestset
github.com/docker/distribution/reference github.com/docker/distribution/reference
github.com/docker/distribution/registry/api/errcode github.com/docker/distribution/registry/api/errcode
# github.com/docker/docker v17.12.0-ce-rc1.0.20191113042239-ea84732a7725+incompatible # github.com/docker/docker v17.12.0-ce-rc1.0.20191113042239-ea84732a7725+incompatible
## explicit
github.com/docker/docker/pkg/homedir github.com/docker/docker/pkg/homedir
github.com/docker/docker/pkg/idtools github.com/docker/docker/pkg/idtools
github.com/docker/docker/pkg/mount github.com/docker/docker/pkg/mount
github.com/docker/docker/pkg/system github.com/docker/docker/pkg/system
# github.com/docker/docker-credential-helpers v0.6.3 # github.com/docker/docker-credential-helpers v0.6.3
## explicit
github.com/docker/docker-credential-helpers/client github.com/docker/docker-credential-helpers/client
github.com/docker/docker-credential-helpers/credentials github.com/docker/docker-credential-helpers/credentials
# github.com/docker/go-connections v0.4.0 # github.com/docker/go-connections v0.4.0
github.com/docker/go-connections/nat github.com/docker/go-connections/nat
# github.com/docker/go-events v0.0.0-20170721190031-9461782956ad # github.com/docker/go-events v0.0.0-20170721190031-9461782956ad
## explicit
github.com/docker/go-events github.com/docker/go-events
# github.com/docker/go-units v0.4.0 # github.com/docker/go-units v0.4.0
github.com/docker/go-units github.com/docker/go-units
# github.com/gogo/googleapis v1.2.0 # github.com/gogo/googleapis v1.2.0
## explicit
github.com/gogo/googleapis/google/rpc github.com/gogo/googleapis/google/rpc
# github.com/gogo/protobuf v1.3.1 # github.com/gogo/protobuf v1.3.1
github.com/gogo/protobuf/proto github.com/gogo/protobuf/proto
github.com/gogo/protobuf/sortkeys github.com/gogo/protobuf/sortkeys
github.com/gogo/protobuf/types github.com/gogo/protobuf/types
# github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9 # github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9
## explicit
github.com/golang/groupcache/lru github.com/golang/groupcache/lru
# github.com/golang/protobuf v1.3.2 # github.com/golang/protobuf v1.3.2
github.com/golang/protobuf/proto github.com/golang/protobuf/proto
@ -163,10 +143,8 @@ github.com/golang/protobuf/ptypes/any
github.com/golang/protobuf/ptypes/duration github.com/golang/protobuf/ptypes/duration
github.com/golang/protobuf/ptypes/timestamp github.com/golang/protobuf/ptypes/timestamp
# github.com/gorilla/mux v1.8.0 # github.com/gorilla/mux v1.8.0
## explicit
github.com/gorilla/mux github.com/gorilla/mux
# github.com/imdario/mergo v0.3.9 # github.com/imdario/mergo v0.3.9
## explicit
github.com/imdario/mergo github.com/imdario/mergo
# github.com/inconshreveable/mousetrap v1.0.0 # github.com/inconshreveable/mousetrap v1.0.0
github.com/inconshreveable/mousetrap github.com/inconshreveable/mousetrap
@ -175,28 +153,21 @@ github.com/mattn/go-shellwords
# github.com/mitchellh/mapstructure v1.3.1 # github.com/mitchellh/mapstructure v1.3.1
github.com/mitchellh/mapstructure github.com/mitchellh/mapstructure
# github.com/morikuni/aec v1.0.0 # github.com/morikuni/aec v1.0.0
## explicit
github.com/morikuni/aec github.com/morikuni/aec
# github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2 # github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2
## explicit
github.com/opencontainers/go-digest github.com/opencontainers/go-digest
# github.com/opencontainers/image-spec v1.0.1 # github.com/opencontainers/image-spec v1.0.1
## explicit
github.com/opencontainers/image-spec/identity github.com/opencontainers/image-spec/identity
github.com/opencontainers/image-spec/specs-go github.com/opencontainers/image-spec/specs-go
github.com/opencontainers/image-spec/specs-go/v1 github.com/opencontainers/image-spec/specs-go/v1
# github.com/opencontainers/runc v1.0.0-rc9 # github.com/opencontainers/runc v1.0.0-rc9
## explicit
github.com/opencontainers/runc/libcontainer/system github.com/opencontainers/runc/libcontainer/system
github.com/opencontainers/runc/libcontainer/user github.com/opencontainers/runc/libcontainer/user
# github.com/opencontainers/runtime-spec v1.0.2 # github.com/opencontainers/runtime-spec v1.0.2
## explicit
github.com/opencontainers/runtime-spec/specs-go github.com/opencontainers/runtime-spec/specs-go
# github.com/openfaas/faas v0.0.0-20201205125747-9bbb25e3c7c4 # github.com/openfaas/faas v0.0.0-20201205125747-9bbb25e3c7c4
## explicit
github.com/openfaas/faas/gateway/requests github.com/openfaas/faas/gateway/requests
# github.com/openfaas/faas-provider v0.17.3 # github.com/openfaas/faas-provider v0.15.3
## explicit
github.com/openfaas/faas-provider github.com/openfaas/faas-provider
github.com/openfaas/faas-provider/auth github.com/openfaas/faas-provider/auth
github.com/openfaas/faas-provider/httputil github.com/openfaas/faas-provider/httputil
@ -204,49 +175,35 @@ github.com/openfaas/faas-provider/logs
github.com/openfaas/faas-provider/proxy github.com/openfaas/faas-provider/proxy
github.com/openfaas/faas-provider/types github.com/openfaas/faas-provider/types
# github.com/pkg/errors v0.9.1 # github.com/pkg/errors v0.9.1
## explicit
github.com/pkg/errors github.com/pkg/errors
# github.com/prometheus/procfs v0.2.0
## explicit
# github.com/sethvargo/go-password v0.1.3 # github.com/sethvargo/go-password v0.1.3
## explicit
github.com/sethvargo/go-password/password github.com/sethvargo/go-password/password
# github.com/sirupsen/logrus v1.7.0 # github.com/sirupsen/logrus v1.7.0
github.com/sirupsen/logrus github.com/sirupsen/logrus
# github.com/spf13/cobra v0.0.5 # github.com/spf13/cobra v0.0.5
## explicit
github.com/spf13/cobra github.com/spf13/cobra
# github.com/spf13/pflag v1.0.5 # github.com/spf13/pflag v1.0.5
## explicit
github.com/spf13/pflag github.com/spf13/pflag
# github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 # github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2
## explicit
github.com/syndtr/gocapability/capability github.com/syndtr/gocapability/capability
# github.com/vishvananda/netlink v1.1.0 # github.com/vishvananda/netlink v1.1.0
## explicit
github.com/vishvananda/netlink github.com/vishvananda/netlink
github.com/vishvananda/netlink/nl github.com/vishvananda/netlink/nl
# github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df # github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df
## explicit
github.com/vishvananda/netns github.com/vishvananda/netns
# github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb # github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb
## explicit
github.com/xeipuuv/gojsonpointer github.com/xeipuuv/gojsonpointer
# github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 # github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415
github.com/xeipuuv/gojsonreference github.com/xeipuuv/gojsonreference
# github.com/xeipuuv/gojsonschema v1.2.0 # github.com/xeipuuv/gojsonschema v1.2.0
github.com/xeipuuv/gojsonschema github.com/xeipuuv/gojsonschema
# go.etcd.io/bbolt v1.3.5
## explicit
# go.opencensus.io v0.22.2 # go.opencensus.io v0.22.2
## explicit
go.opencensus.io go.opencensus.io
go.opencensus.io/internal go.opencensus.io/internal
go.opencensus.io/trace go.opencensus.io/trace
go.opencensus.io/trace/internal go.opencensus.io/trace/internal
go.opencensus.io/trace/tracestate go.opencensus.io/trace/tracestate
# golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 # golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553
## explicit
golang.org/x/net/context golang.org/x/net/context
golang.org/x/net/context/ctxhttp golang.org/x/net/context/ctxhttp
golang.org/x/net/http/httpguts golang.org/x/net/http/httpguts
@ -259,7 +216,6 @@ golang.org/x/net/trace
golang.org/x/sync/errgroup golang.org/x/sync/errgroup
golang.org/x/sync/semaphore golang.org/x/sync/semaphore
# golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 # golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5
## explicit
golang.org/x/sys/unix golang.org/x/sys/unix
golang.org/x/sys/windows golang.org/x/sys/windows
# golang.org/x/text v0.3.2 # golang.org/x/text v0.3.2
@ -268,10 +224,8 @@ golang.org/x/text/transform
golang.org/x/text/unicode/bidi golang.org/x/text/unicode/bidi
golang.org/x/text/unicode/norm golang.org/x/text/unicode/norm
# google.golang.org/genproto v0.0.0-20191216205247-b31c10ee225f # google.golang.org/genproto v0.0.0-20191216205247-b31c10ee225f
## explicit
google.golang.org/genproto/googleapis/rpc/status google.golang.org/genproto/googleapis/rpc/status
# google.golang.org/grpc v1.23.0 # google.golang.org/grpc v1.23.0
## explicit
google.golang.org/grpc google.golang.org/grpc
google.golang.org/grpc/balancer google.golang.org/grpc/balancer
google.golang.org/grpc/balancer/base google.golang.org/grpc/balancer/base
@ -311,5 +265,4 @@ gopkg.in/inf.v0
# gopkg.in/yaml.v2 v2.3.0 # gopkg.in/yaml.v2 v2.3.0
gopkg.in/yaml.v2 gopkg.in/yaml.v2
# k8s.io/apimachinery v0.18.9 # k8s.io/apimachinery v0.18.9
## explicit
k8s.io/apimachinery/pkg/api/resource k8s.io/apimachinery/pkg/api/resource